diff --git a/backend/src/baserow/contrib/builder/apps.py b/backend/src/baserow/contrib/builder/apps.py index d00a24495..bc59b0877 100644 --- a/backend/src/baserow/contrib/builder/apps.py +++ b/backend/src/baserow/contrib/builder/apps.py @@ -183,14 +183,14 @@ class BuilderConfig(AppConfig): ImageElementType, InputTextElementType, LinkElementType, + RatingElementType, + RatingInputElementType, MenuElementType, RecordSelectorElementType, RepeatElementType, SimpleContainerElementType, TableElementType, TextElementType, - RatingElementType, - RatingInputElementType, ) from .elements.registries import element_type_registry @@ -301,9 +301,9 @@ class BuilderConfig(AppConfig): ButtonCollectionFieldType, ImageCollectionFieldType, LinkCollectionFieldType, + RatingCollectionFieldType, TagsCollectionFieldType, TextCollectionFieldType, - RatingCollectionFieldType, ) from .elements.registries import collection_field_type_registry diff --git a/backend/src/baserow/contrib/builder/elements/collection_field_types.py b/backend/src/baserow/contrib/builder/elements/collection_field_types.py index f171dfcfe..8990ceed8 100644 --- a/backend/src/baserow/contrib/builder/elements/collection_field_types.py +++ b/backend/src/baserow/contrib/builder/elements/collection_field_types.py @@ -1,6 +1,7 @@ from typing import Any, Dict, Generator, TypedDict, Union -from django.core.validators import MinValueValidator, MaxValueValidator +from django.core.validators import MinValueValidator + from rest_framework import serializers from baserow.contrib.builder.elements.element_types import NavigationElementManager diff --git a/backend/src/baserow/contrib/builder/elements/element_types.py b/backend/src/baserow/contrib/builder/elements/element_types.py index 098d06ee2..99a6c175e 100644 --- a/backend/src/baserow/contrib/builder/elements/element_types.py +++ b/backend/src/baserow/contrib/builder/elements/element_types.py @@ -59,6 +59,8 @@ from baserow.contrib.builder.elements.models import ( MenuElement, MenuItemElement, NavigationElementMixin, + RatingElement, + RatingInputElement, RecordSelectorElement, RepeatElement, SimpleContainerElement, @@ -66,8 +68,6 @@ from baserow.contrib.builder.elements.models import ( TextElement, VerticalAlignments, get_default_table_orientation, - RatingElement, - RatingInputElement, ) from baserow.contrib.builder.elements.registries import ( ElementType, @@ -1236,9 +1236,9 @@ class RatingElementType(ElementType): def get_pytest_params(self, pytest_data_fixture): return { "max_value": 5, - "value": 5, + "value": "5", "color": "dark-orange", - "style": RatingElement.style.field.choices.STAR.value, + "style": "STAR", } @property @@ -1280,9 +1280,9 @@ class RatingInputElementType(InputElementType): def get_pytest_params(self, pytest_data_fixture): return { "max_value": 5, - "value": 5, + "value": "5", "color": "dark-orange", - "style": RatingInputElement.style.field.choices.STAR.value, + "style": "STAR", "label": "", "required": False, } diff --git a/backend/src/baserow/contrib/builder/migrations/0043_ratingelement_ratinginputelement.py b/backend/src/baserow/contrib/builder/migrations/0043_ratingelement_ratinginputelement.py index bf32b588e..e0ea4dccb 100644 --- a/backend/src/baserow/contrib/builder/migrations/0043_ratingelement_ratinginputelement.py +++ b/backend/src/baserow/contrib/builder/migrations/0043_ratingelement_ratinginputelement.py @@ -1,10 +1,11 @@ # Generated by Django 5.0.9 on 2025-01-05 23:56 -import baserow.core.formula.field import django.core.validators import django.db.models.deletion from django.db import migrations, models +import baserow.core.formula.field + class Migration(migrations.Migration): dependencies = [ diff --git a/backend/src/baserow/core/registry.py b/backend/src/baserow/core/registry.py index 2feeb9dee..8653ba89e 100644 --- a/backend/src/baserow/core/registry.py +++ b/backend/src/baserow/core/registry.py @@ -781,7 +781,7 @@ class Registry(Generic[InstanceSubClass]): ) self.registry[instance.type] = instance - print(f"Registered {self.name} {instance.type}") + instance.after_register() def unregister(self, value: Union[str, InstanceSubClass]): diff --git a/backend/tests/baserow/contrib/builder/elements/test_rating_collection_field_type.py b/backend/tests/baserow/contrib/builder/elements/test_rating_collection_field_type.py index 9f0cf4cf2..18262e571 100644 --- a/backend/tests/baserow/contrib/builder/elements/test_rating_collection_field_type.py +++ b/backend/tests/baserow/contrib/builder/elements/test_rating_collection_field_type.py @@ -2,14 +2,15 @@ Test the RatingCollectionFieldType class. """ -import pytest from unittest.mock import patch +import pytest + from baserow.contrib.builder.elements.collection_field_types import ( RatingCollectionFieldType, ) -from baserow.contrib.builder.elements.models import RatingStyles -from baserow.core.formula.serializers import FormulaSerializerField +from baserow.contrib.builder.elements.registries import element_type_registry +from baserow.contrib.builder.pages.service import PageService MODULE_PATH = "baserow.contrib.builder.elements.collection_field_types" @@ -18,6 +19,7 @@ def test_class_properties_are_set(): """ Test that the properties of the class are correctly set. """ + field_type = RatingCollectionFieldType() assert field_type.type == "rating" @@ -46,6 +48,7 @@ def test_deserialize_property_returns_value_from_super_method( Ensure that the value is returned by calling the parent class's deserialize_property() method. """ + mock_value = "5" mock_super_deserialize.return_value = mock_value value = "5" @@ -72,7 +75,73 @@ def test_deserialize_property_returns_value_from_super_method( @pytest.mark.django_db def test_import_export_rating_collection_field_type(data_fixture): """ - Ensure that the RatingCollectionField's properties are exported correctly + Ensure that the RatingCollectionField's formulas are exported correctly with the updated Data Sources. """ - pass + + user, _ = data_fixture.create_user_and_token() + page = data_fixture.create_builder_page(user=user) + table, fields, _ = data_fixture.build_table( + user=user, + columns=[ + ("Rating", "rating"), + ], + rows=[ + [3], + ], + ) + rating_field = fields[0] + data_source = data_fixture.create_builder_local_baserow_list_rows_data_source( + table=table, page=page + ) + table_element = data_fixture.create_builder_table_element( + page=page, + data_source=data_source, + fields=[ + { + "name": "Rating Field", + "type": "rating", + "config": { + "value": f"get('data_source.{data_source.id}.0.{rating_field.db_column}')", + "max_value": 5, + "style": "star", + "color": "", + }, + }, + ], + ) + + # Create a duplicate page to get a new data source + duplicated_page = PageService().duplicate_page(user, page) + data_source2 = duplicated_page.datasource_set.first() + + # Create ID mapping for the data sources + id_mapping = {"builder_data_sources": {data_source.id: data_source2.id}} + + # Export the element + serialized = element_type_registry.get_by_model(table_element).export_serialized( + table_element + ) + + # Delete the element + table_element.delete() + + # Import it back + imported_element = element_type_registry.get_by_model( + table_element + ).import_serialized( + page, + serialized, + id_mapping, + None, + ) + + # The imported element should have the same field configuration + # with updated data source ID + imported_field = imported_element.fields.get(name="Rating Field") + assert imported_field.config == { + "value": f"get('data_source.{data_source2.id}.0.{rating_field.db_column}')", + "max_value": 5, + "style": "star", + "color": "", + } diff --git a/backend/tests/baserow/contrib/builder/elements/test_rating_element_types.py b/backend/tests/baserow/contrib/builder/elements/test_rating_element_types.py index 38dc6b9a3..cf9f9323d 100644 --- a/backend/tests/baserow/contrib/builder/elements/test_rating_element_types.py +++ b/backend/tests/baserow/contrib/builder/elements/test_rating_element_types.py @@ -1,21 +1,9 @@ -import pytest -from django.core.validators import MinValueValidator, MaxValueValidator -from rest_framework.exceptions import ValidationError - -from baserow.contrib.builder.data_sources.builder_dispatch_context import ( - BuilderDispatchContext, -) -from baserow.contrib.builder.elements.element_types import ( - RatingElementType, - RatingInputElementType, -) -from baserow.contrib.builder.elements.models import ( - RatingElement, - RatingInputElement, - RatingStyles, -) -from baserow.contrib.builder.elements.registries import element_type_registry from collections import defaultdict + +import pytest + +from baserow.contrib.builder.elements.models import RatingElement, RatingStyles +from baserow.contrib.builder.elements.registries import element_type_registry from baserow.core.utils import MirrorDict diff --git a/web-frontend/modules/builder/components/elements/components/RatingInputElement.vue b/web-frontend/modules/builder/components/elements/components/RatingInputElement.vue index 173eefa8e..50d864846 100644 --- a/web-frontend/modules/builder/components/elements/components/RatingInputElement.vue +++ b/web-frontend/modules/builder/components/elements/components/RatingInputElement.vue @@ -70,6 +70,15 @@ export default { return this.formElementData?.value ?? this.resolvedValue }, }, + watch: { + resolvedValue: { + handler(newValue) { + if (this.editable && this.formElementData?.value === undefined) { + this.setFormData(newValue) + } + }, + }, + }, mounted() { if (this.editable) { this.setFormData(this.resolvedValue) @@ -82,15 +91,6 @@ export default { } }, }, - watch: { - resolvedValue: { - handler(newValue) { - if (this.editable && this.formElementData?.value === undefined) { - this.setFormData(newValue) - } - }, - }, - }, } </script> diff --git a/web-frontend/modules/builder/components/elements/components/forms/general/RatingElementForm.vue b/web-frontend/modules/builder/components/elements/components/forms/general/RatingElementForm.vue index 3056a4250..d93a92931 100644 --- a/web-frontend/modules/builder/components/elements/components/forms/general/RatingElementForm.vue +++ b/web-frontend/modules/builder/components/elements/components/forms/general/RatingElementForm.vue @@ -22,8 +22,8 @@ required > <input - type="number" v-model="values.max_value" + type="number" :min="1" :max="10" :step="1" diff --git a/web-frontend/modules/builder/components/elements/components/forms/general/RatingInputElementForm.vue b/web-frontend/modules/builder/components/elements/components/forms/general/RatingInputElementForm.vue index 94ba327ac..56bc28270 100644 --- a/web-frontend/modules/builder/components/elements/components/forms/general/RatingInputElementForm.vue +++ b/web-frontend/modules/builder/components/elements/components/forms/general/RatingInputElementForm.vue @@ -26,8 +26,8 @@ :error-message="valueErrorMessage" > <InjectedFormulaInput - data-test-id="rating-form-value" v-model="values.value" + data-test-id="rating-form-value" :placeholder=" values.editable ? $t('generalForm.valuePlaceholder') @@ -45,8 +45,8 @@ required > <input - type="number" v-model="values.max_value" + type="number" :min="1" :max="10" :step="1" diff --git a/web-frontend/modules/builder/elementTypes.js b/web-frontend/modules/builder/elementTypes.js index 1b8f888f8..0312be3ca 100644 --- a/web-frontend/modules/builder/elementTypes.js +++ b/web-frontend/modules/builder/elementTypes.js @@ -2122,6 +2122,7 @@ export class RatingElementType extends ElementType { static getType() { return 'rating' } + get name() { return 'Rating' } @@ -2174,6 +2175,7 @@ export class RatingElementType extends ElementType { value: 0, } } + isValid(element, value) { return value >= 0 && value <= element.max_value } diff --git a/web-frontend/modules/database/components/Rating.vue b/web-frontend/modules/database/components/Rating.vue index 95b274b5d..dabe27abb 100644 --- a/web-frontend/modules/database/components/Rating.vue +++ b/web-frontend/modules/database/components/Rating.vue @@ -8,7 +8,9 @@ ]" > <i - v-for="index in props.readOnly && !props.showUnselectedInReadOnly ? props.value : props.maxValue" + v-for="index in props.readOnly && !props.showUnselectedInReadOnly + ? props.value + : props.maxValue" :key="index" class="rating__star" :class="{ @@ -30,7 +32,7 @@ export default { props: { readOnly: { type: Boolean, - required: true, + default: false, }, value: { required: true,