mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-10 07:37:30 +00:00
Add new Collection Field Type of Boolean
This commit is contained in:
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
|
@ -242,11 +242,13 @@ class BuilderConfig(AppConfig):
|
||||||
builder_workflow_action_type_registry.register(LogoutWorkflowActionType())
|
builder_workflow_action_type_registry.register(LogoutWorkflowActionType())
|
||||||
|
|
||||||
from .elements.collection_field_types import (
|
from .elements.collection_field_types import (
|
||||||
|
BooleanCollectionFieldType,
|
||||||
LinkCollectionFieldType,
|
LinkCollectionFieldType,
|
||||||
TextCollectionFieldType,
|
TextCollectionFieldType,
|
||||||
)
|
)
|
||||||
from .elements.registries import collection_field_type_registry
|
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(TextCollectionFieldType())
|
||||||
collection_field_type_registry.register(LinkCollectionFieldType())
|
collection_field_type_registry.register(LinkCollectionFieldType())
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,40 @@ from baserow.contrib.builder.formula_importer import import_formula
|
||||||
from baserow.core.formula.serializers import FormulaSerializerField
|
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):
|
class TextCollectionFieldType(CollectionFieldType):
|
||||||
type = "text"
|
type = "text"
|
||||||
allowed_fields = ["value"]
|
allowed_fields = ["value"]
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
|
@ -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"
|
||||||
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
import { Registerable } from '@baserow/modules/core/registry'
|
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 TextField from '@baserow/modules/builder/components/elements/components/collectionField/TextField'
|
||||||
import LinkField from '@baserow/modules/builder/components/elements/components/collectionField/LinkField'
|
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 TextFieldForm from '@baserow/modules/builder/components/elements/components/collectionField/form/TextFieldForm'
|
||||||
import LinkFieldForm from '@baserow/modules/builder/components/elements/components/collectionField/form/LinkFieldForm'
|
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 resolveElementUrl from '@baserow/modules/builder/utils/urlResolution'
|
||||||
import { pathParametersInError } from '@baserow/modules/builder/utils/params'
|
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 {
|
export class TextCollectionFieldType extends CollectionFieldType {
|
||||||
static getType() {
|
static getType() {
|
||||||
return 'text'
|
return 'text'
|
||||||
|
|
|
@ -4,11 +4,13 @@
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="value"
|
:checked="value"
|
||||||
:required="required"
|
:required="required"
|
||||||
:disabled="disabled"
|
|
||||||
class="ab-checkbox__input"
|
class="ab-checkbox__input"
|
||||||
|
:disabled="disabled"
|
||||||
:class="{
|
:class="{
|
||||||
'ab-checkbox--error': error,
|
'ab-checkbox--error': error,
|
||||||
|
'ab-checkbox--readonly': readOnly,
|
||||||
}"
|
}"
|
||||||
|
:aria-disabled="disabled"
|
||||||
/>
|
/>
|
||||||
<label v-if="hasSlot" class="ab-checkbox__label">
|
<label v-if="hasSlot" class="ab-checkbox__label">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
@ -52,6 +54,14 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Whether the checkbox is readonly.
|
||||||
|
*/
|
||||||
|
readOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasSlot() {
|
hasSlot() {
|
||||||
|
@ -60,7 +70,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggle() {
|
toggle() {
|
||||||
if (this.disabled) return
|
if (this.disabled || this.readOnly) return
|
||||||
this.$emit('input', !this.value)
|
this.$emit('input', !this.value)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<ABCheckbox
|
<ABCheckbox
|
||||||
v-model="inputValue"
|
v-model="inputValue"
|
||||||
:required="element.required"
|
:required="element.required"
|
||||||
:disabled="isEditMode"
|
:read-only="isEditMode"
|
||||||
:error="displayFormDataError"
|
:error="displayFormDataError"
|
||||||
class="checkbox-element"
|
class="checkbox-element"
|
||||||
>
|
>
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -430,6 +430,7 @@
|
||||||
"addAction": "add action"
|
"addAction": "add action"
|
||||||
},
|
},
|
||||||
"collectionFieldType": {
|
"collectionFieldType": {
|
||||||
|
"boolean": "Boolean",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"link": "Link"
|
"link": "Link"
|
||||||
},
|
},
|
||||||
|
|
|
@ -93,6 +93,7 @@ import {
|
||||||
} from '@baserow/modules/builder/workflowActionTypes'
|
} from '@baserow/modules/builder/workflowActionTypes'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
BooleanCollectionFieldType,
|
||||||
TextCollectionFieldType,
|
TextCollectionFieldType,
|
||||||
LinkCollectionFieldType,
|
LinkCollectionFieldType,
|
||||||
} from '@baserow/modules/builder/collectionFieldTypes'
|
} from '@baserow/modules/builder/collectionFieldTypes'
|
||||||
|
@ -258,6 +259,10 @@ export default (context) => {
|
||||||
new UpdateRowWorkflowActionType(context)
|
new UpdateRowWorkflowActionType(context)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app.$registry.register(
|
||||||
|
'collectionField',
|
||||||
|
new BooleanCollectionFieldType(context)
|
||||||
|
)
|
||||||
app.$registry.register(
|
app.$registry.register(
|
||||||
'collectionField',
|
'collectionField',
|
||||||
new TextCollectionFieldType(context)
|
new TextCollectionFieldType(context)
|
||||||
|
|
|
@ -13,6 +13,11 @@
|
||||||
border-color: $color-error-300;
|
border-color: $color-error-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ab-checkbox--readonly {
|
||||||
|
accent-color: $palette-blue-500;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.ab-checkbox__label {
|
.ab-checkbox__label {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: $black;
|
color: $black;
|
||||||
|
|
Loading…
Add table
Reference in a new issue