1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-07 06:15:36 +00:00

Add new Collection Field Type of Boolean

This commit is contained in:
Tsering Paljor 2024-04-15 10:18:49 +00:00 committed by Peter Evans
parent 7e04a43b51
commit cddcd8e3e5
12 changed files with 299 additions and 4 deletions
backend
src/baserow/contrib/builder
tests/baserow/contrib/builder/elements
changelog/entries/unreleased/feature
web-frontend/modules
builder
core/assets/scss/components/builder/elements/ab_components

View file

@ -242,11 +242,13 @@ class BuilderConfig(AppConfig):
builder_workflow_action_type_registry.register(LogoutWorkflowActionType())
from .elements.collection_field_types import (
BooleanCollectionFieldType,
LinkCollectionFieldType,
TextCollectionFieldType,
)
from .elements.registries import collection_field_type_registry
collection_field_type_registry.register(BooleanCollectionFieldType())
collection_field_type_registry.register(TextCollectionFieldType())
collection_field_type_registry.register(LinkCollectionFieldType())

View file

@ -6,6 +6,40 @@ from baserow.contrib.builder.formula_importer import import_formula
from baserow.core.formula.serializers import FormulaSerializerField
class BooleanCollectionFieldType(CollectionFieldType):
type = "boolean"
allowed_fields = ["value"]
serializer_field_names = ["value"]
class SerializedDict(TypedDict):
value: bool
@property
def serializer_field_overrides(self):
return {
"value": FormulaSerializerField(
help_text="The boolean value.",
required=False,
allow_blank=True,
default=False,
),
}
def deserialize_property(
self,
prop_name: str,
value: Any,
id_mapping: Dict[str, Any],
data_source_id: Optional[int] = None,
) -> Any:
if prop_name == "value" and data_source_id:
return import_formula(value, id_mapping, data_source_id=data_source_id)
return super().deserialize_property(
prop_name, value, id_mapping, data_source_id
)
class TextCollectionFieldType(CollectionFieldType):
type = "text"
allowed_fields = ["value"]

View file

@ -0,0 +1,132 @@
"""
Test the BooleanCollectionFieldType class.
"""
from unittest.mock import patch
import pytest
from baserow.contrib.builder.elements.collection_field_types import (
BooleanCollectionFieldType,
)
from baserow.core.formula.serializers import FormulaSerializerField
MODULE_PATH = "baserow.contrib.builder.elements.collection_field_types"
def test_class_properties_are_set():
"""
Test that the properties of the class are correctly set.
Ensure the type, allowed_fields, and serializer_field_names properties
are set to the correct values.
"""
expected_type = "boolean"
expected_allowed_fields = ["value"]
expected_serializer_field_names = ["value"]
bool_field_type = BooleanCollectionFieldType()
assert bool_field_type.type == expected_type
assert bool_field_type.allowed_fields == expected_allowed_fields
assert bool_field_type.serializer_field_names == expected_serializer_field_names
def test_serializer_field_overrides_returns_expected_value():
"""
Ensure the serializer_field_overrides() method returns the expected value.
"""
result = BooleanCollectionFieldType().serializer_field_overrides
field = result["value"]
assert type(field) == FormulaSerializerField
assert field.allow_blank is True
assert field.default is False
assert field.required is False
assert field.help_text == "The boolean value."
@patch(f"{MODULE_PATH}.CollectionFieldType.deserialize_property")
@patch(f"{MODULE_PATH}.import_formula")
def test_deserialize_property_returns_value_from_import_formula(
mock_import_formula, mock_super_deserialize
):
"""
Ensure the deserialize_property() method uses import_formula() if the
prop_name is 'value' and a data_source_id is provided.
"""
mock_value = "foo"
mock_import_formula.return_value = mock_value
prop_name = "value"
value = "foo"
id_mapping = {}
data_source_id = 1
result = BooleanCollectionFieldType().deserialize_property(
prop_name,
value,
id_mapping,
data_source_id,
)
assert result == mock_value
mock_import_formula.assert_called_once_with(
value,
id_mapping,
data_source_id=data_source_id,
)
mock_super_deserialize.assert_not_called()
@patch(f"{MODULE_PATH}.CollectionFieldType.deserialize_property")
@patch(f"{MODULE_PATH}.import_formula")
@pytest.mark.parametrize(
"prop_name,data_source_id",
[
("", 1),
(" ", 1),
("", None),
(" ", None),
# Intentionally misspelt "value"
("vallue", 1),
("value", None),
],
)
def test_deserialize_property_returns_value_from_super_method(
mock_import_formula,
mock_super_deserialize,
prop_name,
data_source_id,
):
"""
Ensure that the value is returned by calling the parent class's
deserialize_property() method.
If the prop_name is "value" *and* the data_source_id is not empty, the
import_formula() is called. All other combinations should cause the
super method to be called instead.
"""
mock_value = "foo"
mock_super_deserialize.return_value = mock_value
value = "foo"
id_mapping = {}
result = BooleanCollectionFieldType().deserialize_property(
prop_name,
value,
id_mapping,
data_source_id,
)
assert result == mock_value
mock_import_formula.assert_not_called()
mock_super_deserialize.assert_called_once_with(
prop_name,
value,
id_mapping,
data_source_id,
)

View file

@ -0,0 +1,7 @@
{
"type": "feature",
"message": "Add a new Collection Field Type of Boolean in Application Builder.",
"issue_number": 2359,
"bullet_points": [],
"created_at": "2024-04-04"
}

