From 66d14558ae6c28ccee40f9f0d7131aeecf21ed75 Mon Sep 17 00:00:00 2001 From: marikannanm Date: Mon, 12 Aug 2024 18:07:38 +0530 Subject: [PATCH] ImageChoice Component related code --- .../internal/form/FormConstants.java | 3 + .../models/v1/form/ImageChoiceImpl.java | 150 ++++++ .../internal/models/v1/form/ImageItem.java | 72 +++ .../components/models/form/FieldType.java | 3 +- .../components/models/form/ImageChoice.java | 49 ++ .../models/v1/form/ImageChoiceImplTest.java | 374 ++++++++++++++ .../exporter-imagechoice-customized.json | 52 ++ .../exporter-imagechoice-datalayer.json | 49 ++ .../imagechoice/exporter-imagechoice.json | 37 ++ .../form/imagechoice/test-content.json | 204 ++++++++ .../components/form/imagechoice/.content.xml | 7 + .../form/imagechoice/_cq_template.xml | 5 + .../samples/imagechoice/.content.xml | 11 + .../_jcr_content/folderThumbnail/.content.xml | 5 + .../samples/imagechoice/basic/.content.xml | 23 + .../renditions/cq5dam.thumbnail.140.100.png | Bin 0 -> 1000 bytes .../.content.xml | 7 + .../renditions/cq5dam.thumbnail.319.319.png | Bin 0 -> 3181 bytes .../.content.xml | 7 + .../renditions/cq5dam.thumbnail.48.48.png | Bin 0 -> 387 bytes .../.content.xml | 7 + .../samples/imagechoice/.content.xml | 3 + .../samples/imagechoice/basic/.content.xml | 470 ++++++++++++++++++ .../imagechoice/focustest/.content.xml | 76 +++ ui.af.apps/pom.xml | 1 + .../.content.xml | 2 +- .../components/form/imagechoice/.content.xml | 3 + .../form/imagechoice/v1/.content.xml | 4 + .../imagechoice/v1/imagechoice/.content.xml | 8 + .../form/imagechoice/v1/imagechoice/README.md | 98 ++++ .../_cq_design_dialog/.content.xml | 64 +++ .../v1/imagechoice/_cq_dialog/.content.xml | 242 +++++++++ .../v1/imagechoice/_cq_template.xml | 5 + .../v1/imagechoice/clientlibs/.content.xml | 3 + .../clientlibs/editor/.content.xml | 5 + .../v1/imagechoice/clientlibs/editor/js.txt | 18 + .../clientlibs/editor/js/editDialog.js | 75 +++ .../imagechoice/clientlibs/site/.content.xml | 6 + .../v1/imagechoice/clientlibs/site/css.txt | 18 + .../clientlibs/site/css/imagechoiceview.css | 85 ++++ .../v1/imagechoice/clientlibs/site/js.txt | 18 + .../clientlibs/site/js/imagechoiceview.js | 178 +++++++ .../v1/imagechoice/imagechoice.html | 65 +++ .../imagechoice/v1/imagechoice/imagechoice.js | 33 ++ .../libs/commons/formsConstants.js | 1 + .../imagechoice/imagechoice.authoring.spec.js | 228 +++++++++ .../imagechoice/imagechoice.runtime.spec.js | 258 ++++++++++ 47 files changed, 3030 insertions(+), 2 deletions(-) create mode 100644 bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageChoiceImpl.java create mode 100644 bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageItem.java create mode 100644 bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/ImageChoice.java create mode 100644 bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageChoiceImplTest.java create mode 100644 bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice-customized.json create mode 100644 bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice-datalayer.json create mode 100644 bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice.json create mode 100644 bundles/af-core/src/test/resources/form/imagechoice/test-content.json create mode 100644 examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/imagechoice/.content.xml create mode 100644 examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/imagechoice/_cq_template.xml create mode 100644 it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/.content.xml create mode 100644 it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/_jcr_content/folderThumbnail/.content.xml create mode 100644 it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/.content.xml create mode 100644 it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.140.100.png create mode 100644 it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.140.100.png.dir/.content.xml create mode 100644 it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.319.319.png create mode 100644 it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.319.319.png.dir/.content.xml create mode 100644 it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.48.48.png create mode 100644 it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.48.48.png.dir/.content.xml create mode 100644 it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/.content.xml create mode 100644 it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/basic/.content.xml create mode 100644 it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/focustest/.content.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/.content.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/.content.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/.content.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/README.md create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_design_dialog/.content.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_dialog/.content.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_template.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/.content.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/.content.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/js.txt create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/js/editDialog.js create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/.content.xml create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/css.txt create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/css/imagechoiceview.css create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/js.txt create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/js/imagechoiceview.js create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/imagechoice.html create mode 100644 ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/imagechoice.js create mode 100644 ui.tests/test-module/specs/imagechoice/imagechoice.authoring.spec.js create mode 100644 ui.tests/test-module/specs/imagechoice/imagechoice.runtime.spec.js diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormConstants.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormConstants.java index 9e9af9feab..4c234d9d90 100644 --- a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormConstants.java +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/FormConstants.java @@ -106,6 +106,9 @@ private FormConstants() { /** The resource type for image v1 */ public static final String RT_FD_FORM_IMAGE_V1 = RT_FD_FORM_PREFIX + "image/v1/image"; + /** The resource type for image choice v1 */ + public static final String RT_FD_FORM_IMAGECHOICE_V1 = RT_FD_FORM_PREFIX + "imagechoice/v1/imagechoice"; + /** The resource type for title v1 */ public static final String RT_FD_FORM_TITLE_V1 = RT_FD_FORM_PREFIX + "title/v1/title"; diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageChoiceImpl.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageChoiceImpl.java new file mode 100644 index 0000000000..1475a8994b --- /dev/null +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageChoiceImpl.java @@ -0,0 +1,150 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2024 Adobe + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +package com.adobe.cq.forms.core.components.internal.models.v1.form; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Exporter; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.Optional; +import org.apache.sling.models.annotations.Via; +import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy; +import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.adobe.cq.export.json.ComponentExporter; +import com.adobe.cq.export.json.ExporterConstants; +import com.adobe.cq.forms.core.components.internal.form.FormConstants; +import com.adobe.cq.forms.core.components.models.form.CheckBox; +import com.adobe.cq.forms.core.components.models.form.ImageChoice; +import com.adobe.cq.forms.core.components.util.AbstractFieldImpl; +import com.adobe.cq.forms.core.components.util.ComponentUtils; +import com.fasterxml.jackson.annotation.JsonIgnore; + +@Model( + adaptables = { SlingHttpServletRequest.class, Resource.class }, + adapters = { ImageChoice.class, ComponentExporter.class }, + resourceType = { FormConstants.RT_FD_FORM_IMAGECHOICE_V1 }) +@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) +public class ImageChoiceImpl extends AbstractFieldImpl implements ImageChoice { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = "orientation") + @Nullable + protected String orientationJcr; + private CheckBox.Orientation orientation; + + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = "selectionType") + @Nullable + protected String selectionType; + + @Inject + @Via("resource") + @Optional + protected List options = new ArrayList<>(); + + @PostConstruct + private void initImageChoiceModel() { + orientation = CheckBox.Orientation.fromString(orientationJcr); + } + + @Override + public Map getCustomLayoutProperties() { + Map customLayoutProperties = super.getCustomLayoutProperties(); + if (orientation != null) { + customLayoutProperties.put("orientation", orientation); + } + if (selectionType != null) { + customLayoutProperties.put("selectionType", selectionType); + } + return customLayoutProperties; + } + + @JsonIgnore + public CheckBox.Orientation getOrientation() { + return orientation; + } + + @JsonIgnore + public String getSelectionType() { + return selectionType; + } + + public List getOptions() { + options = setImageValueBasedOnSubmissionDataType(); + return options; + } + + private Map removeDuplicates() { + LinkedHashMap map = new LinkedHashMap<>(); + if (options != null) { + for (ImageItem item : options) { + map.put(item.getValue(), item); + } + } + return map; + } + + private List setImageValueBasedOnSubmissionDataType() { + if (options == null) { + return null; + } else { + Map map = removeDuplicates(); + List updatedOptions = new ArrayList<>(); + for (Map.Entry entry : map.entrySet()) { + Object key = entry.getKey(); + ImageItem item = entry.getValue(); + Object coercedValue = coerceImageValue(type, key); + item.setImageValue(coercedValue); + updatedOptions.add(item); + } + return updatedOptions; + } + } + + private Object coerceImageValue(Type type, Object value) { + if (type.equals(Type.NUMBER) || type.equals(Type.NUMBER_ARRAY)) { + return Long.parseLong(value.toString()); + } else if (type.equals(Type.BOOLEAN) || type.equals(Type.BOOLEAN_ARRAY)) { + return Boolean.parseBoolean(value.toString()); + } + return value; + } + + @Override + public Type getType() { + return super.getType(); + } + + @Override + public Object[] getDefault() { + Object[] typedDefaultValue = null; + if (defaultValue != null) { + typedDefaultValue = ComponentUtils.coerce(type, defaultValue); + } + return typedDefaultValue; + } +} diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageItem.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageItem.java new file mode 100644 index 0000000000..0bc6927b21 --- /dev/null +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageItem.java @@ -0,0 +1,72 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2024 Adobe + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +package com.adobe.cq.forms.core.components.internal.models.v1.form; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy; +import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +@Model(adaptables = Resource.class) +public class ImageItem { + @ValueMapValue(name = "imageSrc") + private String imageSrc; + + @JsonIgnore + @ValueMapValue(name = "imageValue") + private String value; + + private Object imageValue; + + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = "altText") + private String altText; + + public String getImageSrc() { + return imageSrc; + } + + public void setImageSrc(String imageSrc) { + this.imageSrc = imageSrc; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Object getImageValue() { + return imageValue; + } + + public void setImageValue(Object imageValue) { + this.imageValue = imageValue; + } + + public String getAltText() { + return altText; + } + + public void setAltText(String altText) { + this.altText = altText; + } + +} diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/FieldType.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/FieldType.java index fb357d8b17..1d42f91a15 100644 --- a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/FieldType.java +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/FieldType.java @@ -35,7 +35,8 @@ public enum FieldType { PANEL("panel"), FORM("form"), CHECKBOX_GROUP("checkbox-group"), - IMAGE("image"); + IMAGE("image"), + IMAGECHOICE("imagechoice"); private String value; diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/ImageChoice.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/ImageChoice.java new file mode 100644 index 0000000000..de3ba9cdce --- /dev/null +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/ImageChoice.java @@ -0,0 +1,49 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2024 Adobe + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +package com.adobe.cq.forms.core.components.models.form; + +import java.util.List; + +import org.osgi.annotation.versioning.ConsumerType; + +import com.adobe.cq.forms.core.components.internal.models.v1.form.ImageItem; + +/** + * Defines the form {@code ImageChoice} Sling Model used for the {@code /apps/core/fd/components/form/imagechoice/v1/imagechoice} + * component. + * + * @since com.adobe.cq.forms.core.components.models.form 2.0.0 + */ +@ConsumerType +public interface ImageChoice extends Field { + + /** + * Returns the type of selection for the image choice component, either single or multiple selection. + * + * @return the type of selection + * @since com.adobe.cq.forms.core.components.models.form 2.0.0 + */ + String getSelectionType(); + + /** + * Returns the list of options associated with the image choice component. + * + * @return the list of options + * @since com.adobe.cq.forms.core.components.models.form 2.0.0 + */ + List getOptions(); +} diff --git a/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageChoiceImplTest.java b/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageChoiceImplTest.java new file mode 100644 index 0000000000..a6a5751aad --- /dev/null +++ b/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/ImageChoiceImplTest.java @@ -0,0 +1,374 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2024 Adobe + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +package com.adobe.cq.forms.core.components.internal.models.v1.form; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; + +import com.adobe.cq.forms.core.components.internal.form.FormConstants; +import com.adobe.cq.forms.core.components.models.form.*; +import com.adobe.cq.forms.core.context.FormsCoreComponentTestContext; +import io.wcm.testing.mock.aem.junit5.AemContext; +import io.wcm.testing.mock.aem.junit5.AemContextExtension; + +import static org.junit.Assert.*; + +@ExtendWith(AemContextExtension.class) +public class ImageChoiceImplTest { + private static final String BASE = "/form/imagechoice"; + private static final String CONTENT_ROOT = "/content"; + private static final String PATH_IMAGECHOICE_CUSTOMIZED = CONTENT_ROOT + "/imagechoice-customized"; + private static final String PATH_IMAGECHOICE = CONTENT_ROOT + "/imagechoice"; + private static final String PATH_IMAGECHOICE_DATALAYER = CONTENT_ROOT + "/imagechoice-datalayer"; + + private static final String PATH_IMAGECHOICE_FOR_INSERTION_ORDER = CONTENT_ROOT + "/imagechoice-insertion-order"; + + private final AemContext context = FormsCoreComponentTestContext.newAemContext(); + + @BeforeEach + void setUp() { + context.load().json(BASE + FormsCoreComponentTestContext.TEST_CONTENT_JSON, CONTENT_ROOT); + } + + private ImageChoice getImageChoiceUnderTest(String resourcePath) { + context.currentResource(resourcePath); + MockSlingHttpServletRequest request = context.request(); + return request.adaptTo(ImageChoice.class); + } + + @Test + void testExportedType() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(FormConstants.RT_FD_FORM_IMAGECHOICE_V1, imageChoice.getExportedType()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getExportedType()).thenCallRealMethod(); + assertEquals("", imageChoiceMock.getExportedType()); + } + + @Test + void testFieldType() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(FieldType.IMAGECHOICE.getValue(), imageChoice.getFieldType()); + } + + @Test + void testGetLabel() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals("Image Choice", imageChoice.getLabel().getValue()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getLabel()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getLabel()); + + Label labelMock = Mockito.mock(Label.class); + Mockito.when(labelMock.isRichText()).thenCallRealMethod(); + assertEquals(null, labelMock.isRichText()); + Mockito.when(labelMock.getValue()).thenCallRealMethod(); + assertEquals(null, labelMock.getValue()); + Mockito.when(labelMock.isVisible()).thenCallRealMethod(); + assertEquals(null, labelMock.isVisible()); + } + + @Test + void testGetName() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals("imagechoice_12345", imageChoice.getName()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getName()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getName()); + } + + @Test + void testGetDataRef() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals("a.b", imageChoice.getDataRef()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getDataRef()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getDataRef()); + } + + @Test + void testGetDescription() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals("long description", imageChoice.getDescription()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getDescription()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getDescription()); + } + + @Test + void testGetScreenReaderText() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals("'custom text'", imageChoice.getScreenReaderText()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getScreenReaderText()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getScreenReaderText()); + } + + @Test + void testIsVisible() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE); + assertEquals(true, imageChoice.isVisible()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.isVisible()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.isVisible()); + } + + @Test + void testIsVisibleForCustomized() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(false, imageChoice.isVisible()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.isVisible()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.isVisible()); + } + + @Test + void testIsEnabled() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE); + assertEquals(true, imageChoice.isEnabled()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.isEnabled()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.isEnabled()); + } + + @Test + void testIsEnabledForCustomized() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(true, imageChoice.isEnabled()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.isEnabled()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.isEnabled()); + } + + @Test + void testIsReadOnly() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE); + assertEquals(false, imageChoice.isReadOnly()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.isReadOnly()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.isReadOnly()); + } + + @Test + void testIsReadOnlyForCustomized() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(true, imageChoice.isReadOnly()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.isReadOnly()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.isReadOnly()); + } + + @Test + void testGetPlaceHolder() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(null, imageChoice.getPlaceHolder()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getPlaceHolder()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getPlaceHolder()); + } + + @Test + void testGetDisplayFormat() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(null, imageChoice.getDisplayFormat()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getDisplayFormat()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getDisplayFormat()); + } + + @Test + void testGetEditFormat() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(null, imageChoice.getEditFormat()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getEditFormat()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getEditFormat()); + } + + @Test + void testGetDataFormat() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(null, imageChoice.getDataFormat()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getDataFormat()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getDataFormat()); + } + + @Test + void testGetTooltip() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals("

short description

", imageChoice.getTooltip()); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getTooltip()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getTooltip()); + } + + @Test + void testGetConstraintMessages() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + Map constraintsMessages = imageChoice.getConstraintMessages(); + assertEquals(constraintsMessages.get(ConstraintType.TYPE), null); + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getConstraintMessages()).thenCallRealMethod(); + assertEquals(Collections.emptyMap(), imageChoiceMock.getConstraintMessages()); + } + + @Test + void testGetProperties() throws Exception { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + Map properties = imageChoice.getProperties(); + assertFalse(properties.isEmpty()); + // get custom properties of "af:layout" + Map customProperties = (Map) properties.get(Base.CUSTOM_PROPERTY_WRAPPER); + assertTrue((boolean) customProperties.get("tooltipVisible")); + } + + @Test + void testGetProperties_should_return_empty_if_no_custom_properties() { + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getProperties()).thenCallRealMethod(); + assertTrue(imageChoiceMock.getProperties().isEmpty()); + } + + @Test + void testGetShortDescription() { + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.getTooltip()).thenCallRealMethod(); + assertEquals(null, imageChoiceMock.getTooltip()); + } + + @Test + void testIsShortDescriptionVisible() { + ImageChoice imageChoiceMock = Mockito.mock(ImageChoice.class); + Mockito.when(imageChoiceMock.isTooltipVisible()).thenCallRealMethod(); + assertEquals(false, imageChoiceMock.isTooltipVisible()); + } + + @Test + void testGetDefault() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertArrayEquals(new String[] { "1" }, imageChoice.getDefault()); + ImageChoice radioButtonMock = Mockito.mock(ImageChoice.class); + Mockito.when(radioButtonMock.getDefault()).thenCallRealMethod(); + assertEquals(null, radioButtonMock.getDefault()); + } + + @Test + void testGetType() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(BaseConstraint.Type.STRING, imageChoice.getType()); + ImageChoice radioButtonMock = Mockito.mock(ImageChoice.class); + Mockito.when(radioButtonMock.getType()).thenCallRealMethod(); + assertEquals(BaseConstraint.Type.STRING, radioButtonMock.getType()); + } + + @Test + void testGetOrientation() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(CheckBox.Orientation.VERTICAL, ((ImageChoiceImpl) imageChoice).getOrientation()); + } + + @Test + void testDorProperties() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals(true, imageChoice.getDorProperties().get("dorExclusion")); + assertEquals("4", imageChoice.getDorProperties().get("dorColspan")); + + } + + @Test + void testGetOptions() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + ImageItem item1 = new ImageItem(); + assertNotNull(imageChoice.getOptions()); + item1.setAltText("aem-corecomponents-logo"); + item1.setImageSrc("/content/dam/core-components-examples/library/aem-corecomponents-logo.svg"); + item1.setImageValue("1"); + item1.setValue("1"); + ImageItem item2 = new ImageItem(); + item2.setAltText("adobe-logo"); + item2.setImageSrc("/content/dam/core-components-examples/library/adobe-logo.svg"); + item2.setImageValue("2"); + item2.setValue("2"); + // Add the mock ImageItem objects to the options list + imageChoice.getOptions().add(item1); + imageChoice.getOptions().add(item2); + + assertEquals(2, imageChoice.getOptions().size()); + + // Assertions for the first ImageItem + assertEquals("1", imageChoice.getOptions().get(0).getValue()); + assertEquals("aem-corecomponents-logo", imageChoice.getOptions().get(0).getAltText()); + assertEquals("/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", imageChoice.getOptions().get(0) + .getImageSrc()); + assertEquals("1", imageChoice.getOptions().get(0).getImageValue()); + + // Assertions for the second ImageItem + assertEquals("2", imageChoice.getOptions().get(1).getValue()); + assertEquals("adobe-logo", imageChoice.getOptions().get(1).getAltText()); + assertEquals("/content/dam/core-components-examples/library/adobe-logo.svg", imageChoice.getOptions().get(1).getImageSrc()); + assertEquals("2", imageChoice.getOptions().get(1).getImageValue()); + } + + @Test + void testGetOptionsWithNullOptions() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + ((ImageChoiceImpl) imageChoice).options = null; + List result = ((ImageChoiceImpl) imageChoice).getOptions(); + assertNull(result); + } + + @Test + void testCoerceImageValue() throws Exception { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + Method method = ((ImageChoiceImpl) imageChoice).getClass().getDeclaredMethod("coerceImageValue", BaseConstraint.Type.class, + Object.class); + method.setAccessible(true); + // Test boolean type + Object coercedBooleanValue = method.invoke(imageChoice, BaseConstraint.Type.BOOLEAN, "true"); + assertTrue(coercedBooleanValue instanceof Boolean); + assertTrue((Boolean) coercedBooleanValue); + // Test number type + Object coercedNumberValue = method.invoke(imageChoice, BaseConstraint.Type.NUMBER, "123"); + assertTrue(coercedNumberValue instanceof Long); + assertEquals(123L, (long) coercedNumberValue); + // Test String types + BaseConstraint.Type type = BaseConstraint.Type.STRING; + Object value = "test"; + Object coercedNonBooleanValue = method.invoke(imageChoice, type, value); + assertEquals(value, coercedNonBooleanValue); + } + + @Test + void testGetSelectionType() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE_CUSTOMIZED); + assertEquals("single", ((ImageChoiceImpl) imageChoice).getSelectionType()); + } + + @Test + void testGetSelectionTypeWithMultiple() { + ImageChoice imageChoice = getImageChoiceUnderTest(PATH_IMAGECHOICE); + assertEquals("multiple", ((ImageChoiceImpl) imageChoice).getSelectionType()); + } +} diff --git a/bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice-customized.json b/bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice-customized.json new file mode 100644 index 0000000000..0e2559845f --- /dev/null +++ b/bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice-customized.json @@ -0,0 +1,52 @@ +{ + ":type": "forms-components-examples/components/form/imagechoice", + "constraintMessages": { + "required": "This is a required field", + "validationExpression": "Required Expression" + }, + "dataRef": "a.b", + "default": "1", + "description": "long description", + "enabled": true, + "events": { + "custom:setProperty": [ + "$event.payload" + ] + }, + "fieldType": "imagechoice", + "id": "imagechoice-3922625f43", + "label": { + "value": "Image Choice" + }, + "name": "imagechoice_12345", + "options": { + "item0": { + "altText": "aem-corecomponents-logo", + "imageSrc": "/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", + "imageValue": "1" + }, + "item1": { + "altText": "adobe-logo", + "imageSrc": "/content/dam/core-components-examples/library/adobe-logo.svg", + "imageValue": "2" + } + }, + "properties": { + "afs:layout": { + "orientation": "vertical", + "selectionType": "single", + "tooltipVisible": true + }, + "fd:dor": { + "dorColspan": "4", + "dorExclusion": true + }, + "fd:path": "/content/imagechoice-customized" + }, + "readOnly": true, + "required": true, + "screenReaderText": "'custom text'", + "tooltip": "

short description

", + "type": "string[]", + "visible": false +} \ No newline at end of file diff --git a/bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice-datalayer.json b/bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice-datalayer.json new file mode 100644 index 0000000000..ed85a8d37b --- /dev/null +++ b/bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice-datalayer.json @@ -0,0 +1,49 @@ +{ + ":type": "forms-components-examples/components/form/imagechoice", + "dataLayer": { + "imagechoice-337847e8a8": { + "@type": "core/fd/components/form/imagechoice/v1/imagechoice", + "dc:description": "Your preferred option", + "dc:title": "Select your preferred option", + "fieldType": "imagechoice" + } + }, + "description": "Input your preferred option", + "enabled": true, + "events": { + "custom:setProperty": [ + "$event.payload" + ] + }, + "fieldType": "imagechoice", + "id": "imagechoice-337847e8a8", + "label": { + "value": "Preferred Option" + }, + "name": "PreferredOption", + "options": { + "item0": { + "altText": "aem-corecomponents-logo", + "imageSrc": "/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", + "imageValue": "1" + }, + "item1": { + "altText": "adobe-logo", + "imageSrc": "/content/dam/core-components-examples/library/adobe-logo.svg", + "imageValue": "2" + } + }, + "properties": { + "afs:layout": { + "orientation": "horizontal", + "selectionType": "single" + }, + "fd:dor": { + "dorExclusion": false + }, + "fd:path": "/content/imagechoice-datalayer" + }, + "readOnly": false, + "type": "string[]", + "visible": true +} \ No newline at end of file diff --git a/bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice.json b/bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice.json new file mode 100644 index 0000000000..24310ffe0f --- /dev/null +++ b/bundles/af-core/src/test/resources/form/imagechoice/exporter-imagechoice.json @@ -0,0 +1,37 @@ +{ + ":type": "forms-components-examples/components/form/imagechoice", + "events": { + "custom:setProperty": [ + "$event.payload" + ] + }, + "fieldType": "imagechoice", + "id": "imagechoice-3922625f42", + "label": { + "value": "Image Choice" + }, + "name": "imagechoice1709406386838", + "options": { + "item0": { + "altText": "aem-corecomponents-logo", + "imageSrc": "/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", + "imageValue": "1" + }, + "item1": { + "altText": "adobe-logo", + "imageSrc": "/content/dam/core-components-examples/library/adobe-logo.svg", + "imageValue": "2" + } + }, + "properties": { + "afs:layout": { + "orientation": "horizontal", + "selectionType": "single" + }, + "fd:dor": { + "dorExclusion": false + }, + "fd:path": "/content/forms/af/core-components-it/samples/imagechoice/basic/jcr:content/guideContainer/imagechoice" + }, + "type": "string[]" +} \ No newline at end of file diff --git a/bundles/af-core/src/test/resources/form/imagechoice/test-content.json b/bundles/af-core/src/test/resources/form/imagechoice/test-content.json new file mode 100644 index 0000000000..25091201e3 --- /dev/null +++ b/bundles/af-core/src/test/resources/form/imagechoice/test-content.json @@ -0,0 +1,204 @@ +{ + "imagechoice": { + ":type": "forms-components-examples/components/form/imagechoice", + "fieldType": "imagechoice", + "id": "imagechoice-3922625f42", + "jcr:created": "Sun Mar 03 2024 00:36:26 GMT+0530", + "jcr:createdBy": "admin", + "jcr:lastModified": "Sun Mar 03 2024 00:55:31 GMT+0530", + "jcr:lastModifiedBy": "admin", + "jcr:primaryType": "nt:unstructured", + "jcr:title": "Image Choice", + "name": "imagechoice1709406386838", + "options": { + "item0": { + "altText": "aem-corecomponents-logo", + "imageSrc": "/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", + "imageValue": "1" + }, + "item1": { + "altText": "adobe-logo", + "imageSrc": "/content/dam/core-components-examples/library/adobe-logo.svg", + "imageValue": "2" + } + }, + "orientation": "horizontal", + "selectionType": "multiple", + "sling:resourceType": "core/fd/components/form/imagechoice/v1/imagechoice", + "properties": { + "afs:layout": { + "orientation": "horizontal", + "selectionType": "multiple" + }, + "fd:dor": { + "dorExclusion": false + }, + "fd:path": "/content/forms/af/core-components-it/samples/imagechoice/basic/jcr:content/guideContainer/imagechoice" + }, + "type": "string[]" + }, + "imagechoice-customized": { + ":type": "forms-components-examples/components/form/imagechoice", + "assistPriority": "custom", + "custom": "custom text", + "dataRef": "a.b", + "default": "1", + "description": "long description", + "dorColspan": "4", + "dorExclusion": true, + "enabled": true, + "fieldType": "imagechoice", + "id": "imagechoice-3922625f43", + "jcr:created": "Sun Mar 03 2024 00:36:26 GMT+0530", + "jcr:createdBy": "admin", + "jcr:lastModified": "Sun Mar 03 2024 00:55:31 GMT+0530", + "jcr:lastModifiedBy": "admin", + "jcr:primaryType": "nt:unstructured", + "jcr:title": "Image Choice", + "mandatoryMessage": "This is a required field", + "name": "imagechoice_12345", + "options": { + "item0": { + "altText": "aem-corecomponents-logo", + "imageSrc": "/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", + "imageValue": "1" + }, + "item1": { + "altText": "adobe-logo", + "imageSrc": "/content/dam/core-components-examples/library/adobe-logo.svg", + "imageValue": "2" + } + }, + "orientation": "vertical", + "readOnly": true, + "required": "true", + "selectionType": "single", + "sling:resourceType": "core/fd/components/form/imagechoice/v1/imagechoice", + "textIsRich": [ + "true", + "true" + ], + "properties": { + "afs:layout": { + "orientation": "vertical", + "selectionType": "single", + "tooltipVisible": true + }, + "fd:dor": { + "dorColspan": "4", + "dorExclusion": true + }, + "fd:path": "/content/forms/af/core-components-it/samples/imagechoice/basic/jcr:content/guideContainer/imagechoice" + }, + "tooltip": "

short description

", + "tooltipVisible": "true", + "type": "string", + "validateExpMessage": "Required Expression", + "visible": false + }, + "imagechoice-datalayer": { + ":type": "forms-components-examples/components/form/imagechoice", + "description": "Input your preferred option", + "enabled": true, + "events": { + "custom:setProperty": [ + "$event.payload" + ] + }, + "fieldType": "imagechoice", + "id": "imagechoice-337847e8a8", + "jcr:title": "Select your preferred option", + "label": { + "value": "Preferred Option" + }, + "name": "PreferredOption", + "options": { + "item0": { + "altText": "aem-corecomponents-logo", + "imageSrc": "/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", + "imageValue": "1" + }, + "item1": { + "altText": "adobe-logo", + "imageSrc": "/content/dam/core-components-examples/library/adobe-logo.svg", + "imageValue": "2" + } + }, + "properties": { + "afs:layout": { + "orientation": "horizontal", + "selectionType": "single" + }, + "fd:dor": { + "dorExclusion": false + }, + "fd:path": "/content/forms/af/core-components-it/samples/imagechoice/basic/jcr:content/guideContainer/imagechoice" + }, + "readOnly": false, + "sling:resourceType": "core/fd/components/form/imagechoice/v1/imagechoice", + "type": "string[]", + "visible": true + }, + "imagechoice-insertion-order": { + ":type": "forms-components-examples/components/form/imagechoice", + "enabled": true, + "events": { + "custom:setProperty": [ + "$event.payload" + ] + }, + "fieldType": "imagechoice", + "id": "imagechoice-420118894b", + "label": { + "value": "Image Choice", + "visible": true + }, + "name": "imagechoice21333404011709406558711", + "options": { + "item0": { + "altText": "aem-corecomponents-logo", + "imageSrc": "/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", + "imageValue": "1" + }, + "item1": { + "altText": "adobe-logo", + "imageSrc": "/content/dam/core-components-examples/library/adobe-logo.svg", + "imageValue": "2" + }, + "item2": { + "altText": "aem-corecomponents-logo", + "imageSrc": "/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", + "imageValue": "2" + }, + "item3": { + "altText": "adobe-logo", + "imageSrc": "/content/dam/core-components-examples/library/adobe-logo.svg", + "imageValue": "3" + }, + "item4": { + "altText": "aem-corecomponents-logo", + "imageSrc": "/content/dam/core-components-examples/library/aem-corecomponents-logo.svg", + "imageValue": "4" + }, + "item5": { + "altText": "adobe-logo", + "imageSrc": "/content/dam/core-components-examples/library/adobe-logo.svg", + "imageValue": "5" + } + }, + "properties": { + "afs:layout": { + "orientation": "horizontal", + "selectionType": "single", + "tooltipVisible": true + }, + "fd:dor": { + "dorExclusion": false + }, + "fd:path": "/content/forms/af/core-components-it/samples/imagechoice/basic/jcr:content/guideContainer/imagechoice" + }, + "readOnly": false, + "type": "string[]", + "visible": true + } +} \ No newline at end of file diff --git a/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/imagechoice/.content.xml b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/imagechoice/.content.xml new file mode 100644 index 0000000000..8ebb6827ef --- /dev/null +++ b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/imagechoice/.content.xml @@ -0,0 +1,7 @@ + + diff --git a/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/imagechoice/_cq_template.xml b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/imagechoice/_cq_template.xml new file mode 100644 index 0000000000..b2011a118c --- /dev/null +++ b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/components/form/imagechoice/_cq_template.xml @@ -0,0 +1,5 @@ + + diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/.content.xml b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/.content.xml new file mode 100644 index 0000000000..7c72e9aab6 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/.content.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/_jcr_content/folderThumbnail/.content.xml b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/_jcr_content/folderThumbnail/.content.xml new file mode 100644 index 0000000000..adc9c1601e --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/_jcr_content/folderThumbnail/.content.xml @@ -0,0 +1,5 @@ + + + + diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/.content.xml b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/.content.xml new file mode 100644 index 0000000000..d6c1e8200d --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/.content.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.140.100.png b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.140.100.png new file mode 100644 index 0000000000000000000000000000000000000000..7e350c86b0063bb03eccfb24fab8636bb2b1c534 GIT binary patch literal 1000 zcmeAS@N?(olHy`uVBq!ia0vp^6+oQI!3HGTbkwIaFfgz8ba4!+hH6!^l~=PCK0e{Yx^LgUY2Dr2FTYkT+!**lcE{B$&y^uu z|4NTfO42IOn?8BxPRo-iMsZ5NdG6$GPtDHuzIOfk(u)}_$0jFfZE(AKV)Eq4MI|LB zpHA#!vbV80@#)hhA75Y3r9qs%>L)`UG)|r@JagvEiOtQ;6aNKWd0E2r?qgz1T->Qw zuU4&Dvxa5i-H%IV@~n9u^EYzamMtPRZ(bgs|GQpi$IhJ-TU%Ly#wgb+_Dwo@fO-4& z?UT+wfBN&MrAgYs%a-A+%PuJO&+*!4e^H1e= zz20XT|MmJ7Uwn0P`P{1i=6^rTaOIH^WUfAR;G;o-3(p%)=HrJC2pVos=aJ!Oc0Y1p zoBo;B5C65bdh`9F1l#NNw(Q@lCr_wI*lUrVo*twj5;u3=ytceE(R_E_ zmN}ny++P;I#M8rOLs9(SvuVa9g*xolimw)z&2Rp_*V91oM|}iejwH-hn3Fk~e{@ohO9vXC2)0LcTRj>D%vq+l{f4=Rcn#{?_&3D&>{`T<@J!bC$P0Kb1G7 z^X7qFN$-FBu!uPLyf1Qrw%pfk3$CUvW>#Z^Js`XNK*)ovty-5aU*5E1 zheV7`c};}fw-*=6*S%9;^t + + + diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.319.319.png b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.319.319.png new file mode 100644 index 0000000000000000000000000000000000000000..c3d97cde70adf169f1018ad4fc83bc39e52e5540 GIT binary patch literal 3181 zcmd^CX;70%8V*v6GpL}jDiR^cAIXhwq|NlZ|VELS3+94bV^kqH<+LV#Rf!bWG^TL15lshS^MZ*})u-CghT zJWuBz4h>jgZflM}AXWq(^gn_?nD`mT*I$_#S0p(ONCaZ}jzIrCM>C@3!<~gk2k1=- zc%5~GkWiG!>H-}VB?!|$wOgG)d}o>6o_Kb*?KC8>Uou6F1$))^-vpINhO2P(D~_cV)Zt)Mw{8Z+r8^hz_6Z z7y*q813;RT`BxE23UaH!v*r3UY+HQb#=;A6@reHwIF+iG8Ryh?y(07kzFV49xQ;S8>} zv1f$xb$#*L(t%_moT#GDF`42(KXG$khq6qXPDrNsY!?B%ERo6hD!w^46Yqv;m?C6? zD*4YYLG&w(1XT}2`Dl4)+MWnwlAC(>W8;Z&QEyd+m z#3?Pz3<>s0P6~!5h6-u(cB4Z&{lkYsl8B*sanpbld}FMWWZgAEf#TPa4OZDnhuW1D zQZJFJK8!qg-B_B$g|_CozMHLOYsXN&9O;on+898VvjnAFfG6te%7Sb<9KqEY7bl8T zjr4{-B`3Kigkm2=Y4q-isFWs$&R(BIPiVT7T6p>m9T0NlBk8+=7Uq)X(LNcGVk`e} z=n0@n#(O)<96ce2&hXcICm-PN$Q6CCm!BLHodxstx-xvsw*+6M$B)&z`@Io z#7R9kc_r?+Dhrfv<;IO0wQbu^2 z?%bKZ-oWgScDOycxw*a7Ar)+CYR~QcvKP1Zkw@bhMlPgUqN?u%6cPyLtb*9)+!b`X zKQtIQR&z(gi!}^o)#6pt!$Gdh&xq zrr?z`lom$!e$|NW&#E=^nuPd^psI30*D%$8Kj@=|h&e}9Ki=eind_7>YjtkXpr_k? z4-LK^>(oR%6`W6l)e0f@bgW2GzB8fs=JvQxWSS6YAvQ`0P8gm=6q6e|I-B?|hhQTu zZN@$zL4m2f^#)ieGCs*ok+6qXT1rH)3eJpo+@Ra-g`Sk}&llGkQ?oJ2s2{R~rCX!Q zeYL;b@jwx>55l%3#3f@b6!-IR>^Sr3 z7{9$w=iR-^FLD(ItPA@>SA8kCT3ZX&_R0EceEhOLsj>bsN;O}#GyZR%x$;Zq{N2p- a?iK~w9WQRr)EU1Kh`{}!{txz^{^8$8%x~`i literal 0 HcmV?d00001 diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.319.319.png.dir/.content.xml b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.319.319.png.dir/.content.xml new file mode 100644 index 0000000000..34d9f64840 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.319.319.png.dir/.content.xml @@ -0,0 +1,7 @@ + + + + diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.48.48.png b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.48.48.png new file mode 100644 index 0000000000000000000000000000000000000000..8aae24bb92b1711271888ec23749bf45aead9024 GIT binary patch literal 387 zcmV-}0et?6P)Nkl2Wd5Qg*r|4@67RzX_)13lD3dXaiiV|8tJy-a-rLfM7vhk-TC9G=TeX7Dkv`+At? znQobbUbr<_AT}%z8y1KS3&e&6E-_6L2_cO7H3 z3aabc=(!Li0Gg(uBuS|2I-|EDr97k8S0JC~IhAE;^jrxR0C>BnzNe2y((Cct3(WjT zb^ZtRy*_Lb0G&|u_$KJHmtH(9Gnm!)`EnJ3=l98|+jv@LSYW#TbRQOKp;dg)O)T32_X6+0^5R^ElxuokvvUP8iv8>yAUMc%5Fbs_5=&Wh6Q57 h0)nYrp^i002ovPDHLkV1iRkr)mHI literal 0 HcmV?d00001 diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.48.48.png.dir/.content.xml b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.48.48.png.dir/.content.xml new file mode 100644 index 0000000000..34d9f64840 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/imagechoice/basic/_jcr_content/renditions/cq5dam.thumbnail.48.48.png.dir/.content.xml @@ -0,0 +1,7 @@ + + + + diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/.content.xml new file mode 100644 index 0000000000..a0ac99e384 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/basic/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/basic/.content.xml new file mode 100644 index 0000000000..108d70b645 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/basic/.content.xml @@ -0,0 +1,470 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/focustest/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/focustest/.content.xml new file mode 100644 index 0000000000..cd8c6c706d --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/imagechoice/focustest/.content.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + diff --git a/ui.af.apps/pom.xml b/ui.af.apps/pom.xml index 38362a2211..7f3f7da549 100644 --- a/ui.af.apps/pom.xml +++ b/ui.af.apps/pom.xml @@ -310,6 +310,7 @@ + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/.content.xml index 99d071211e..73d7029d08 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/.content.xml +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/.content.xml @@ -5,4 +5,4 @@ cssProcessor="[default:none,min:none]" jsProcessor="[default:none,min:none]" categories="[core.forms.components.runtime.all]" - embed="[core.forms.components.runtime.base,core.forms.components.container.v2.runtime,core.forms.components.datePicker.v1.runtime,core.forms.components.textinput.v1.runtime,core.forms.components.numberinput.v1.runtime,core.forms.components.panelcontainer.v1.runtime,core.forms.components.radiobutton.v1.runtime,core.forms.components.text.v1.runtime,core.forms.components.checkboxgroup.v1.runtime,core.forms.components.button.v1.runtime,core.forms.components.image.v1.runtime,core.forms.components.dropdown.v1.runtime,core.forms.components.fileinput.v3.runtime,core.forms.components.accordion.v1.runtime,core.forms.components.tabs.v1.runtime,core.forms.components.wizard.v1.runtime,core.forms.components.verticaltabs.v1.runtime,core.forms.components.recaptcha.v1.runtime,core.forms.components.checkbox.v1.runtime,core.forms.components.fragment.v1.runtime,core.forms.components.switch.v1.runtime,core.forms.components.termsandconditions.v1.runtime, core.forms.components.hcaptcha.v1.runtime]"/> + embed="[core.forms.components.runtime.base,core.forms.components.container.v2.runtime,core.forms.components.datePicker.v1.runtime,core.forms.components.textinput.v1.runtime,core.forms.components.numberinput.v1.runtime,core.forms.components.panelcontainer.v1.runtime,core.forms.components.radiobutton.v1.runtime,core.forms.components.text.v1.runtime,core.forms.components.checkboxgroup.v1.runtime,core.forms.components.button.v1.runtime,core.forms.components.image.v1.runtime,core.forms.components.imagechoice.v1.runtime,core.forms.components.dropdown.v1.runtime,core.forms.components.fileinput.v3.runtime,core.forms.components.accordion.v1.runtime,core.forms.components.tabs.v1.runtime,core.forms.components.wizard.v1.runtime,core.forms.components.verticaltabs.v1.runtime,core.forms.components.recaptcha.v1.runtime,core.forms.components.checkbox.v1.runtime,core.forms.components.fragment.v1.runtime,core.forms.components.switch.v1.runtime,core.forms.components.termsandconditions.v1.runtime, core.forms.components.hcaptcha.v1.runtime]"/> diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/.content.xml new file mode 100644 index 0000000000..491392d539 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/.content.xml new file mode 100644 index 0000000000..5e25fbe65e --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/.content.xml @@ -0,0 +1,4 @@ + + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/.content.xml new file mode 100644 index 0000000000..3c2115baf1 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/.content.xml @@ -0,0 +1,8 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/README.md b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/README.md new file mode 100644 index 0000000000..7a2ef82e3c --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/README.md @@ -0,0 +1,98 @@ + +Adaptive Form Image Choice (v1) +==== +Adaptive Form Image Choice component written in HTL. + +## Features + +* Provides the following type of input: + * checkbox or radio +* Custom constraint messages for the above types +* Styles + +### Use Object +The Form Image Choice component uses the `com.adobe.cq.forms.core.components.models.form.ImageChoice` Sling Model for its Use-object. + +### Edit Dialog Properties +The following properties are written to JCR for this Form Image Choice`component and are expected to be available as `Resource` properties: + +1. `./jcr:title` - defines the label to use for this field +2. `./hideTitle` - if set to `true`, the label of this field will be hidden +3. `./name` - defines the name of the field, which will be submitted with the form data +4. `./default` - defines the default value of the field +5. `./description` - defines a help message that can be rendered in the field as a hint for the user +6. `./required` - if set to `true`, this field will be marked as required, not allowing the form to be submitted until the field has a value +7. `./requiredMessage` - defines the message displayed as tooltip when submitting the form if the value is left empty +8. `./readOnly` - if set to `true`, the filed will be read only +9. `./type` - defines the data type of the value +9. `./imageValue` - defines the set of possible values for this field +10. `./imageSrc` - Image Src +11. `./altText` - alternate text for image +12. `./selectionType` - defines the data type of field which can be radio or checkboxgroup + +## Client Libraries +The component provides a `core.forms.components.imagechoice.v1.runtime` client library category that contains the Javascript runtime for the component. +It should be added to a relevant site client library using the `embed` property. + +It also provides a `core.forms.components.imagechoice.v1.editor` editor client library category that includes +JavaScript handling for dialog interaction. It is already included by its edit dialog. + +## BEM Description +``` +BLOCK cmp-adaptiveform-imagechoice + ELEMENT cmp-adaptiveform-imagechoice__label + ELEMENT cmp-adaptiveform-imagechoice__label-container + ELEMENT cmp-adaptiveform-imagechoice__widget + ELEMENT cmp-adaptiveform-imagechoice__questionmark + ELEMENT cmp-adaptiveform-imagechoice__shortdescription + ELEMENT cmp-adaptiveform-imagechoice__longdescription + ELEMENT cmp-adaptiveform-imagechoice__errormessage +``` + +### Note +By placing the class names `cmp-adaptiveform-imagechoice__label` and `cmp-adaptiveform-imagechoice__questionmark` within the `cmp-adaptiveform-imagechoice__label-container` class, you create a logical grouping of the label and question mark elements. This approach simplifies the process of maintaining a consistent styling for both elements. + +## JavaScript Data Attribute Bindings + +The following attributes must be added for the initialization of the imagechoice component in the form view: +1. `data-cmp-is="adaptiveFormImagechoice"` +2. `data-cmp-adaptiveformcontainer-path="${formstructparser.formContainerPath}"` + + +The following are optional attributes that can be added to the component in the form view: +1. `data-cmp-valid` having a boolean value to indicate whether the field is currently valid or not +2. `data-cmp-required` having a boolean value to indicate whether the field is currently required or not +3. `data-cmp-readonly` having a boolean value to indicate whether the field is currently readonly or not +4. `data-cmp-active` having a boolean value to indicate whether the field is currently active or not +5. `data-cmp-visible` having a boolean value to indicate whether the field is currently visible or not +6. `data-cmp-enabled` having a boolean value to indicate whether the field is currently enabled or not + +## Replace feature: +We support replace feature that allows replacing Reset Button component to any of the below components: + +* Drop Down +* Radio Button +* Image Choice + +## Information +* **Vendor**: Adobe +* **Version**: v1 +* **Compatibility**: Cloud +* **Status**: production-ready + + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_design_dialog/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_design_dialog/.content.xml new file mode 100644 index 0000000000..106848d5b4 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_design_dialog/.content.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_dialog/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_dialog/.content.xml new file mode 100644 index 0000000000..06b3a09903 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_dialog/.content.xml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_template.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_template.xml new file mode 100644 index 0000000000..41fa748867 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/_cq_template.xml @@ -0,0 +1,5 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/.content.xml new file mode 100644 index 0000000000..491392d539 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/.content.xml new file mode 100644 index 0000000000..7d3bb51610 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/.content.xml @@ -0,0 +1,5 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/js.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/js.txt new file mode 100644 index 0000000000..8a845d07a4 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/js.txt @@ -0,0 +1,18 @@ +############################################################################### +# Copyright 2024 Adobe +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### + +#base=js +editDialog.js \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/js/editDialog.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/js/editDialog.js new file mode 100644 index 0000000000..bf4144ecd5 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/editor/js/editDialog.js @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright 2024 Adobe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +(function($) { + "use strict"; + + var EDIT_DIALOG = ".cmp-adaptiveform-imagechoice__editdialog", + IMAGECHOICE_ASSISTPRIORITY = EDIT_DIALOG + " .cmp-adaptiveform-imagechoice__assistprioritycustom", + IMAGECHOICE_CUSTOMTEXT = EDIT_DIALOG + " .cmp-adaptiveform-imagechoice__customtext", + IMAGECHOICE_DATATYPE = EDIT_DIALOG + " .cmp-adaptiveform-imagechoice__type", + IMAGECHOICE_DEFAULTVALUE = EDIT_DIALOG + " .cmp-adaptiveform-imagechoice__value", + IMAGECHOICE_OPTION = EDIT_DIALOG + " .cmp-adaptiveform-base__option", + Utils = window.CQ.FormsCoreComponents.Utils.v1; + + + /** + * Shows custom text box depending on the value of assist priority of radio button + * @param {HTMLElement} dialog The dialog on which the operation is to be performed. + */ + function handleAssistPriorityChange(dialog) { + var assistpriority = dialog.find(IMAGECHOICE_ASSISTPRIORITY); + var customtext = dialog.find(IMAGECHOICE_CUSTOMTEXT); + var hideAndShowElements = function() { + if(assistpriority[0].value === "custom"){ + customtext.show(); + } else { + customtext.hide(); + } + }; + hideAndShowElements(); + dialog.on("change", assistpriority, function() { + hideAndShowElements(); + }); + } + + var registerDialogValidator = Utils.registerDialogDataTypeValidators( + IMAGECHOICE_DEFAULTVALUE + " input", + IMAGECHOICE_OPTION, + function (dialog) { + var selectedValue = ''; + var checkboxSaveValue = dialog.find(IMAGECHOICE_DATATYPE + " coral-select"); + if (checkboxSaveValue && checkboxSaveValue.length > 0) { + selectedValue = checkboxSaveValue[0].selectedItem ? checkboxSaveValue[0].selectedItem.value : ''; + } + var dataType = ''; + switch (selectedValue) { + case 'string[]': + dataType = 'string'; + break; + case 'number[]': + dataType = 'number'; + break; + case 'boolean[]': + dataType = 'boolean'; + break; + } + return dataType; + } + ); + + Utils.initializeEditDialog(EDIT_DIALOG)(handleAssistPriorityChange, registerDialogValidator); + +})(jQuery); diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/.content.xml new file mode 100644 index 0000000000..5f0f2bdf1b --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/.content.xml @@ -0,0 +1,6 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/css.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/css.txt new file mode 100644 index 0000000000..7743cbc1b3 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/css.txt @@ -0,0 +1,18 @@ +############################################################################### +# Copyright 2024 Adobe +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### + +#base=css +imagechoiceview.css diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/css/imagechoiceview.css b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/css/imagechoiceview.css new file mode 100644 index 0000000000..2b4bde6331 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/css/imagechoiceview.css @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright 2024 Adobe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +/** +## BEM Description +BLOCK cmp-adaptiveform-imagechoice + ELEMENT cmp-adaptiveform-imagechoice__label + ELEMENT cmp-adaptiveform-imagechoice__widget + ELEMENT cmp-adaptiveform-imagechoice__questionmark + ELEMENT cmp-adaptiveform-imagechoice__shortdescription + ELEMENT cmp-adaptiveform-imagechoice__longdescription +*/ + + + +.cmp-adaptiveform-imagechoice { + +} + +.cmp-adaptiveform-imagechoice__widget { + +} + +.cmp-adaptiveform-imagechoice__label { + +} +.cmp-adaptiveform-imagechoice__label-container{ + +} + +.cmp-adaptiveform-imagechoice__widget__label { +} + +.cmp-adaptiveform-imagechoice__longdescription { + +} + +.cmp-adaptiveform-imagechoice__shortdescription { + +} + +.cmp-adaptiveform-imagechoice__questionmark { + +} + +.cmp-adaptiveform-imagechoice__options { + +} + +.cmp-adaptiveform-imagechoice__option { + +} + +.cmp-adaptiveform-imagechoice__option--selected { + +} + +.cmp-adaptiveform-imagechoice__option input[type="checkbox"], +.cmp-adaptiveform-imagechoice__option input[type="radio"] { + +} + +.cmp-adaptiveform-imagechoice__option-label { + +} + +.cmp-adaptiveform-imagechoice__option-image { + +} + +.cmp-adaptiveform-imagechoice__option__alt-text { + +} \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/js.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/js.txt new file mode 100644 index 0000000000..14352fc6dd --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/js.txt @@ -0,0 +1,18 @@ +############################################################################### +# Copyright 2024 Adobe +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### + +#base=js +imagechoiceview.js diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/js/imagechoiceview.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/js/imagechoiceview.js new file mode 100644 index 0000000000..acaa43db6f --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/clientlibs/site/js/imagechoiceview.js @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright 2024 Adobe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +(function() { + + "use strict"; + class ImageChoice extends FormView.FormFieldBase { + + static NS = FormView.Constants.NS; + /** + * Each FormField has a data attribute class that is prefixed along with the global namespace to + * distinguish between them. If a component wants to put a data-attribute X, the attribute in HTML would be + * data-{NS}-{IS}-x="" + * @type {string} + */ + static IS = "adaptiveFormImageChoice"; + static bemBlock = 'cmp-adaptiveform-imagechoice' + static selectors = { + self: "[data-" + this.NS + '-is="' + this.IS + '"]', + widgets: `.${ImageChoice.bemBlock}__widget`, + widget: `.${ImageChoice.bemBlock}__option__widget`, + widgetLabel: `.${ImageChoice.bemBlock}__label`, + label: `.${ImageChoice.bemBlock}__label`, + description: `.${ImageChoice.bemBlock}__longdescription`, + qm: `.${ImageChoice.bemBlock}__questionmark`, + errorDiv: `.${ImageChoice.bemBlock}__errormessage`, + tooltipDiv: `.${ImageChoice.bemBlock}__shortdescription`, + option: `.${ImageChoice.bemBlock}-option`, + optionLabel: `${ImageChoice.bemBlock}__option-label` + }; + + constructor(params) { + super(params); + this.qm = this.element.querySelector(ImageChoice.selectors.qm) + this.widgetLabel = this.element.querySelector(ImageChoice.selectors.widgetLabel) + } + + getWidgets() { + return this.element.querySelector(ImageChoice.selectors.widgets); + } + + getWidget() { + return this.element.querySelectorAll(ImageChoice.selectors.widget); + } + + getDescription() { + return this.element.querySelector(ImageChoice.selectors.description); + } + + getLabel() { + return this.element.querySelector(ImageChoice.selectors.label); + } + + getErrorDiv() { + return this.element.querySelector(ImageChoice.selectors.errorDiv); + } + + getQuestionMarkDiv() { + return this.element.querySelector(ImageChoice.selectors.qm); + } + + getTooltipDiv() { + return this.element.querySelector(ImageChoice.selectors.tooltipDiv); + } + + getOptions() { + return this.element.querySelectorAll(ImageChoice.selectors.option); + } + + setModel(model) { + super.setModel(model); + let widgets = this.widget; + widgets.forEach(widget => { + let self = widget; + this.#updateModelValue(self); + widget.addEventListener('change', (e) => { + this.#updateModelValue(self); + }); + widget.addEventListener('focus', (e) => { + this.setActive(); + }); + widget.addEventListener('blur', (e) => { + this.setInactive(); + }); + }) + } + + #updateModelValue(widget) { + let value = []; + this.widget.forEach(widget => { + if (widget.checked) { + value.push(widget.value) + } + }, this); + if (value.length !== 0 || this._model.value != null) { + this._model.value = value; + } + } + + updateValidity(validity) { + const valid = validity.valid ? validity.valid : false; + let widgets = this.widget; + this.element.setAttribute(FormView.Constants.DATA_ATTRIBUTE_VALID, valid); + widgets.forEach(widget => widget.setAttribute(FormView.Constants.ARIA_INVALID, !valid)); + } + + updateValue(modelValue) { + modelValue = [].concat(modelValue); + let selectedWidgetValues = modelValue.map(String); + this.widget.forEach(widget => { + if (selectedWidgetValues.includes((widget.value))) { + widget.checked = true + widget.setAttribute(FormView.Constants.HTML_ATTRS.CHECKED, FormView.Constants.HTML_ATTRS.CHECKED) + widget.setAttribute(FormView.Constants.ARIA_CHECKED, true) + } else { + widget.checked = false + widget.removeAttribute(FormView.Constants.HTML_ATTRS.CHECKED); + widget.setAttribute(FormView.Constants.ARIA_CHECKED, false); + } + }, this) + super.updateEmptyStatus(); + } + + updateEnabled(enabled, state) { + this.element.setAttribute(FormView.Constants.DATA_ATTRIBUTE_ENABLED, enabled); + let widgets = this.widget; + widgets.forEach(widget => { + if (enabled === false) { + if(state.readOnly === false){ + widget.setAttribute(FormView.Constants.HTML_ATTRS.DISABLED, "disabled"); + widget.setAttribute(FormView.Constants.ARIA_DISABLED, true); + } + } else if (state.readOnly === false) { + widget.removeAttribute(FormView.Constants.HTML_ATTRS.DISABLED); + widget.removeAttribute(FormView.Constants.ARIA_DISABLED); + } + }); + } + + updateRequired(required, state) { + if (this.widget) { + this.element.toggleAttribute("required", required); + this.element.setAttribute("data-cmp-required", required); + } + } + + updateReadOnly(readonly) { + let widgets = this.widget; + this.element.setAttribute(FormView.Constants.DATA_ATTRIBUTE_READONLY, readonly); + widgets.forEach(widget => { + if (readonly === true) { + widget.setAttribute(FormView.Constants.HTML_ATTRS.DISABLED, "disabled"); + widget.setAttribute("aria-readonly", true); + } else { + widget.removeAttribute(FormView.Constants.HTML_ATTRS.DISABLED); + widget.removeAttribute("aria-readonly"); + } + }); + } + } + + FormView.Utils.setupField(({element, formContainer}) => { + return new ImageChoice({element, formContainer}) + }, ImageChoice.selectors.self); + +})(); \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/imagechoice.html b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/imagechoice.html new file mode 100644 index 0000000000..1724172c79 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/imagechoice.html @@ -0,0 +1,65 @@ + + + + +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/imagechoice.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/imagechoice.js new file mode 100644 index 0000000000..1a2acce6ea --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/imagechoice/v1/imagechoice/imagechoice.js @@ -0,0 +1,33 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2024 Adobe + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +use(function () { + + var clientlibsArr = ['core.forms.components.base.v1.editor']; + var labelPath = 'core/fd/components/af-commons/v1/fieldTemplates/label.html'; + var shortDescriptionPath = "core/fd/components/af-commons/v1/fieldTemplates/shortDescription.html"; + var longDescriptionPath = "core/fd/components/af-commons/v1/fieldTemplates/longDescription.html"; + var questionMarkPath = "core/fd/components/af-commons/v1/fieldTemplates/questionMark.html" + var errorMessagePath = "core/fd/components/af-commons/v1/fieldTemplates/errorMessage.html"; + return { + labelPath: labelPath, + shortDescriptionPath: shortDescriptionPath, + longDescriptionPath: longDescriptionPath, + questionMarkPath: questionMarkPath, + errorMessagePath: errorMessagePath, + clientlibs: clientlibsArr + } +}); \ No newline at end of file diff --git a/ui.tests/test-module/libs/commons/formsConstants.js b/ui.tests/test-module/libs/commons/formsConstants.js index 0f59444f59..4b58d6f67f 100644 --- a/ui.tests/test-module/libs/commons/formsConstants.js +++ b/ui.tests/test-module/libs/commons/formsConstants.js @@ -39,6 +39,7 @@ var formsConstants = { "formdropdown": "/apps/forms-components-examples/components/form/dropdown", "formbutton": "/apps/forms-components-examples/components/form/button", "formimage": "/apps/forms-components-examples/components/form/image", + "formimagechoice": "/apps/forms-components-examples/components/form/imagechoice", "formradiobutton": "/apps/forms-components-examples/components/form/radiobutton", "formfileinput": "/apps/forms-components-examples/components/form/fileinput", "wizard": "/apps/forms-components-examples/components/form/wizard", diff --git a/ui.tests/test-module/specs/imagechoice/imagechoice.authoring.spec.js b/ui.tests/test-module/specs/imagechoice/imagechoice.authoring.spec.js new file mode 100644 index 0000000000..6290f2484f --- /dev/null +++ b/ui.tests/test-module/specs/imagechoice/imagechoice.authoring.spec.js @@ -0,0 +1,228 @@ +/* + * Copyright 2024 Adobe Systems Incorporated + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const sitesSelectors = require('../../libs/commons/sitesSelectors'), +afConstants = require('../../libs/commons/formsConstants'); + +/** + * Testing ImageChoice with Sites Editor + */ +describe('Page - Authoring', function () { + // we can use these values to log in + + const dropImageChoiceInContainer = function () { + const dataPath = "/content/forms/af/core-components-it/blank/jcr:content/guideContainer/*", + responsiveGridDropZoneSelector = sitesSelectors.overlays.overlay.component + "[data-path='" + dataPath + "']"; + cy.selectLayer("Edit"); + cy.insertComponent(responsiveGridDropZoneSelector, "Adaptive Form Image Choice", afConstants.components.forms.resourceType.formimagechoice); + cy.get('body').click(0, 0); + } + + const dropTextInputInSites = function () { + const dataPath = "/content/core-components-examples/library/adaptive-form/textinput/jcr:content/root/responsivegrid/demo/component/guideContainer/*", + responsiveGridDropZoneSelector = sitesSelectors.overlays.overlay.component + "[data-path='" + dataPath + "']"; + cy.selectLayer("Edit"); + cy.insertComponent(responsiveGridDropZoneSelector, "Adaptive Form Image Choice", afConstants.components.forms.resourceType.formtextinput); + cy.get('body').click(0, 0); + } + + const getPreviewIframeBody = () => { + // get the iframe > document > body + // and retry until the body element is not empty + return cy + .get('iframe#ContentFrame') + .its('0.contentDocument.body').should('not.be.empty') + .then(cy.wrap) + } + + const testImageChoiceBehaviour = function (imageChoiceEditPathSelector, imageChoiceDrop, isSites) { + if (isSites) { + dropTextInputInSites(); + } else { + dropImageChoiceInContainer(); + } + cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + imageChoiceEditPathSelector); + cy.invokeEditableAction("[data-action='CONFIGURE']"); // this line is causing frame busting which is causing cypress to fail + // Check If Dialog Options Are Visible + cy.get("[name='./type@Delete']") + .should("exist"); + + // Checking some dynamic behaviours + cy.get("[name='./required'][type=\"checkbox\"]").should("exist"); + cy.get("[data-granite-coral-multifield-name='./options'] coral-button-label:contains('Add')").should("exist"); + + // selecting number as type of the checkbox + cy.get('.cmp-adaptiveform-imagechoice__type').should("exist").click(); + cy.get('coral-selectlist-item[value="number[]"]').should("exist").click(); + + // selecting vertical alignment as an orientation + cy.get('input[name="./orientation"][value="vertical"]').should("exist").click(); + + // selecting multiple as type of the selectionType dropdown + cy.get('.cmp-adaptiveform-imagechoice__selectionType').should("exist").click(); + cy.get('coral-selectlist-item[value="multiple"]').should("exist").click(); + + // saving the dialog with changes + cy.get('.cq-dialog-submit').click(); + + // verifying alignment change in preview editor + getPreviewIframeBody().find('.cmp-adaptiveform-imagechoice__widget.VERTICAL').should('have.length', 1); + getPreviewIframeBody().find('.cmp-adaptiveform-imagechoice__option').should('have.length', 0); + + cy.deleteComponentByPath(imageChoiceDrop); + } + + context('Open Forms Editor', function () { + const pagePath = "/content/forms/af/core-components-it/blank", + imageChoicePath = pagePath + afConstants.FORM_EDITOR_FORM_CONTAINER_SUFFIX + "/imagechoice", + imageChoiceEditPathSelector = "[data-path='" + imageChoicePath + "']", + imageChoiceDrop = pagePath + afConstants.FORM_EDITOR_FORM_CONTAINER_SUFFIX + "/" + afConstants.components.forms.resourceType.formimagechoice.split("/").pop(); + beforeEach(function () { + // this is done since cypress session results in 403 sometimes + cy.openAuthoring(pagePath); + }); + + it('insert ImageChoice in form container', function () { + dropImageChoiceInContainer(); + cy.deleteComponentByPath(imageChoiceDrop); + }); + + it('open edit dialog of ImageChoice', { + retries: 3 + }, function () { + cy.cleanTest(imageChoiceDrop).then(function () { + testImageChoiceBehaviour(imageChoiceEditPathSelector, imageChoiceDrop); + }); + }); + + it('check value type validations', function () { + // For Number Type + dropImageChoiceInContainer(); + cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + imageChoiceEditPathSelector); + cy.invokeEditableAction("[data-action='CONFIGURE']"); + cy.get('.cmp-adaptiveform-imagechoice__type').click(); + cy.get("coral-selectlist-item-content").contains('Number').should('be.visible').click({ + force: true + }); + + cy.get('.cmp-adaptiveform-imagechoice__value button').click(); + cy.get(".cmp-adaptiveform-imagechoice__value input").invoke('val', 'Not a Number'); + cy.get('.cq-dialog-submit').click(); + cy.get('._coral-Tooltip-label').should('contain.text', 'Value Type Mismatch'); + + cy.get('.cq-dialog-cancel').click(); + cy.deleteComponentByPath(imageChoiceDrop); + + // For Boolean + dropImageChoiceInContainer(); + cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + imageChoiceEditPathSelector); + cy.invokeEditableAction("[data-action='CONFIGURE']"); + cy.get('.cmp-adaptiveform-imagechoice__type').click(); + cy.get("coral-selectlist-item-content").contains('Boolean').should('be.visible').click({ + force: true + }); + + cy.get('.cmp-adaptiveform-imagechoice__value button').click(); + cy.get(".cmp-adaptiveform-imagechoice__value input").invoke('val', 'Not a Boolean'); + cy.get('.cq-dialog-submit').click(); + cy.get('._coral-Tooltip-label').should('contain.text', 'Value Type Mismatch'); + + cy.get('.cq-dialog-cancel').click(); + cy.deleteComponentByPath(imageChoiceDrop); + }) + + it('add options to image choice - single select', function () { + dropImageChoiceInContainer(); + cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + imageChoiceEditPathSelector); + cy.invokeEditableAction("[data-action='CONFIGURE']"); + cy.get("[data-granite-coral-multifield-name='./options'] coral-button-label:contains('Add')").should("exist").click({ + force: true + }); + + cy.get('input[name*="options/item"][name*="/imageValue"]').last().invoke('val', '1'); + cy.get('input[name*="options/item"][name*="/altText"]').last().invoke('val', 'corecompoLogo'); + cy.get('foundation-autocomplete[name="./options/item0/./imageSrc"]') + .find('input[type="hidden"][name="./options/item0/./imageSrc"]') + .invoke('val', '/content/dam/core-components-examples/library/aem-corecomponents-logo.svg', { + force: true + }) + .trigger('change', { + force: true + }); + cy.get("[data-granite-coral-multifield-name='./options'] coral-button-label:contains('Add')").should("exist").click({ + force: true + }); + cy.get('input[name*="options/item"][name*="/imageValue"]').last().invoke('val', '2'); + cy.get('input[name*="options/item"][name*="/altText"]').last().invoke('val', 'corecomponentLogo'); + cy.get('foundation-autocomplete[name="./options/item1/./imageSrc"]') + .find('input[type="hidden"][name="./options/item1/./imageSrc"]') + .invoke('val', '/content/dam/core-components-examples/library/adobe-logo.svg', { + force: true + }) + .trigger('change', { + force: true + }); + cy.get('.cq-dialog-submit').click().then(() => { + cy.get('.cq-dialog-submit').should('not.exist') + }); + //getPreviewIframeBody().find('.cmp-adaptiveform-imagechoice__option').should('have.length', 0); + cy.deleteComponentByPath(imageChoiceDrop); + }); + + it('add options to image choice - multiple select', function () { + dropImageChoiceInContainer(); + cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + imageChoiceEditPathSelector); + cy.invokeEditableAction("[data-action='CONFIGURE']"); + + // selecting multiple as type of the selectionType dropdown + cy.get('.cmp-adaptiveform-imagechoice__selectionType').should("exist").click(); + cy.get('coral-selectlist-item[value="multiple"]').should("exist").click(); + + cy.get("[data-granite-coral-multifield-name='./options'] coral-button-label:contains('Add')").should("exist").click({ + force: true + }); + + cy.get('input[name*="options/item"][name*="/imageValue"]').last().invoke('val', '1'); + cy.get('input[name*="options/item"][name*="/altText"]').last().invoke('val', 'corecomponentLogo'); + cy.get('foundation-autocomplete[name="./options/item0/./imageSrc"]') + .find('input[type="hidden"][name="./options/item0/./imageSrc"]') + .invoke('val', '/content/dam/core-components-examples/library/aem-corecomponents-logo.svg', { + force: true + }) + .trigger('change', { + force: true + }); + cy.get("[data-granite-coral-multifield-name='./options'] coral-button-label:contains('Add')").should("exist").click({ + force: true + }); + cy.get('input[name*="options/item"][name*="/imageValue"]').last().invoke('val', '2'); + cy.get('input[name*="options/item"][name*="/altText"]').last().invoke('val', 'adobeLogo'); + cy.get('foundation-autocomplete[name="./options/item1/./imageSrc"]') + .find('input[type="hidden"][name="./options/item1/./imageSrc"]') + .invoke('val', '/content/dam/core-components-examples/library/adobe-logo.svg', { + force: true + }) + .trigger('change', { + force: true + }); + cy.get('.cq-dialog-submit').click().then(() => { + cy.get('.cq-dialog-submit').should('not.exist') + }); + cy.deleteComponentByPath(imageChoiceDrop); + }); + + }) +}); diff --git a/ui.tests/test-module/specs/imagechoice/imagechoice.runtime.spec.js b/ui.tests/test-module/specs/imagechoice/imagechoice.runtime.spec.js new file mode 100644 index 0000000000..64175f6260 --- /dev/null +++ b/ui.tests/test-module/specs/imagechoice/imagechoice.runtime.spec.js @@ -0,0 +1,258 @@ +/******************************************************************************* + * Copyright 2024 Adobe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +describe("Form Runtime with ImageChoice Input", () => { + + const pagePath = "content/forms/af/core-components-it/samples/imagechoice/basic.html" + const bemBlock = 'cmp-adaptiveform-imagechoice' + const IS = "adaptiveFormImageChoice" + const selectors = { + imagechoice: `[data-cmp-is="${IS}"]` + } + + let formContainer = null + + beforeEach(() => { + cy.previewForm(pagePath).then(p => { + formContainer = p; + }) + }); + + const checkHTML = (id, state) => { + const visible = state.visible; + const passVisibleCheck = `${visible === true ? "" : "not."}be.visible`; + const passDisabledAttributeCheck = `${state.enabled === false || state.readOnly === true ? "" : "not."}have.attr`; + const value = state.value + cy.get(`#${id}`) + .should(passVisibleCheck) + .invoke('attr', 'data-cmp-visible') + .should('eq', visible.toString()); + cy.get(`#${id}`) + .invoke('attr', 'data-cmp-enabled') + .should('eq', state.enabled.toString()); + return cy.get(`#${id}`).within((root) => { + cy.get('*').should(passVisibleCheck) + cy.get('input') + .should(passDisabledAttributeCheck, 'disabled'); + }) + } + + it(" should get model and view initialized properly ", () => { + expect(formContainer, "formcontainer is initialized").to.not.be.null; + expect(formContainer._model.items.length, "model and view elements match").to.equal(Object.keys(formContainer._fields).length); + Object.entries(formContainer._fields).forEach(([id, field]) => { + expect(field.getId()).to.equal(id) + expect(formContainer._model.getElement(id), `model and view are in sync`).to.equal(field.getModel()) + }); + }) + + it(" model's changes are reflected in the html ", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[0] + const model = formContainer._model.getElement(id) + model.value = '1' + cy.get(`#${id}`).find(".cmp-adaptiveform-imagechoice__widget").should('have.class', 'HORIZONTAL') + const [id2, fieldView2] = Object.entries(formContainer._fields)[1] + cy.get(`#${id2}`).find(".cmp-adaptiveform-imagechoice__widget").should('have.class', 'VERTICAL') + const [id3, fieldView3] = Object.entries(formContainer._fields)[6] + cy.get(`#${id3}`).find(".cmp-adaptiveform-imagechoice__widget").should('have.class', 'HORIZONTAL') + checkHTML(model.id, model.getState()).then(() => { + model.visible = false + return checkHTML(model.id, model.getState()) + }).then(() => { + model.enable = false + return checkHTML(model.id, model.getState()) + }) + }); + + it(" html changes are reflected in model ", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[0] + const model = formContainer._model.getElement(id) + cy.log(model.getState().value) + cy.get(`#${id}`).find("input").eq(1).click().then(x => { + cy.log(model.getState().value) + expect(model.getState().value).to.contain('2'); + }) + const [id2, fieldView2] = Object.entries(formContainer._fields)[1] + cy.get(`#${id2}`).find("input").eq(1).click().then(x => { + cy.log(model.getState().value) + expect(model.getState().value).to.contain('2'); + }) + const [id3, fieldView3] = Object.entries(formContainer._fields)[7] + cy.get(`#${id3}`).find("input").eq(1).click().then(x => { + cy.log(model.getState().value) + expect(model.getState().value).to.contain('2'); + }) + }); + + it(" should show error messages in the HTML ", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[1] + const model = formContainer._model.getElement(id) + + cy.get(`#${id}`).find("input").eq(1).click().then(x => { + cy.log(model.getState().value) + expect(model.getState().value).to.contain('2'); + }) + + cy.get(`#${id}`).find("input").eq(1).click().then(x => { + cy.get(`#${id}`).find(".cmp-adaptiveform-imagechoice__errormessage").should('have.text', "This is a custom required imagechoice") + }) + + cy.get(`#${id}`).find("input").eq(1).click().then(x => { + cy.get(`#${id}`).find(".cmp-adaptiveform-imagechoice__errormessage").should('have.text', "") + }) + }); + + it("should toggle description and tooltip", () => { + cy.toggleDescriptionTooltip(bemBlock, 'tooltip_scenario_test'); + }) + + it("should show and hide components on certain imagechoice input", () => { + // Rule on imagechoice2: When imagechoice2 has 1 AND 2 selected => Show imagechoice3 and Hide imagechoice4 + + const [imageChoice2, imageChoice2FieldView] = Object.entries(formContainer._fields)[1]; + const [imageChoice3, imageChoice3FieldView] = Object.entries(formContainer._fields)[2]; + const [imageChoice4, imageChoice4FieldView] = Object.entries(formContainer._fields)[3]; + + cy.get(`#${imageChoice2}`).find("input").check(["1", "2"]).then(x => { + cy.get(`#${imageChoice3}`).should('be.visible') + cy.get(`#${imageChoice4}`).should('not.be.visible') + }) + + // Rule on imagechoice8: When imagechoice8 has 1 selected => Show imagechoice9 and Hide imagechoice10 + + const [imageChoice8, imageChoice8FieldView] = Object.entries(formContainer._fields)[7]; + const [imageChoice9, imageChoice9FieldView] = Object.entries(formContainer._fields)[8]; + const [imageChoice10, imageChoice10FieldView] = Object.entries(formContainer._fields)[9]; + + cy.get(`#${imageChoice8}`).find("input").check(["1"]).then(x => { + cy.get(`#${imageChoice9}`).should('be.visible') + cy.get(`#${imageChoice10}`).should('not.be.visible') + }) + + }) + + it("should enable and disable components on certain imagechoice input", () => { + // Rule on imageChoice2: When imageChoice2 has 1 AND 2 selected => Enable imageChoice5 and Disable imageChoice6 + + const [imageChoice2, imageChoice2FieldView] = Object.entries(formContainer._fields)[1]; + const [imageChoice5, imageChoice5FieldView] = Object.entries(formContainer._fields)[4]; + const [imageChoice6, imageChoice6FieldView] = Object.entries(formContainer._fields)[5]; + + cy.get(`#${imageChoice2}`).find("input").check(["1", "2"]).then(x => { + cy.get(`#${imageChoice5}`).find("input").should('be.enabled') + cy.get(`#${imageChoice6}`).find("input").should('not.be.enabled') + }) + + // Rule on imageChoice8: When imageChoice8 has 1 selected => Enable imageChoice11 and Disable imageChoice12 + + const [imageChoice8, imageChoice8FieldView] = Object.entries(formContainer._fields)[7]; + const [imageChoice11, imageChoice11FieldView] = Object.entries(formContainer._fields)[10]; + const [imageChoice12, imageChoice12FieldView] = Object.entries(formContainer._fields)[11]; + + cy.get(`#${imageChoice8}`).find("input").check(["1"]).then(x => { + cy.get(`#${imageChoice11}`).find("input").should('be.enabled') + cy.get(`#${imageChoice12}`).find("input").should('not.be.enabled') + }) + }) + + it("should show validation error messages based on expression rules", () => { + // Rule on imageChoice5: Validate checkBox using Expression: imageChoice3 === imageChoice5 + const [imageChoice2, imageChoice2FieldView] = Object.entries(formContainer._fields)[1]; + const [imageChoice3, imageChoice3FieldView] = Object.entries(formContainer._fields)[2]; + const [imageChoice5, imageChoice5FieldView] = Object.entries(formContainer._fields)[4]; + // Making imageChoice3 visible + cy.get(`#${imageChoice2}`).find("input").check(["1", "2"]) + cy.get(`#${imageChoice3}`).find("input").uncheck().check(["1"]).blur().then(x => { + cy.get(`#${imageChoice5}`).find("input").uncheck().check(["2"]) + cy.get(`#${imageChoice5}`).find(".cmp-adaptiveform-imagechoice__errormessage").should('have.text', "Please enter a valid value.") + }) + + cy.get(`#${imageChoice3}`).find("input").uncheck().check(["1"]).blur().then(x => { + cy.get(`#${imageChoice5}`).find("input").uncheck().check(["1"]) + cy.get(`#${imageChoice5}`).find(".cmp-adaptiveform-imagechoice__errormessage").should('have.text', "") + }) + }) + + it("should set and clear value based on rules", () => { + // Rule on imageChoice3: When input has 1 selected, set value of imageChoice5 to 1 and clear value of imageChoice2 + + const [imageChoice2, imageChoice2FieldView] = Object.entries(formContainer._fields)[1]; + const [imageChoice3, imageChoice3FieldView] = Object.entries(formContainer._fields)[2]; + const [imageChoice5, imageChoice5FieldView] = Object.entries(formContainer._fields)[4]; + + // Make imageChoice3 visible + cy.get(`#${imageChoice2}`).find("input").check(["1", "2"]) + cy.get(`#${imageChoice3}`).find("input").check("1").blur().then(x => { + cy.get(`#${imageChoice5}`).find("input").should('be.checked') + cy.get(`#${imageChoice2}`).find("input").should('not.be.checked') + }) + + // Rule on imageChoice9: When input has 1 selected, set value of imageChoice11 to 1 and clear value of imageChoice8 + + const [imageChoice8, imageChoice8FieldView] = Object.entries(formContainer._fields)[7]; + const [imageChoice9, imageChoice9FieldView] = Object.entries(formContainer._fields)[8]; + const [imageChoice11, imageChoice11FieldView] = Object.entries(formContainer._fields)[10]; + + // Make imageChoice8 visible + cy.get(`#${imageChoice8}`).find("input").check(["1", "2"]) + cy.get(`#${imageChoice9}`).find("input").check("1").blur().then(x => { + cy.get(`#${imageChoice11}`).find("input").should('be.checked') + cy.get(`#${imageChoice8}`).find("input").should('not.be.checked') + }) + }) + + it("decoration element should not have same class name", () => { + expect(formContainer, "formcontainer is initialized").to.not.be.null; + cy.wrap().then(() => { + const id = formContainer._model._children[0].id; + cy.get(`#${id}`).parent().should("not.have.class", bemBlock); + }) + + }) + + it(" should add filled/empty class at container div ", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[2] + cy.get(`#${id}`).should('have.class', 'cmp-adaptiveform-imagechoice--empty'); + cy.get(`#${id}`).invoke('attr', 'data-cmp-required').should('eq', 'false'); + cy.get(`#${id}`).invoke('attr', 'data-cmp-readonly').should('eq', 'false'); + const [imageChoice2, imageChoice2FieldView] = Object.entries(formContainer._fields)[1]; + cy.get(`#${imageChoice2}`).find("input").check(["1", "2"]) + cy.get(`#${id}`).find("input").eq(1).click().then(x => { + cy.get(`#${id}`).should('have.class', 'cmp-adaptiveform-imagechoice--filled'); + }); + }); + +}) + +describe("setFocus on imagechoice via rules", () => { + + const pagePath = "content/forms/af/core-components-it/samples/imagechoice/focustest.html" + let formContainer = null + + beforeEach(() => { + cy.previewForm(pagePath).then(p => { + formContainer = p; + }) + }); + + it("should focus on image choice when button is clicked", () => { + const [button] = Object.entries(formContainer._fields).filter(it => it[1].getModel()._jsonModel.fieldType === 'button')[0]; + const [radioButton] = Object.entries(formContainer._fields).filter(it => it[1].getModel()._jsonModel.fieldType === 'imagechoice')[0]; + cy.get(`#${radioButton}`).find("input").eq(0).should('not.have.focus'); + cy.get(`#${button}-widget`).click().then(() => { + cy.get(`#${radioButton}`).find("input").eq(0).should('have.focus') + }) + }) +})