Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.jetbrains.annotations.Nullable;

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
Expand All @@ -44,6 +45,10 @@ public class DropDownImpl extends AbstractOptionsFieldImpl implements DropDown {
@Default(booleanValues = false)
protected boolean multiSelect;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_PATTERN)
@Nullable
protected String pattern;

@Override
public Integer getMinItems() {
return minItems;
Expand Down Expand Up @@ -87,4 +92,34 @@ public Type getType() {
public String getFieldType() {
return super.getFieldType(FieldType.DROP_DOWN);
}

/**
* String length/pattern constraints are only meaningful for a single-select (string-typed) drop-down,
* e.g. a searchable drop-down where {@code enforceEnum} is disabled and free text is allowed. For a
* multi-select drop-down the value type is an array, so these constraints are not applicable and are
* omitted from the model (mirroring the runtime, which strips them for non-string types).
*/
@Override
public Integer getMinLength() {
return isMultiSelect() ? null : minLength;
}

@Override
public Integer getMaxLength() {
return isMultiSelect() ? null : maxLength;
}

@Override
public String getPattern() {
return isMultiSelect() ? null : pattern;
}

/**
* Drop-down does not support the {@code format} string constraint (there is no authorable format for an
* enumerated field), so this always returns {@code null} and is omitted from the exported model.
*/
@Override
public String getFormat() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
* @since com.adobe.cq.forms.core.components.models.form 2.0.0
*/
@ConsumerType
public interface DropDown extends Field, OptionsConstraint, ContainerConstraint {
public interface DropDown extends Field, OptionsConstraint, ContainerConstraint, StringConstraint {

/**
* Returns {@code true} if multiple items can be selected in dropdown, {@code false} otherwise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public class DropDownImplTest {
private static final String PATH_MULTISELECT_DROPDOWN = CONTENT_ROOT + "/multiselect-dropdown";
private static final String PATH_MULTISELECT_DROPDOWN_WITH_VARIANT_PROPERTY = CONTENT_ROOT + "/multiselect-dropdown-2";
private static final String PATH_DROPDOWN2 = CONTENT_ROOT + "/dropdown2";
private static final String PATH_SEARCHABLE_DROPDOWN = CONTENT_ROOT + "/searchable-dropdown";

private static final String PATH_DROPDOWN = CONTENT_ROOT + "/dropdown";
private static final String PATH_DROPDOWN_DATALAYER = CONTENT_ROOT + "/dropdown-datalayer";
Expand Down Expand Up @@ -287,6 +288,38 @@ void testMultiSelectJSONExport() throws Exception {
Utils.testJSONExport(dropdown, Utils.getTestExporterJSONPath(BASE, PATH_MULTISELECT_DROPDOWN));
}

@Test
void testStringConstraintsForSingleSelect() {
DropDown dropdown = Utils.getComponentUnderTest(PATH_SEARCHABLE_DROPDOWN, DropDown.class, context);
assertFalse(dropdown.isMultiSelect());
assertEquals(Integer.valueOf(2), dropdown.getMinLength());
assertEquals(Integer.valueOf(10), dropdown.getMaxLength());
assertEquals("[a-z]+", dropdown.getPattern());
}

@Test
void testStringConstraintsNullForMultiSelect() {
// authored minLength/maxLength/pattern must be dropped for a multi-select (array-typed) drop-down
DropDown dropdown = Utils.getComponentUnderTest(PATH_MULTISELECT_DROPDOWN, DropDown.class, context);
assertTrue(dropdown.isMultiSelect());
assertNull(dropdown.getMinLength());
assertNull(dropdown.getMaxLength());
assertNull(dropdown.getPattern());
}

@Test
void testGetPatternDefault() {
DropDown dropdownMock = Mockito.mock(DropDown.class);
Mockito.when(dropdownMock.getPattern()).thenCallRealMethod();
assertNull(dropdownMock.getPattern());
}

@Test
void testSearchableDropdownJSONExport() throws Exception {
DropDown dropdown = Utils.getComponentUnderTest(PATH_SEARCHABLE_DROPDOWN, DropDown.class, context);
Utils.testJSONExport(dropdown, Utils.getTestExporterJSONPath(BASE, PATH_SEARCHABLE_DROPDOWN));
}

@Test
void testGetProperties() throws Exception {
DropDown dropdown = Utils.getComponentUnderTest(PATH_DROPDOWN_1, DropDown.class, context);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"id": "dropdown-6bc8619e69",
"fieldType": "drop-down",
"name": "abc",
"type": "string",
"minLength": 2,
"maxLength": 10,
"enforceEnum": false,
"enumNames": [
"m",
"f",
"o"
],
"pattern": "[a-z]+",
"label": {
"value": "def"
},
"properties": {
"fd:path": "/content/searchable-dropdown"
},
"events": {
"custom:setProperty": [
"$event.payload"
]
},
"enum": [
"a",
"b",
"c"
],
":type": "core/fd/components/form/dropdown/v1/dropdown"
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,24 @@
"enumNames" : ["m", "f", "o"],
"minItems" : 1,
"maxItems" : 2,
"required": true
"required": true,
"minLength" : 2,
"maxLength" : 10,
"pattern" : "[a-z]+"
},
"searchable-dropdown" : {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType" : "core/fd/components/form/dropdown/v1/dropdown",
"name" : "abc",
"jcr:title" : "def",
"fieldType": "drop-down",
"type" : "string",
"enforceEnum" : false,
"enum" : ["a", "b", "c"],
"enumNames" : ["m", "f", "o"],
"minLength" : 2,
"maxLength" : 10,
"pattern" : "[a-z]+"
},
"dropdown2" : {
"jcr:primaryType": "nt:unstructured",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,6 @@
"properties": {
"fieldType": {
"enum": [
"drop-down",
"checkbox-group"
]
}
Expand All @@ -592,7 +591,6 @@
"properties": {
"fieldType": {
"enum": [
"drop-down",
"checkbox-group"
]
},
Expand Down Expand Up @@ -668,7 +666,109 @@
"enum": "Options are missing from selection. Add an empty enum array"
},
"properties": {
"fieldType": "${0/fieldType} is not a valid SelectionField field type. It should be either drop-down or checkbox-group"
"fieldType": "${0/fieldType} is not a valid SelectionField field type. It should be checkbox-group"
}
}
}
},
"DropDownField": {
"if": {
"type": "object",
"properties": {
"fieldType": {
"enum": [
"drop-down"
]
}
},
"required": [
"fieldType"
]
},
"then": {
"type": "object",
"properties": {
"fieldType": {
"enum": [
"drop-down"
]
},
"type": {
"enum": [
"string",
"number",
"boolean",
"string[]",
"number[]",
"boolean[]"
]
},
"rules": {
"type": "object",
"propertyNames": {
"enum": [
"description",
"enabled",
"enum",
"enumNames",
"label",
"properties",
"readOnly",
"required",
"value",
"visible"
]
}
}
},
"propertyNames": {
"enum": [
":type",
"appliedCssClassNames",
"constraintMessages",
"dataLayer",
"dataRef",
"default",
"description",
"enabled",
"enforceEnum",
"enum",
"enumNames",
"events",
"fieldType",
"id",
"label",
"maxItems",
"maxLength",
"minItems",
"minLength",
"name",
"pattern",
"placeholder",
"properties",
"readOnly",
"required",
"rules",
"screenReaderText",
"tooltip",
"type",
"uniqueItems",
"validationExpression",
"visible"
],
"errorMessage": {
"enum": "${0} property is not allowed in DropDownField"
}
},
"required": [
"enum"
],
"errorMessage": {
"required": {
"enum": "Options are missing from selection. Add an empty enum array"
},
"properties": {
"fieldType": "${0/fieldType} is not a valid DropDownField field type. It should be drop-down"
}
}
}
Expand Down Expand Up @@ -1696,6 +1796,9 @@
{
"$ref": "#/$defs/SelectionField"
},
{
"$ref": "#/$defs/DropDownField"
},
{
"$ref": "#/$defs/RadioGroupField"
},
Expand Down
Loading