View file

@ -1,9 +1,14 @@
import { Registerable } from '@baserow/modules/core/registry'
import BooleanField from '@baserow/modules/builder/components/elements/components/collectionField/BooleanField'
import TextField from '@baserow/modules/builder/components/elements/components/collectionField/TextField'
import LinkField from '@baserow/modules/builder/components/elements/components/collectionField/LinkField'
import BooleanFieldForm from '@baserow/modules/builder/components/elements/components/collectionField/form/BooleanFieldForm'
import TextFieldForm from '@baserow/modules/builder/components/elements/components/collectionField/form/TextFieldForm'
import LinkFieldForm from '@baserow/modules/builder/components/elements/components/collectionField/form/LinkFieldForm'
import { ensureString } from '@baserow/modules/core/utils/validator'
import {
ensureBoolean,
ensureString,
} from '@baserow/modules/core/utils/validator'
import resolveElementUrl from '@baserow/modules/builder/utils/urlResolution'
import { pathParametersInError } from '@baserow/modules/builder/utils/params'
@ -38,6 +43,36 @@ export class CollectionFieldType extends Registerable {
}
}
export class BooleanCollectionFieldType extends CollectionFieldType {
static getType() {
return 'boolean'
}
get name() {
return this.app.i18n.t('collectionFieldType.boolean')
}
get component() {
return BooleanField
}
get formComponent() {
return BooleanFieldForm
}
getProps(field, { resolveFormula, applicationContext }) {
try {
return { value: ensureBoolean(resolveFormula(field.value)) }
} catch (error) {
return { value: false }
}
}
getOrder() {
return 5
}
}
export class TextCollectionFieldType extends CollectionFieldType {
static getType() {
return 'text'

View file

@ -4,11 +4,13 @@
type="checkbox"
:checked="value"
:required="required"
:disabled="disabled"
class="ab-checkbox__input"
:disabled="disabled"
:class="{
'ab-checkbox--error': error,
'ab-checkbox--readonly': readOnly,
}"
:aria-disabled="disabled"
/>
<label v-if="hasSlot" class="ab-checkbox__label">
<slot></slot>
@ -52,6 +54,14 @@ export default {
required: false,
default: false,
},
/**
* Whether the checkbox is readonly.
*/
readOnly: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
hasSlot() {
@ -60,7 +70,7 @@ export default {
},
methods: {
toggle() {
if (this.disabled) return
if (this.disabled || this.readOnly) return
this.$emit('input', !this.value)
},
},

View file

@ -3,7 +3,7 @@
<ABCheckbox
v-model="inputValue"
:required="element.required"
:disabled="isEditMode"
:read-only="isEditMode"
:error="displayFormDataError"
class="checkbox-element"
>

View file

@ -0,0 +1,19 @@
<template>
<ABCheckbox :value="value" :read-only="true" />
</template>
<script>
import ABCheckbox from '@baserow/modules/builder/components/elements/baseComponents/ABCheckbox'
export default {
name: 'BooleanField',
components: { ABCheckbox },
props: {
value: {
type: Boolean,
required: true,
default: false,
},
},
}
</script>

View file

@ -0,0 +1,45 @@
<template>
<form @submit.prevent @keydown.enter.prevent>
<ApplicationBuilderFormulaInputGroup
v-model="values.value"
:label="$t('textFieldForm.fieldValueLabel')"
:placeholder="$t('textFieldForm.fieldValuePlaceholder')"
:data-providers-allowed="DATA_PROVIDERS_ALLOWED_ELEMENTS"
:application-context-additions="{
element,
}"
horizontal
/>
</form>
</template>
<script>
import { DATA_PROVIDERS_ALLOWED_ELEMENTS } from '@baserow/modules/builder/enums'
import form from '@baserow/modules/core/mixins/form'
import ApplicationBuilderFormulaInputGroup from '@baserow/modules/builder/components/ApplicationBuilderFormulaInputGroup'
export default {
name: 'BooleanFieldForm',
components: { ApplicationBuilderFormulaInputGroup },
mixins: [form],
props: {
element: {
type: Object,
required: true,
},
},
data() {
return {
allowedValues: ['value'],
values: {
value: '',
},
}
},
computed: {
DATA_PROVIDERS_ALLOWED_ELEMENTS() {
return DATA_PROVIDERS_ALLOWED_ELEMENTS
},
},
}
</script>

View file

@ -430,6 +430,7 @@
"addAction": "add action"
},
"collectionFieldType": {
"boolean": "Boolean",
"text": "Text",
"link": "Link"
},

View file

@ -93,6 +93,7 @@ import {
} from '@baserow/modules/builder/workflowActionTypes'
import {
BooleanCollectionFieldType,
TextCollectionFieldType,
LinkCollectionFieldType,
} from '@baserow/modules/builder/collectionFieldTypes'
@ -258,6 +259,10 @@ export default (context) => {
new UpdateRowWorkflowActionType(context)
)
app.$registry.register(
'collectionField',
new BooleanCollectionFieldType(context)
)
app.$registry.register(
'collectionField',
new TextCollectionFieldType(context)

View file

@ -13,6 +13,11 @@
border-color: $color-error-300;
}
.ab-checkbox--readonly {
accent-color: $palette-blue-500;
pointer-events: none;
}
.ab-checkbox__label {
cursor: pointer;
color: $black;