diff --git a/.circleci/ci/accessibilityConfig.json b/.circleci/ci/accessibilityConfig.json index f89990d697..3aa96f3c30 100644 --- a/.circleci/ci/accessibilityConfig.json +++ b/.circleci/ci/accessibilityConfig.json @@ -1,3 +1,3 @@ { - "accessibilityExceptionList": ["landmark-one-main", "label-title-only", "region", "focus-order-semantics", "target-size"] + "accessibilityExceptionList": ["landmark-one-main", "label-title-only", "region", "focus-order-semantics", "target-size", "page-has-heading-one"] } diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/util/AbstractBaseImpl.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/util/AbstractBaseImpl.java index 9c7ab9f2ea..79f8f78ba2 100644 --- a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/util/AbstractBaseImpl.java +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/util/AbstractBaseImpl.java @@ -182,12 +182,12 @@ public String getScreenReaderText() { if (AssistPriority.LABEL.equals(assistPriority)) { Label label = getLabel(); if (label != null) { - screenReaderText = "$label.$value"; + screenReaderText = label.getValue(); } } else if (AssistPriority.NAME.equals(assistPriority)) { - screenReaderText = "$name"; + screenReaderText = getName(); } else if (AssistPriority.DESCRIPTION.equals(assistPriority)) { - screenReaderText = "$description"; + screenReaderText = getDescription(); } else if (AssistPriority.CUSTOM.equals(assistPriority)) { screenReaderText = "'" + customAssistPriorityMsg + "'"; // json formula string literal } diff --git a/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/RadioButtonImplTest.java b/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/RadioButtonImplTest.java index 0e62af6be2..7506d2deea 100644 --- a/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/RadioButtonImplTest.java +++ b/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/RadioButtonImplTest.java @@ -45,6 +45,9 @@ public class RadioButtonImplTest { private static final String BASE = "/form/radiobutton"; private static final String CONTENT_ROOT = "/content"; private static final String PATH_RADIOBUTTON_CUSTOMIZED = CONTENT_ROOT + "/radiobutton-customized"; + private static final String PATH_RADIOBUTTON_CUSTOMIZED_WITH_LABEL = CONTENT_ROOT + "/radiobutton-customized-withLabel"; + private static final String PATH_RADIOBUTTON_CUSTOMIZED_WITH_NAME = CONTENT_ROOT + "/radiobutton-customized-withName"; + private static final String PATH_RADIOBUTTON_CUSTOMIZED_WITH_DESC = CONTENT_ROOT + "/radiobutton-customized-withDescription"; private static final String PATH_RADIOBUTTON = CONTENT_ROOT + "/radiobutton"; private static final String PATH_RADIOBUTTON_DATALAYER = CONTENT_ROOT + "/radiobutton-datalayer"; @@ -390,4 +393,31 @@ void testInsertionOrderForEnumNames() { "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen", "Twenty")); assertArrayEquals(set.toArray(new String[0]), radioButton.getEnumNames()); } + + @Test + void testGetScreenReaderTextWithLabel() { + RadioButton radioButton = getRadioButtonUnderTest(PATH_RADIOBUTTON_CUSTOMIZED_WITH_LABEL); + assertEquals("Radio Button", radioButton.getScreenReaderText()); + RadioButton radioButtonMock = Mockito.mock(RadioButton.class); + Mockito.when(radioButtonMock.getScreenReaderText()).thenCallRealMethod(); + assertEquals(null, radioButtonMock.getScreenReaderText()); + } + + @Test + void testGetScreenReaderTextWithName() { + RadioButton radioButton = getRadioButtonUnderTest(PATH_RADIOBUTTON_CUSTOMIZED_WITH_NAME); + assertEquals("radiobutton_12345", radioButton.getScreenReaderText()); + RadioButton radioButtonMock = Mockito.mock(RadioButton.class); + Mockito.when(radioButtonMock.getScreenReaderText()).thenCallRealMethod(); + assertEquals(null, radioButtonMock.getScreenReaderText()); + } + + @Test + void testGetScreenReaderTextWithDescription() { + RadioButton radioButton = getRadioButtonUnderTest(PATH_RADIOBUTTON_CUSTOMIZED_WITH_DESC); + assertEquals("long description", radioButton.getScreenReaderText()); + RadioButton radioButtonMock = Mockito.mock(RadioButton.class); + Mockito.when(radioButtonMock.getScreenReaderText()).thenCallRealMethod(); + assertEquals(null, radioButtonMock.getScreenReaderText()); + } } diff --git a/bundles/af-core/src/test/resources/form/radiobutton/exporter-radiobutton-customized-withDescription.json b/bundles/af-core/src/test/resources/form/radiobutton/exporter-radiobutton-customized-withDescription.json new file mode 100644 index 0000000000..f1a904eaf5 --- /dev/null +++ b/bundles/af-core/src/test/resources/form/radiobutton/exporter-radiobutton-customized-withDescription.json @@ -0,0 +1,48 @@ +{ + "id": "radiobutton-3b6e1ca0f6", + "dataRef": "a.b", + "fieldType": "radio-group", + "name": "radiobutton_12345", + "visible": false, + "description": "long description", + "tooltip": "

short description

", + "type": "string", + "required": true, + "enabled": true, + "readOnly": true, + "constraintMessages": { + "required": "This is a required field", + "validationExpression": "Required Expression" + }, + "enforceEnum": true, + "enumNames": [ + "

Item 1

", + "

Item 2

" + ], + "default": "0", + "screenReaderText": "long description", + "label": { + "value": "Radio Button" + }, + "events": { + "custom:setProperty": [ + "$event.payload" + ] + }, + "properties": { + "afs:layout": { + "tooltipVisible": true, + "orientation": "vertical" + }, + "fd:dor": { + "dorExclusion": true, + "dorColspan": "4" + }, + "fd:path": "/content/radiobutton-customized" + }, + "enum": [ + "0", + "1" + ], + ":type": "core/fd/components/form/radiobutton/v1/radiobutton" + } \ No newline at end of file diff --git a/bundles/af-core/src/test/resources/form/radiobutton/exporter-radiobutton-customized-withLabel.json b/bundles/af-core/src/test/resources/form/radiobutton/exporter-radiobutton-customized-withLabel.json new file mode 100644 index 0000000000..4f4b74f461 --- /dev/null +++ b/bundles/af-core/src/test/resources/form/radiobutton/exporter-radiobutton-customized-withLabel.json @@ -0,0 +1,48 @@ +{ + "id": "radiobutton-3b6e1ca0f6", + "dataRef": "a.b", + "fieldType": "radio-group", + "name": "radiobutton_12345", + "visible": false, + "description": "long description", + "tooltip": "

short description

", + "type": "string", + "required": true, + "enabled": true, + "readOnly": true, + "constraintMessages": { + "required": "This is a required field", + "validationExpression": "Required Expression" + }, + "enforceEnum": true, + "enumNames": [ + "

Item 1

", + "

Item 2

" + ], + "default": "0", + "screenReaderText": "'Radio Button'", + "label": { + "value": "Radio Button" + }, + "events": { + "custom:setProperty": [ + "$event.payload" + ] + }, + "properties": { + "afs:layout": { + "tooltipVisible": true, + "orientation": "vertical" + }, + "fd:dor": { + "dorExclusion": true, + "dorColspan": "4" + }, + "fd:path": "/content/radiobutton-customized" + }, + "enum": [ + "0", + "1" + ], + ":type": "core/fd/components/form/radiobutton/v1/radiobutton" + } \ No newline at end of file diff --git a/bundles/af-core/src/test/resources/form/radiobutton/exporter-radiobutton-customized-withName.json b/bundles/af-core/src/test/resources/form/radiobutton/exporter-radiobutton-customized-withName.json new file mode 100644 index 0000000000..f40f680134 --- /dev/null +++ b/bundles/af-core/src/test/resources/form/radiobutton/exporter-radiobutton-customized-withName.json @@ -0,0 +1,48 @@ +{ + "id": "radiobutton-3b6e1ca0f6", + "dataRef": "a.b", + "fieldType": "radio-group", + "name": "radiobutton_12345", + "visible": false, + "description": "long description", + "tooltip": "

short description

", + "type": "string", + "required": true, + "enabled": true, + "readOnly": true, + "constraintMessages": { + "required": "This is a required field", + "validationExpression": "Required Expression" + }, + "enforceEnum": true, + "enumNames": [ + "

Item 1

", + "

Item 2

" + ], + "default": "0", + "screenReaderText": "radiobutton_12345", + "label": { + "value": "Radio Button" + }, + "events": { + "custom:setProperty": [ + "$event.payload" + ] + }, + "properties": { + "afs:layout": { + "tooltipVisible": true, + "orientation": "vertical" + }, + "fd:dor": { + "dorExclusion": true, + "dorColspan": "4" + }, + "fd:path": "/content/radiobutton-customized" + }, + "enum": [ + "0", + "1" + ], + ":type": "core/fd/components/form/radiobutton/v1/radiobutton" + } \ No newline at end of file diff --git a/bundles/af-core/src/test/resources/form/radiobutton/test-content.json b/bundles/af-core/src/test/resources/form/radiobutton/test-content.json index 08fc21151c..0c8a147bde 100644 --- a/bundles/af-core/src/test/resources/form/radiobutton/test-content.json +++ b/bundles/af-core/src/test/resources/form/radiobutton/test-content.json @@ -211,5 +211,128 @@ "20" ], ":type": "forms-components-examples/components/form/radiobutton" + }, + "radiobutton-customized-withLabel": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "core/fd/components/form/radiobutton/v1/radiobutton", + "name": "radiobutton_12345", + "jcr:title": "Radio Button", + "jcr:createdBy": "admin", + "description": "long description", + "enabled": true, + "orientation": "vertical", + "dataRef": "a.b", + "custom": "custom text", + "jcr:lastModifiedBy": "admin", + "tooltip": "

short description

", + "required": "true", + "dorExclusion": true, + "dorColspan": "4", + "assistPriority": "label", + "jcr:created": "Tue Sep 13 2022 15:17:35 GMT+0530", + "areOptionsRichText": true, + "enum": [ + "0", + "1" + ], + "type": "string", + "enumNames": [ + "

Item 1

", + "

Item 2

" + ], + "visible": false, + "readOnly": true, + "jcr:lastModified": "Tue Sep 13 2022 15:18:50 GMT+0530", + "fieldType": "radio-group", + "mandatoryMessage": "This is a required field", + "default": "0", + "tooltipVisible": "true", + "textIsRich": [ + "true", + "true" + ], + "validateExpMessage": "Required Expression" + }, + "radiobutton-customized-withName": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "core/fd/components/form/radiobutton/v1/radiobutton", + "name": "radiobutton_12345", + "jcr:title": "Radio Button", + "jcr:createdBy": "admin", + "description": "long description", + "enabled": true, + "orientation": "vertical", + "dataRef": "a.b", + "custom": "custom text", + "jcr:lastModifiedBy": "admin", + "tooltip": "

short description

", + "required": "true", + "dorExclusion": true, + "dorColspan": "4", + "assistPriority": "name", + "jcr:created": "Tue Sep 13 2022 15:17:35 GMT+0530", + "areOptionsRichText": true, + "enum": [ + "0", + "1" + ], + "type": "string", + "enumNames": [ + "

Item 1

", + "

Item 2

" + ], + "visible": false, + "readOnly": true, + "jcr:lastModified": "Tue Sep 13 2022 15:18:50 GMT+0530", + "fieldType": "radio-group", + "mandatoryMessage": "This is a required field", + "default": "0", + "tooltipVisible": "true", + "textIsRich": [ + "true", + "true" + ], + "validateExpMessage": "Required Expression" + }, + "radiobutton-customized-withDescription": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "core/fd/components/form/radiobutton/v1/radiobutton", + "name": "radiobutton_12345", + "jcr:title": "Radio Button", + "jcr:createdBy": "admin", + "description": "long description", + "enabled": true, + "orientation": "vertical", + "dataRef": "a.b", + "custom": "custom text", + "jcr:lastModifiedBy": "admin", + "tooltip": "

short description

", + "required": "true", + "dorExclusion": true, + "dorColspan": "4", + "assistPriority": "description", + "jcr:created": "Tue Sep 13 2022 15:17:35 GMT+0530", + "areOptionsRichText": true, + "enum": [ + "0", + "1" + ], + "type": "string", + "enumNames": [ + "

Item 1

", + "

Item 2

" + ], + "visible": false, + "readOnly": true, + "jcr:lastModified": "Tue Sep 13 2022 15:18:50 GMT+0530", + "fieldType": "radio-group", + "mandatoryMessage": "This is a required field", + "default": "0", + "tooltipVisible": "true", + "textIsRich": [ + "true", + "true" + ], + "validateExpMessage": "Required Expression" } } \ No newline at end of file diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/accessibility/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/accessibility/.content.xml index 344ca4f021..86138e7205 100644 --- a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/accessibility/.content.xml +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/accessibility/.content.xml @@ -60,7 +60,7 @@ sling:resourceType="forms-components-examples/components/form/panelcontainer" enabled="{Boolean}true" fieldType="panel" - hideTitle="false" + hideTitle="true" name="panelcontainer1684928145531" readOnly="{Boolean}false" repeatable="false" diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/accessibility/screenreadertext/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/accessibility/screenreadertext/.content.xml new file mode 100644 index 0000000000..a28c2b5255 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/accessibility/screenreadertext/.content.xml @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/de.json b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/de.json index 52ec276f03..7de58f6b84 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/de.json +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/de.json @@ -1,28 +1,28 @@ { - "FileCloseAccessText" : "Drücken Sie die Eingabetaste, um die Datei zu löschen ", - "FileSizeGreater" : "Dateien ${0} übersteigen die erwartete Größe: ${1}MB.", - "FileNameInvalid" : "Datei(en) ${0} hat/haben ungültige Zeichen in ihrem Namen. Es werden nur alphanumerische Zeichen unterstützt", - "FileMimeTypeInvalid" : "Datei(en) ${0} ist/sind nicht unterstützte(r) Dateityp(en)", - "InternalFormSubmissionError" : "Beim Übermitteln des Formulars ist ein interner Fehler aufgetreten.", - "type" : "Please enter a valid value.", - "required" : "Please fill in this field.", - "minimum" : "Value must be greater than or equal to ${0}.", - "maximum" : "Value must be less than or equal to ${0}.", - "minLength" : "Please lengthen this text to ${0} characters or more.", - "maxLength" : "Please shorten this text to ${0} characters or less.", - "step" : "Please enter a valid value.", - "format" : "Specify the value in allowed format : ${0}.", - "pattern" : "Please match the format requested.", - "minItems" : "Specify a number of items equal to or greater than ${0}.", - "maxItems" : "Specify a number of items equal to or less than ${0}.", - "uniqueItems" : "All the items must be unique.", - "validationExpression" : "Please enter a valid value.", - "maxFileSize" : "File too large. Reduce size and try again.", - "accept" : "The specified file type not supported.", - "defaultError" : "There is an error in the field", - "clearText" : "Löschen", - "calendarSymbols" : { - "monthNames" : [ + "FileCloseAccessText": "Drücken Sie die Eingabetaste, um die Datei zu löschen ", + "FileSizeGreater": "Dateien ${0} übersteigen die erwartete Größe: ${1}MB.", + "FileNameInvalid": "Hängen Sie keine Dateien an, deren Dateiname mit (.) beginnt, \\ / : * ? \" < > | ; % oder $ enthält oder ein reserviertes Schlüsselwort wie nul, prn, con, lpt oder com ist.", + "FileMimeTypeInvalid": "Datei(en) ${0} ist/sind nicht unterstützte(r) Dateityp(en)", + "InternalFormSubmissionError": "Beim Übermitteln des Formulars ist ein interner Fehler aufgetreten.", + "type": "Bitte geben Sie einen gültigen Wert ein.", + "required": "Bitte füllen Sie dieses Feld aus.", + "minimum": "Der Wert muss größer als oder gleich ${0} sein.", + "maximum": "Der Wert muss kleiner als oder gleich ${0} sein.", + "minLength": "Bitte verlängern Sie diesen Text auf ${0} Zeichen oder mehr.", + "maxLength": "Bitte kürzen Sie diesen Text auf maximal ${0} Zeichen.", + "step": "Bitte geben Sie einen gültigen Wert ein.", + "format": "Geben Sie den Wert im zulässigen Format an: ${0}.", + "pattern": "Bitte passen Sie dies an das angeforderte Format an.", + "minItems": "Geben Sie eine Anzahl von Elementen an, die größer oder gleich ${0} ist.", + "maxItems": "Geben Sie eine Anzahl von Elementen an, die kleiner oder gleich ${0} ist.", + "uniqueItems": "Alle Elemente müssen eindeutig sein.", + "validationExpression": "Bitte geben Sie einen gültigen Wert ein.", + "maxFileSize": "Datei zu groß. Reduzieren Sie die Größe, und versuchen Sie es erneut.", + "accept": "Der angegebene Dateityp wird nicht unterstützt.", + "defaultError": "In dem Feld liegt ein Fehler vor.", + "clearText": "Löschen", + "calendarSymbols": { + "monthNames": [ "Januar", "Februar", "März", @@ -36,7 +36,7 @@ "November", "Dezember" ], - "abbrmonthNames" : [ + "abbrmonthNames": [ "Jan", "Feb", "Mrz", @@ -50,7 +50,7 @@ "Nov", "Dez" ], - "dayNames" : [ + "dayNames": [ "Sonntag", "Montag", "Dienstag", @@ -59,7 +59,7 @@ "Freitag", "Samstag" ], - "abbrdayNames" : [ + "abbrdayNames": [ "So", "Mo", "Di", @@ -68,21 +68,21 @@ "Fr", "Sa" ], - "meridiemNames" : [ + "meridiemNames": [ "vorm.", "nachm." ], - "eraNames" : [ + "eraNames": [ "v. Chr.", "n. Chr." ], - "day": "day", - "days": "days", - "month": "month", - "months": "months", - "year": "year", - "years": "years", - "more": "more", - "less": "less" + "day": "Tag", + "days": "Tage", + "month": "Monat", + "months": "Monate", + "year": "Jahr", + "years": "Jahre", + "more": "mehr", + "less": "weniger" } } \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/es.json b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/es.json index 13d60a5d4a..3d3ecf2954 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/es.json +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/es.json @@ -1,41 +1,88 @@ { - "FileCloseAccessText" : "Presione Intro para eliminar el archivo ", - "FileSizeGreater" : "FLos archivos ${0} tienen un tamaño superior al esperado: ${1}MB.", - "FileNameInvalid" : "El nombre de los archivos ${0} contiene caracteres no válidos. Solo se admiten caracteres alfanuméricos", - "FileMimeTypeInvalid" : "Los tipos de archivo ${0} no son compatibles", - "InternalFormSubmissionError" : "Error interno al enviar el formulario.", - "type" : "Please enter a valid value.", - "required" : "Please fill in this field.", - "minimum" : "Value must be greater than or equal to ${0}.", - "maximum" : "Value must be less than or equal to ${0}.", - "minLength" : "Please lengthen this text to ${0} characters or more.", - "maxLength" : "Please shorten this text to ${0} characters or less.", - "step" : "Please enter a valid value.", - "format" : "Specify the value in allowed format : ${0}.", - "pattern" : "Please match the format requested.", - "minItems" : "Specify a number of items equal to or greater than ${0}.", - "maxItems" : "Specify a number of items equal to or less than ${0}.", - "uniqueItems" : "All the items must be unique.", - "validationExpression" : "Please enter a valid value.", - "maxFileSize" : "File too large. Reduce size and try again.", - "accept" : "The specified file type not supported.", - "defaultError" : "There is an error in the field", - "clearText" : "Borrar", - "calendarSymbols" : { - "monthNames" : ["enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", - "octubre", "noviembre", "diciembre"], - "abbrmonthNames" : ["ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic"], - "dayNames" : ["domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado"], - "abbrdayNames" : ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"], - "meridiemNames" : ["AM", "PM"], - "eraNames" : ["a.C.", "d.C."], - "day": "day", - "days": "days", - "month": "month", - "months": "months", - "year": "year", - "years": "years", - "more": "more", - "less": "less" + "FileCloseAccessText": "Presione Intro para eliminar el archivo ", + "FileSizeGreater": "Los archivos ${0} son mayores que el tamaño esperado: ${1} MB.", + "FileNameInvalid": "No adjunte archivos cuyo nombre comience con (.), contenga \\ / : * ? \" < > | ; % $, o sea una palabra clave reservada como nul, prn, con, lpt o com.", + "FileMimeTypeInvalid": "Los tipos de archivo ${0} no son compatibles", + "InternalFormSubmissionError": "Error interno al enviar el formulario.", + "type": "Introduzca un valor válido.", + "required": "Rellene este campo.", + "minimum": "El valor debe ser mayor que o igual a ${0}.", + "maximum": "El valor debe ser menor que o igual a ${0}.", + "minLength": "Amplíe este texto a ${0} caracteres o más.", + "maxLength": "Reduzca este texto a ${0} caracteres o menos.", + "step": "Introduzca un valor válido.", + "format": "Especifique el valor en un formato permitido: ${0}.", + "pattern": "Debe coincidir con el formato solicitado.", + "minItems": "Especifique un número de elementos igual a o mayor que ${0}.", + "maxItems": "Especifique un número de elementos igual o menor que ${0}.", + "uniqueItems": "Todos los elementos deben ser únicos.", + "validationExpression": "Introduzca un valor válido.", + "maxFileSize": "Archivo demasiado grande. Reduzca el tamaño e inténtelo de nuevo.", + "accept": "No se admite el tipo de archivo especificado.", + "defaultError": "Hay un error en el campo", + "clearText": "Borrar", + "calendarSymbols": { + "monthNames": [ + "Enero", + "Febrero", + "Marzo", + "Abril", + "Mayo", + "Junio", + "Julio", + "Agosto", + "Septiembre", + "Octubre", + "Noviembre", + "Diciembre" + ], + "abbrmonthNames": [ + "Ene", + "Feb", + "Mar", + "Abr", + "Mayo", + "Jun", + "Jul", + "Ago", + "Sep", + "Oct", + "Nov", + "Dic" + ], + "dayNames": [ + "Domingo", + "Lunes", + "Martes", + "Miércoles", + "Jueves", + "Viernes", + "Sábado" + ], + "abbrdayNames": [ + "dom.", + "lun.", + "mar.", + "miérc.", + "juev.", + "vier.", + "sáb." + ], + "meridiemNames": [ + "a. m.", + "p. m." + ], + "eraNames": [ + "a. C.", + "d. C." + ], + "day": "día", + "days": "días", + "month": "mes", + "months": "meses", + "year": "año", + "years": "años", + "more": "más", + "less": "menos" } } \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/fr.json b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/fr.json index 27f8a558cc..cd3d6f84dd 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/fr.json +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/fr.json @@ -1,28 +1,28 @@ { - "FileCloseAccessText" : "Appuyer sur Entrée pour supprimer le fichier ", - "FileSizeGreater" : "FLes fichiers ${0} font plus que la taille attendue : ${1} Mo.", - "FileNameInvalid" : "Le nom du ou des fichiers ${0} comportent des caractères non valides. Seuls les caractères alphanumériques sont pris en charge", - "FileMimeTypeInvalid" : "Le ou les fichiers ${0} sont des types de fichiers non pris en charge", - "InternalFormSubmissionError" : "Une erreur interne s'est produite lors de l'envoi du formulaire.", - "type" : "Please enter a valid value.", - "required" : "Please fill in this field.", - "minimum" : "Value must be greater than or equal to ${0}.", - "maximum" : "Value must be less than or equal to ${0}.", - "minLength" : "Please lengthen this text to ${0} characters or more.", - "maxLength" : "Please shorten this text to ${0} characters or less.", - "step" : "Please enter a valid value.", - "format" : "Specify the value in allowed format : ${0}.", - "pattern" : "Please match the format requested.", - "minItems" : "Specify a number of items equal to or greater than ${0}.", - "maxItems" : "Specify a number of items equal to or less than ${0}.", - "uniqueItems" : "All the items must be unique.", - "validationExpression" : "Please enter a valid value.", - "maxFileSize" : "File too large. Reduce size and try again.", - "accept" : "The specified file type not supported.", - "defaultError" : "There is an error in the field", - "clearText" : "Effacer", - "calendarSymbols" : { - "monthNames" : [ + "FileCloseAccessText": "Appuyer sur Entrée pour supprimer le fichier ", + "FileSizeGreater": "${0} fichier(s) dépasse(nt) la taille attendue : ${1} Mo.", + "FileNameInvalid": "Ne joignez pas de fichiers dont le nom commence par (.), contient \\ / : * ? \" < > | ; % $, ou est un mot-clé réservé comme nul, prn, con, lpt ou com.", + "FileMimeTypeInvalid": "Le ou les fichiers ${0} sont des types de fichiers non pris en charge", + "InternalFormSubmissionError": "Une erreur interne s'est produite lors de l'envoi du formulaire.", + "type": "Saisissez une valeur valide.", + "required": "Remplissez ce champ.", + "minimum": "La valeur doit être supérieure ou égale à ${0}.", + "maximum": "La valeur doit être supérieure ou égale à ${0}.", + "minLength": "Allongez ce texte à ${0} caractères minimum.", + "maxLength": "Réduisez ce texte à ${0} caractères maximum.", + "step": "Saisissez une valeur valide.", + "format": "Spécifiez la valeur au format autorisé : ${0}.", + "pattern": "Veuillez respecter le format demandé.", + "minItems": "Spécifiez un nombre d'éléments supérieur ou égal à ${0}.", + "maxItems": "Spécifiez un nombre d'éléments inférieur ou égal à ${0}.", + "uniqueItems": "Tous les éléments doivent être uniques.", + "validationExpression": "Saisissez une valeur valide.", + "maxFileSize": "Fichier trop volumineux. Réduisez la taille et réessayez.", + "accept": "Le type de fichier spécifié n’est pas pris en charge.", + "defaultError": "Le champ contient une erreur.", + "clearText": "Effacer", + "calendarSymbols": { + "monthNames": [ "janvier", "février", "mars", @@ -36,7 +36,7 @@ "novembre", "décembre" ], - "abbrmonthNames" : [ + "abbrmonthNames": [ "janv.", "févr.", "mars", @@ -50,7 +50,7 @@ "nov.", "déc." ], - "dayNames" : [ + "dayNames": [ "dimanche", "lundi", "mardi", @@ -59,7 +59,7 @@ "vendredi", "samedi" ], - "abbrdayNames" : [ + "abbrdayNames": [ "dim.", "lun.", "mar.", @@ -68,21 +68,21 @@ "ven.", "sam." ], - "meridiemNames" : [ + "meridiemNames": [ "AM", "PM" ], - "eraNames" : [ + "eraNames": [ "av. J.-C.", "ap. J.-C." ], - "day": "day", - "days": "days", - "month": "month", - "months": "months", - "year": "year", - "years": "years", - "more": "more", - "less": "less" + "day": "jour", + "days": "jours", + "month": "mois", + "months": "mois", + "year": "année", + "years": "années", + "more": "plus", + "less": "moins" } } \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/it.json b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/it.json index 8f1c8bb98d..7a677f5734 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/it.json +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/it.json @@ -1,41 +1,88 @@ { - "FileCloseAccessText" : "Premete Invio per eliminare il file ", - "FileSizeGreater" : "I file ${0} superano le dimensioni previste: ${1} MB.", - "FileNameInvalid" : "I file ${0} contengono caratteri non validi nel nome. Sono supportati solo caratteri alfanumerici", - "FileMimeTypeInvalid" : "I file ${0} non sono tipi di file supportati", - "InternalFormSubmissionError" : "Errore interno durante l'invio del modulo.", - "type" : "Please enter a valid value.", - "required" : "Please fill in this field.", - "minimum" : "Value must be greater than or equal to ${0}.", - "maximum" : "Value must be less than or equal to ${0}.", - "minLength" : "Please lengthen this text to ${0} characters or more.", - "maxLength" : "Please shorten this text to ${0} characters or less.", - "step" : "Please enter a valid value.", - "format" : "Specify the value in allowed format : ${0}.", - "pattern" : "Please match the format requested.", - "minItems" : "Specify a number of items equal to or greater than ${0}.", - "maxItems" : "Specify a number of items equal to or less than ${0}.", - "uniqueItems" : "All the items must be unique.", - "validationExpression" : "Please enter a valid value.", - "maxFileSize" : "File too large. Reduce size and try again.", - "accept" : "The specified file type not supported.", - "defaultError" : "There is an error in the field", - "clearText" : "Cancella", - "calendarSymbols" : { - "monthNames" : ["gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno", "luglio", "agosto", - "settembre", "ottobre", "novembre", "dicembre"], - "abbrmonthNames" : ["gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic"], - "dayNames" : ["domenica", "lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato"], - "abbrdayNames" : ["dom", "lun", "mar", "mer", "gio", "ven", "sab"], - "meridiemNames" : ["AM", "PM"], - "eraNames" : ["aC", "dC"], - "day": "day", - "days": "days", - "month": "month", - "months": "months", - "year": "year", - "years": "years", - "more": "more", - "less": "less" + "FileCloseAccessText": "Premete Invio per eliminare il file ", + "FileSizeGreater": "I file ${0} superano le dimensioni previste: ${1} MB.", + "FileNameInvalid": "Non allegare file il cui nome inizia con (.), contiene \\ / : * ? \" < > | ; % $ oppure è una parola chiave riservata come nul, prn, con, lpt o com.", + "FileMimeTypeInvalid": "I file ${0} non sono tipi di file supportati", + "InternalFormSubmissionError": "Errore interno durante l'invio del modulo.", + "type": "Immetti un valore valido.", + "required": "Compila questo campo.", + "minimum": "Il valore deve essere maggiore di o uguale a ${0}.", + "maximum": "Il valore deve essere minore di o uguale a ${0}.", + "minLength": "Allunga questo testo fino ad almeno ${0} caratteri.", + "maxLength": "Abbrevia il testo a un massimo di ${0} caratteri.", + "step": "Immetti un valore valido.", + "format": "Specifica il valore nel formato consentito: ${0}.", + "pattern": "Utilizza il formato richiesto.", + "minItems": "Specifica un numero di elementi uguale o superiore a ${0}.", + "maxItems": "Specifica un numero di elementi uguale o inferiore a ${0}.", + "uniqueItems": "Tutti gli elementi devono essere univoci.", + "validationExpression": "Immetti un valore valido.", + "maxFileSize": "File troppo grande. Riduci la dimensione del file e riprova.", + "accept": "Tipo di file specificato non supportato.", + "defaultError": "Il campo contiene un errore.", + "clearText": "Cancella", + "calendarSymbols": { + "monthNames": [ + "gennaio", + "febbraio", + "marzo", + "aprile", + "Maggio", + "giugno", + "luglio", + "agosto", + "settembre", + "ottobre", + "novembre", + "dicembre" + ], + "abbrmonthNames": [ + "gen", + "feb", + "mar", + "apr", + "Maggio", + "giu", + "lug", + "ago", + "set", + "ott", + "nov", + "dic" + ], + "dayNames": [ + "domenica", + "lunedì", + "martedì", + "mercoledì", + "giovedì", + "venerdì", + "sabato" + ], + "abbrdayNames": [ + "dom", + "lun", + "mar", + "mer", + "gio", + "ven", + "sab" + ], + "meridiemNames": [ + "AM", + "PM" + ], + "eraNames": [ + "a.C.", + "d.C." + ], + "day": "giorno", + "days": "giorni", + "month": "mese", + "months": "mesi", + "year": "anno", + "years": "anni", + "more": "più", + "less": "meno" } } \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/ja.json b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/ja.json index c2f1a00b30..deea3da6ff 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/ja.json +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/ja.json @@ -1,56 +1,56 @@ { - "FileCloseAccessText" : "ファイルを削除するには Enter を押します ", - "FileSizeGreater" : "ファイル「${0}」は予期されたサイズを超えています :${1} MB。", - "FileNameInvalid" : "${0} ファイルの名前に無効な文字が含まれています。サポートされるのは英数字のみになります", - "FileMimeTypeInvalid" : "${0} ファイルの形式はサポートされていません", - "InternalFormSubmissionError" : "フォームを送信中に内部エラーが発生しました。", - "type" : "Please enter a valid value.", - "required" : "Please fill in this field.", - "minimum" : "Value must be greater than or equal to ${0}.", - "maximum" : "Value must be less than or equal to ${0}.", - "minLength" : "Please lengthen this text to ${0} characters or more.", - "maxLength" : "Please shorten this text to ${0} characters or less.", - "step" : "Please enter a valid value.", - "format" : "Specify the value in allowed format : ${0}.", - "pattern" : "Please match the format requested.", - "minItems" : "Specify a number of items equal to or greater than ${0}.", - "maxItems" : "Specify a number of items equal to or less than ${0}.", - "uniqueItems" : "All the items must be unique.", - "validationExpression" : "Please enter a valid value.", - "maxFileSize" : "File too large. Reduce size and try again.", - "accept" : "The specified file type not supported.", - "defaultError" : "There is an error in the field", - "clearText" : "消去", - "calendarSymbols" : { - "monthNames" : [ - "1月", - "2月", - "3月", - "4月", - "5月", - "6月", - "7月", - "8月", - "9月", - "10月", - "11月", - "12月" + "FileCloseAccessText": "ファイルを削除するには Enter を押します ", + "FileSizeGreater": "ファイル「${0}」は予期されたサイズを超えています :${1} MB。", + "FileNameInvalid": "ファイル名が (.) で始まる、\\ / : * ? \" < > | ; % $ を含む、または nul、prn、con、lpt、com などの予約キーワードを含むファイルは添付しないでください。", + "FileMimeTypeInvalid": "${0} ファイルの形式はサポートされていません", + "InternalFormSubmissionError": "フォームを送信中に内部エラーが発生しました。", + "type": "有効な値を入力してください。", + "required": "このフィールドに入力してください。", + "minimum": "値は ${0} 以上にする必要があります。", + "maximum": "値は ${0} 以下にする必要があります。", + "minLength": "このテキストを ${0} 文字以上に長くしてください。", + "maxLength": "このテキストを ${0} 文字以下に短くしてください。", + "step": "有効な値を入力してください。", + "format": "許可された形式 : ${0} で値を指定します。", + "pattern": "リクエストされた形式と一致させてください。", + "minItems": "${0} 以上の項目数を指定します。", + "maxItems": "${0} 以下の項目数を指定します。", + "uniqueItems": "すべての項目は一意にする必要があります。", + "validationExpression": "有効な値を入力してください。", + "maxFileSize": "ファイルが大きすぎます。サイズを小さくして、もう一度試してください。", + "accept": "指定したファイルタイプはサポートされていません。", + "defaultError": "フィールドにエラーがあります。", + "clearText": "消去", + "calendarSymbols": { + "monthNames": [ + "1 月", + "2 月", + "3 月", + "4 月", + "5 月", + "6 月", + "7 月", + "8 月", + "9 月", + "10 月", + "11 月", + "12 月" ], - "abbrmonthNames" : [ - "1月", - "2月", - "3月", - "4月", - "5月", - "6月", - "7月", - "8月", - "9月", - "10月", - "11月", - "12月" + "abbrmonthNames": [ + "1 月", + "2 月", + "3 月", + "4 月", + "5 月", + "6 月", + "7 月", + "8 月", + "9 月", + "10 月", + "11 月", + "12 月" ], - "dayNames" : [ + "dayNames": [ "日曜日", "月曜日", "火曜日", @@ -59,7 +59,7 @@ "金曜日", "土曜日" ], - "abbrdayNames" : [ + "abbrdayNames": [ "日", "月", "火", @@ -68,21 +68,21 @@ "金", "土" ], - "meridiemNames" : [ + "meridiemNames": [ "午前", "午後" ], - "eraNames" : [ - "紀元前", - "西暦" + "eraNames": [ + "BC", + "AD" ], - "day": "day", - "days": "days", - "month": "month", - "months": "months", - "year": "year", - "years": "years", - "more": "more", - "less": "less" + "day": "日", + "days": "日", + "month": "月", + "months": "月", + "year": "年", + "years": "年", + "more": "さらに表示", + "less": "表示を減らす" } } \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/ko-kr.json b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/ko-kr.json index a7cbafcd76..4aab35214d 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/ko-kr.json +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/ko-kr.json @@ -1,40 +1,88 @@ { - "FileCloseAccessText" : "Enter 키를 눌러 파일 삭제", - "FileSizeGreater" : "파일 ${0}이(가) 예상 크기 ${1}MB를 초과합니다.", - "FileNameInvalid" : "파일 ${0}의 이름에 잘못된 문자가 포함되어 있습니다. 영숫자만 지원됩니다.", - "FileMimeTypeInvalid" : "파일 ${0}은(는) 지원되지 않는 파일 유형입니다.", - "InternalFormSubmissionError" : "양식을 제출하는 중 내부 오류가 발생했습니다.", - "type" : "Please enter a valid value.", - "required" : "Please fill in this field.", - "minimum" : "Value must be greater than or equal to ${0}.", - "maximum" : "Value must be less than or equal to ${0}.", - "minLength" : "Please lengthen this text to ${0} characters or more.", - "maxLength" : "Please shorten this text to ${0} characters or less.", - "step" : "Please enter a valid value.", - "format" : "Specify the value in allowed format : ${0}.", - "pattern" : "Please match the format requested.", - "minItems" : "Specify a number of items equal to or greater than ${0}.", - "maxItems" : "Specify a number of items equal to or less than ${0}.", - "uniqueItems" : "All the items must be unique.", - "validationExpression" : "Please enter a valid value.", - "maxFileSize" : "File too large. Reduce size and try again.", - "accept" : "The specified file type not supported.", - "defaultError" : "There is an error in the field", - "clearText" : "지우기", - "calendarSymbols" : { - "monthNames" : ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"], - "abbrmonthNames" : ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"], - "dayNames" : ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"], - "abbrdayNames" : ["일", "월", "화", "수", "목", "금", "토"], - "meridiemNames" : ["오전", "오후"], - "eraNames" : ["기원전", "서기"], - "day": "day", - "days": "days", - "month": "month", - "months": "months", - "year": "year", - "years": "years", - "more": "more", - "less": "less" + "FileCloseAccessText": "Enter 키를 눌러 파일 삭제", + "FileSizeGreater": "파일 ${0}이(가) 예상 크기 ${1}MB를 초과합니다.", + "FileNameInvalid": "파일 이름이 (.)으로 시작하거나, \\ / : * ? \" < > | ; % $를 포함하거나, nul, prn, con, lpt 또는 com과 같이 예약된 키워드인 파일은 첨부하지 마십시오.", + "FileMimeTypeInvalid": "파일 ${0}은(는) 지원되지 않는 파일 유형입니다.", + "InternalFormSubmissionError": "양식을 제출하는 중 내부 오류가 발생했습니다.", + "type": "유효한 값을 입력해 주십시오.", + "required": "이 필드를 작성해 주십시오.", + "minimum": "값은 ${0}보다 크거나 같아야 합니다.", + "maximum": "값은 ${0}보다 작거나 같아야 합니다.", + "minLength": "이 텍스트의 문자 길이를 ${0}자 이상으로 늘려 주십시오.", + "maxLength": "이 텍스트의 문자 길이를 ${0}자 이하로 줄여 주십시오.", + "step": "유효한 값을 입력해 주십시오.", + "format": "허용되는 형식(${0})으로 값을 지정하십시오.", + "pattern": "요청되는 형식에 맞춰 주십시오.", + "minItems": "항목의 수를 ${0} 이상으로 지정하십시오.", + "maxItems": "항목의 수를 ${0} 이하로 지정하십시오.", + "uniqueItems": "모든 항목은 고유해야 합니다.", + "validationExpression": "유효한 값을 입력해 주십시오.", + "maxFileSize": "파일이 너무 큽니다. 크기를 줄이고 다시 시도하십시오.", + "accept": "지정된 파일 유형이 지원되지 않습니다.", + "defaultError": "필드에 오류가 있습니다.", + "clearText": "지우기", + "calendarSymbols": { + "monthNames": [ + "1월", + "2월", + "3월", + "4월", + "5월", + "6월", + "7월", + "8월", + "9월", + "10월", + "11월", + "12월" + ], + "abbrmonthNames": [ + "1월", + "2월", + "3월", + "4월", + "5월", + "6월", + "7월", + "8월", + "9월", + "10월", + "11월", + "12월" + ], + "dayNames": [ + "일요일", + "월요일", + "화요일", + "수요일", + "목요일", + "금요일", + "토요일" + ], + "abbrdayNames": [ + "일", + "월", + "화", + "수", + "목", + "금", + "토" + ], + "meridiemNames": [ + "오전", + "오후" + ], + "eraNames": [ + "기원전", + "서기" + ], + "day": "일", + "days": "일", + "month": "월", + "months": "개월", + "year": "년", + "years": "년", + "more": "기타", + "less": "미만" } } \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/pt-br.json b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/pt-br.json index 6cd1f4e26a..26db53f7e0 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/pt-br.json +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/pt-br.json @@ -1,42 +1,88 @@ { - "FileCloseAccessText" : "Pressione Enter para excluir o arquivo ", - "FileSizeGreater" : "Fos arquivos ${0} são maiores do que o tamanho esperado: ${1}MB.", - "FileNameInvalid" : "O(s) arquivo(s) ${0} tem caracteres inválidos em seu nome. Somente caracteres alfanuméricos são suportados", - "FileMimeTypeInvalid" : "O(s) arquivo(s) ${0} não é(são) suportado(s)", - "InternalFormSubmissionError" : "Encontrou um erro interno ao enviar o formulário.", - "type" : "Please enter a valid value.", - "required" : "Please fill in this field.", - "minimum" : "Value must be greater than or equal to ${0}.", - "maximum" : "Value must be less than or equal to ${0}.", - "minLength" : "Please lengthen this text to ${0} characters or more.", - "maxLength" : "Please shorten this text to ${0} characters or less.", - "step" : "Please enter a valid value.", - "format" : "Specify the value in allowed format : ${0}.", - "pattern" : "Please match the format requested.", - "minItems" : "Specify a number of items equal to or greater than ${0}.", - "maxItems" : "Specify a number of items equal to or less than ${0}.", - "uniqueItems" : "All the items must be unique.", - "validationExpression" : "Please enter a valid value.", - "maxFileSize" : "File too large. Reduce size and try again.", - "accept" : "The specified file type not supported.", - "defaultError" : "There is an error in the field", - "clearText" : "Limpar", - "calendarSymbols" : { - "monthNames" : ["janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", - "outubro", "novembro", "dezembro"], - "abbrmonthNames" : ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"], - "dayNames" : ["domingo", "segunda-feira", "terça-feira", "quarta-feira", "quinta-feira", "sexta-feira", - "sábado"], - "abbrdayNames" : ["dom", "seg", "ter", "qua", "qui", "sex", "sáb"], - "meridiemNames" : ["AM", "PM"], - "eraNames" : ["BC", "AD"], - "day": "day", - "days": "days", - "month": "month", - "months": "months", - "year": "year", - "years": "years", - "more": "more", - "less": "less" + "FileCloseAccessText": "Pressione Enter para excluir o arquivo ", + "FileSizeGreater": "Fos arquivos ${0} são maiores do que o tamanho esperado: ${1}MB.", + "FileNameInvalid": "Não anexe arquivos cujos nomes comecem com (.), contenham \\ / : * ? \" < > | ; % $, ou sejam palavras-chave reservadas, como nul, prn, con, lpt ou com.", + "FileMimeTypeInvalid": "O(s) arquivo(s) ${0} não é(são) suportado(s)", + "InternalFormSubmissionError": "Encontrou um erro interno ao enviar o formulário.", + "type": "Insira um valor válido.", + "required": "Preencha este campo.", + "minimum": "O valor precisa ser superior ou igual a ${0}.", + "maximum": "O valor precisa ser inferior ou igual a ${0}.", + "minLength": "Aumente este texto para ${0} caracteres ou mais.", + "maxLength": "Diminua este texto para ${0} caracteres ou menos.", + "step": "Insira um valor válido.", + "format": "Especifique o valor no formato permitido: ${0}.", + "pattern": "Siga o formato solicitado.", + "minItems": "Especifique um número de itens igual ou superior a ${0}.", + "maxItems": "Especifique um número de itens igual ou inferior a ${0}.", + "uniqueItems": "Todos os itens devem ser exclusivos.", + "validationExpression": "Insira um valor válido.", + "maxFileSize": "O arquivo é grande demais. Reduza o tamanho e tente novamente.", + "accept": "O tipo de arquivo especificado não é compatível.", + "defaultError": "Há um erro no campo", + "clearText": "Limpar", + "calendarSymbols": { + "monthNames": [ + "Janeiro", + "Fevereiro", + "Março", + "Abril", + "Maio", + "Junho", + "Julho", + "Agosto", + "Setembro", + "Outubro", + "Novembro", + "Dezembro" + ], + "abbrmonthNames": [ + "Jan", + "Fev", + "Mar", + "Abr", + "Maio", + "Jun", + "Jul", + "Ago", + "Set", + "Out", + "Nov", + "Dez" + ], + "dayNames": [ + "Domingo", + "Segunda-feira", + "Terça-feira", + "Quarta-feira", + "Quinta-feira", + "Sexta-feira", + "Sábado" + ], + "abbrdayNames": [ + "Dom", + "Seg", + "Ter", + "Qua", + "Qui", + "Sex", + "Sáb" + ], + "meridiemNames": [ + "AM", + "PM" + ], + "eraNames": [ + "a.C.", + "d.C." + ], + "day": "dia", + "days": "dias", + "month": "mês", + "months": "meses", + "year": "ano", + "years": "anos", + "more": "mais", + "less": "menos" } } \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/zh-cn.json b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/zh-cn.json index 50095fac6a..13a6a78cd7 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/zh-cn.json +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/zh-cn.json @@ -1,40 +1,88 @@ { - "FileCloseAccessText" : "按 Enter 可删除文件", - "FileSizeGreater" : "文件 ${0} 大于预期大小: ${1}MB。", - "FileNameInvalid" : "文件 ${0} 的名称中包含无效字符。仅支持字母数字字符", - "FileMimeTypeInvalid" : "文件 ${0} 的类型不受支持", - "InternalFormSubmissionError" : "提交表单时遇到内部错误。", - "type" : "Please enter a valid value.", - "required" : "Please fill in this field.", - "minimum" : "Value must be greater than or equal to ${0}.", - "maximum" : "Value must be less than or equal to ${0}.", - "minLength" : "Please lengthen this text to ${0} characters or more.", - "maxLength" : "Please shorten this text to ${0} characters or less.", - "step" : "Please enter a valid value.", - "format" : "Specify the value in allowed format : ${0}.", - "pattern" : "Please match the format requested.", - "minItems" : "Specify a number of items equal to or greater than ${0}.", - "maxItems" : "Specify a number of items equal to or less than ${0}.", - "uniqueItems" : "All the items must be unique.", - "validationExpression" : "Please enter a valid value.", - "maxFileSize" : "File too large. Reduce size and try again.", - "accept" : "The specified file type not supported.", - "defaultError" : "There is an error in the field", - "clearText" : "清除", - "calendarSymbols" : { - "monthNames" : ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], - "abbrmonthNames" : ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], - "dayNames" : ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"], - "abbrdayNames" : ["日", "一", "二", "三", "四", "五", "六"], - "meridiemNames" : ["上午", "下午"], - "eraNames" : ["公元前", "公元"], - "day": "day", - "days": "days", - "month": "month", - "months": "months", - "year": "year", - "years": "years", - "more": "more", - "less": "less" + "FileCloseAccessText": "按 Enter 可删除文件", + "FileSizeGreater": "文件 ${0} 大于预期大小: ${1}MB。", + "FileNameInvalid": "请勿附加文件名以 (.) 开头、包含 \\ / : * ? \" < > | ; % $ 或为保留关键字(如 nul、prn、con、lpt 或 com)的文件。", + "FileMimeTypeInvalid": "文件 ${0} 的类型不受支持", + "InternalFormSubmissionError": "提交表单时遇到内部错误。", + "type": "请输入有效的值。", + "required": "请填写此字段。", + "minimum": "值必须大于或等于 ${0}。", + "maximum": "值必须小于或等于${0}。", + "minLength": "请将此文本加长至 ${0} 个字符或更多。", + "maxLength": "请将此文本缩短至 ${0} 个字符或更少。", + "step": "请输入有效的值。", + "format": "以允许的格式指定值:${0}。", + "pattern": "请匹配要求的格式。", + "minItems": "指定等于或大于 ${0}的项目数。", + "maxItems": "指定等于或小于 ${0}的项目数。", + "uniqueItems": "所有项目都必须是唯一的。", + "validationExpression": "请输入有效的值。", + "maxFileSize": "文件太大。减小文件大小并重试。", + "accept": "指定的文件类型不受支持。", + "defaultError": "该字段有错误", + "clearText": "清除", + "calendarSymbols": { + "monthNames": [ + "一月", + "二月", + "三月", + "四月", + "五月", + "六月", + "七月", + "八月", + "九月", + "十月", + "十一月", + "十二月" + ], + "abbrmonthNames": [ + "一月", + "二月", + "三月", + "四月", + "五月", + "六月", + "七月", + "八月", + "九月", + "十月", + "十一月", + "十二月" + ], + "dayNames": [ + "星期日", + "星期一", + "星期二", + "星期三", + "星期四", + "星期五", + "星期六" + ], + "abbrdayNames": [ + "星期日", + "星期一", + "星期二", + "星期三", + "星期四", + "星期五", + "星期六" + ], + "meridiemNames": [ + "上午", + "下午" + ], + "eraNames": [ + "公元前", + "公元" + ], + "day": "天", + "days": "天", + "month": "月", + "months": "月", + "year": "年", + "years": "年", + "more": "更多", + "less": "更少" } } \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/zh-tw.json b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/zh-tw.json index 5be52e97dc..23f3ddf971 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/zh-tw.json +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/af-clientlibs/core-forms-components-runtime-all/resources/i18n/zh-tw.json @@ -1,40 +1,88 @@ { - "FileCloseAccessText" : "按 Enter 以刪除檔案", - "FileSizeGreater" : "檔案 ${0} 的大小比預期大: ${1}MB。", - "FileNameInvalid" : "${0} 檔案名稱中包含無效字元。僅支援字母數字字元", - "FileMimeTypeInvalid" : "${0} 檔案的檔案類型不受支援", - "InternalFormSubmissionError" : "提交表單時發生內部錯誤。", - "type" : "Please enter a valid value.", - "required" : "Please fill in this field.", - "minimum" : "Value must be greater than or equal to ${0}.", - "maximum" : "Value must be less than or equal to ${0}.", - "minLength" : "Please lengthen this text to ${0} characters or more.", - "maxLength" : "Please shorten this text to ${0} characters or less.", - "step" : "Please enter a valid value.", - "format" : "Specify the value in allowed format : ${0}.", - "pattern" : "Please match the format requested.", - "minItems" : "Specify a number of items equal to or greater than ${0}.", - "maxItems" : "Specify a number of items equal to or less than ${0}.", - "uniqueItems" : "All the items must be unique.", - "validationExpression" : "Please enter a valid value.", - "maxFileSize" : "File too large. Reduce size and try again.", - "accept" : "The specified file type not supported.", - "defaultError" : "There is an error in the field", - "clearText" : "清除", - "calendarSymbols" : { - "monthNames" : ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], - "abbrmonthNames" : ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], - "dayNames" : ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"], - "abbrdayNames" : ["日", "一", "二", "三", "四", "五", "六"], - "meridiemNames" : ["上午", "下午"], - "eraNames" : ["公元前", "公元"], - "day": "day", - "days": "days", - "month": "month", - "months": "months", - "year": "year", - "years": "years", - "more": "more", - "less": "less" + "FileCloseAccessText": "按 Enter 以刪除檔案", + "FileSizeGreater": "檔案 ${0} 的大小比預期大: ${1}MB。", + "FileNameInvalid": "不要附加以下檔案:檔案名稱以 (.) 開頭且包含 \\ / : * ? \" < > | ; % $,或檔案名稱為保留關鍵字,例如 nul、prn、con、lpt 或 com。", + "FileMimeTypeInvalid": "${0} 檔案的檔案類型不受支援", + "InternalFormSubmissionError": "提交表單時發生內部錯誤。", + "type": "請輸入有效值。", + "required": "請填寫此欄位。", + "minimum": "值必須大於或等於 ${0}。", + "maximum": "值必須小於或等於 ${0}。", + "minLength": "請將此文字延長至 ${0} 個字元或更多。", + "maxLength": "請將此文字縮短至 ${0} 個字元或更少。", + "step": "請輸入有效值。", + "format": "以允許的格式指定值:${0}。", + "pattern": "請符合要求的格式。", + "minItems": "指定等於或大於 ${0} 的項目數。", + "maxItems": "指定等於或小於 ${0} 的項目數。", + "uniqueItems": "所有項目必須是唯一的。", + "validationExpression": "請輸入有效值。", + "maxFileSize": "檔案太大。請縮減大小並再試一次。", + "accept": "指定的檔案類型不受支援。", + "defaultError": "此欄位有錯誤", + "clearText": "清除", + "calendarSymbols": { + "monthNames": [ + "1 月", + "2 月", + "3 月", + "4 月", + "5 月", + "6 月", + "7 月", + "8 月", + "9 月", + "10 月", + "11 月", + "12 月" + ], + "abbrmonthNames": [ + "1 月", + "2 月", + "3 月", + "4 月", + "5 月", + "6 月", + "7 月", + "8 月", + "9 月", + "10 月", + "11 月", + "12 月" + ], + "dayNames": [ + "星期日", + "星期一", + "星期二", + "星期三", + "星期四", + "星期五", + "星期六" + ], + "abbrdayNames": [ + "週日", + "週一", + "週二", + "週三", + "週四", + "週五", + "週六" + ], + "meridiemNames": [ + "上午", + "下午" + ], + "eraNames": [ + "公元前", + "公元" + ], + "day": "天", + "days": "天", + "month": "個月", + "months": "個月", + "year": "年", + "years": "年", + "more": "超過", + "less": "少於" } } \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/af-commons/v1/clientlibs/tabs/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/af-commons/v1/clientlibs/tabs/.content.xml new file mode 100644 index 0000000000..aaa876c946 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/af-commons/v1/clientlibs/tabs/.content.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/af-commons/v1/clientlibs/tabs/js.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/af-commons/v1/clientlibs/tabs/js.txt new file mode 100644 index 0000000000..9f3b9ebc04 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/af-commons/v1/clientlibs/tabs/js.txt @@ -0,0 +1,18 @@ +############################################################################### +# Copyright 2023 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 +common.js \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/af-commons/v1/clientlibs/tabs/js/common.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/af-commons/v1/clientlibs/tabs/js/common.js new file mode 100644 index 0000000000..ecfe9c6bd5 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/af-commons/v1/clientlibs/tabs/js/common.js @@ -0,0 +1,169 @@ +/******************************************************************************* + * 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 () { + + function TabsMixin(Base) { + return class extends Base { + + /** + * Private property for storing the active tab ID. + * @type {string} + */ + #_active; + + /** + * Private property for storing the IS. + * @type {string} + */ + #_IS; + /** + * Private property for storing the namespace. + * @type {string} + */ + #_NS; + /** + * Private property for storing the selectors. + * @type {object} + */ + #_selectors; + + constructor(params, ns, is, selectors) { + super(params); + this.#_IS = is; + this.#_NS = ns; + this.#_selectors = selectors; + const { element } = params; + this.#cacheElements(element); + this.#_active = this.getActiveTabId(this.#getCachedTabs()); + this.#refreshActive(); + } + + /** + * Gets the cached tab panels. + * @returns {NodeList} The cached tab panels. + * @private + */ + #getCachedTabPanels() { + return this._elements["tabpanel"] + } + + /** + * Gets the cached tabs. + * @returns {NodeList} The cached tabs. + * @private + */ + #getCachedTabs() { + return this._elements["tab"]; + } + + /** + * Caches the Tabs elements as defined via the {@code data-tabs-hook="ELEMENT_NAME"} markup API. + * @private + * @param {HTMLElement} wrapper - The Tabs wrapper element. + */ + #cacheElements(wrapper) { + this._elements = {}; + this._elements.self = wrapper; + var hooks = this._elements.self.querySelectorAll("[data-" + this.#_NS + "-hook-" + this.#_IS + "]"); + + for (var i = 0; i < hooks.length; i++) { + var hook = hooks[i]; + if (hook.closest("[data-cmp-is=" + this.#_IS + "]") === this._elements.self) { // only process own tab elements + var lowerCased = this.#_IS.toLowerCase(); + var capitalized = lowerCased.charAt(0).toUpperCase() + lowerCased.slice(1); + var key = hook.dataset[this.#_NS + "Hook" + capitalized]; + if (this._elements[key]) { + if (!Array.isArray(this._elements[key])) { + var tmp = this._elements[key]; + this._elements[key] = [tmp]; + } + this._elements[key].push(hook); + } else { + this._elements[key] = [hook]; + } + } + } + } + + /** + * Refreshes the tab markup based on the current active index. + * @private + */ + #refreshActive() { + var tabpanels = this.#getCachedTabPanels(); + var tabs = this.#getCachedTabs(); + if (tabpanels) { + for (var i = 0; i < tabpanels.length; i++) { + if(tabs[i]) { + if (tabs[i].id === this.#_active) { + tabpanels[i].classList.add(this.#_selectors.active.tabpanel); + tabpanels[i].removeAttribute("aria-hidden"); + tabs[i].classList.add(this.#_selectors.active.tab); + tabs[i].setAttribute("aria-selected", true); + tabs[i].setAttribute("tabindex", "0"); + tabs[i].setAttribute("aria-current", "true"); + } else { + tabpanels[i].classList.remove(this.#_selectors.active.tabpanel); + tabpanels[i].setAttribute("aria-hidden", true); + tabs[i].classList.remove(this.#_selectors.active.tab); + tabs[i].setAttribute("aria-selected", false); + tabs[i].setAttribute("tabindex", "-1"); + tabs[i].setAttribute("aria-current", "false"); + } + } + } + } + } + + /** + * Returns the id of the active tab, if no tab is active returns 0th element id + * + * @param {Array} tabs Tab elements + * @returns {Number} Id of the active tab, 0th element id if none is active + */ + getActiveTabId(tabs) { + if (tabs) { + var result = tabs[0].id; + for (var i = 0; i < tabs.length; i++) { + if (tabs[i].classList.contains(this.#_selectors.active.tab)) { + result = tabs[i].id; + break; + } + } + return result; + } + } + + /** + * Navigates to the tab at the provided index + * + * @private + * @param {Number} index The index of the tab to navigate to + */ + navigate(index) { + this.#_active = index; + this.#refreshActive(); + } + } + } + + window.Forms = window.Forms || {}; + window.Forms.CoreComponentsCommons = window.Forms.CoreComponentsCommons || {}; + window.Forms.CoreComponentsCommons.TabsMixin = TabsMixin; + +}()); diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/accordion.html b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/accordion.html index 8d42a320a4..0ad0b906f3 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/accordion.html +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/accordion.html @@ -84,6 +84,6 @@ data-sly-test="${(wcmmode.edit || wcmmode.preview) && accordion.items.size < 1}"> + data-sly-call="${clientlib.js @ categories='core.forms.components.accordion.v1.contentframe'}"/> diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/.content.xml new file mode 100644 index 0000000000..7f8a033ed9 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/.content.xml @@ -0,0 +1,6 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/js.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/js.txt new file mode 100644 index 0000000000..9f3b9ebc04 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/js.txt @@ -0,0 +1,18 @@ +############################################################################### +# Copyright 2023 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 +common.js \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/js/common.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/js/common.js new file mode 100644 index 0000000000..0d112b1c02 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/js/common.js @@ -0,0 +1,260 @@ +/******************************************************************************* + * 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 () { + + function AccordionMixin(Base) { + return class extends Base { + static NS = "cmp"; + static IS = "adaptiveFormAccordion"; + static bemBlock = 'cmp-accordion'; + static selectors = { + self: "[data-" + this.NS + '-is="' + this.IS + '"]' + }; + + + static idSuffixes = { + item: "-item", + button: "-button", + panel: "-panel" + } + + static cacheKeys = { + buttonKey: "button", + panelKey: "panel", + itemKey: "item" + } + + static cssClasses = { + button: { + disabled: "cmp-accordion__button--disabled", + expanded: "cmp-accordion__button--expanded" + }, + panel: { + hidden: "cmp-accordion__panel--hidden", + expanded: "cmp-accordion__panel--expanded" + } + }; + + static delay = 100; + + static dataAttributes = { + item: { + expanded: "data-cmp-expanded" + } + }; + + constructor(params) { + super(params); + } + + getCachedItems() { + return (this._elements[this.constructor.cacheKeys.itemKey] != null) ? this._elements[this.constructor.cacheKeys.itemKey] : []; + } + + getCachedPanels() { + return this._elements[this.constructor.cacheKeys.panelKey]; + } + + getCachedButtons() { + return this._elements[this.constructor.cacheKeys.buttonKey] + } + + getItemById(itemId) { + var items = this.getCachedItems(); + if (items) { + for (var i = 0; i < items.length; i++) { + if (items[i].id === itemId) { + return items[i]; + } + } + } + } + + + /** + * Returns all expanded items. + * + * @private + * @returns {HTMLElement[]} The expanded items + */ + getExpandedItems() { + var expandedItems = []; + + for (var i = 0; i < this.getCachedItems().length; i++) { + var item = this.getCachedItems()[i]; + var expanded = this.isItemExpanded(item); + if (expanded) { + expandedItems.push(item); + } + } + + return expandedItems; + } + + /** + * Gets an item's expanded state. + * + * @private + * @param {HTMLElement} item The item for checking its expanded state + * @returns {Boolean} true if the item is expanded, false otherwise + */ + isItemExpanded(item) { + return item && item.dataset && item.dataset["cmpExpanded"] !== undefined; + } + + /** + * Caches the Accordion elements as defined via the {@code data-accordion-hook="ELEMENT_NAME"} markup API. + * + * @private + * @param {HTMLElement} wrapper The Accordion wrapper element + */ + cacheElements(wrapper) { + this._elements = {}; + this._elements.self = wrapper; + var hooks = this._elements.self.querySelectorAll("[data-" + this.constructor.NS + "-hook-" + this.constructor.IS + "]"); + + for (var i = 0; i < hooks.length; i++) { + var hook = hooks[i]; + if (hook.closest("[data-cmp-is=" + this.constructor.IS + "]") === this._elements.self) { // only process own accordion elements + var lowerCased = this.constructor.IS.toLowerCase(); + var capitalized = lowerCased.charAt(0).toUpperCase() + lowerCased.slice(1); + var key = hook.dataset[this.constructor.NS + "Hook" + capitalized]; + if (this._elements[key]) { + if (!Array.isArray(this._elements[key])) { + var tmp = this._elements[key]; + this._elements[key] = [tmp]; + } + this._elements[key].push(hook); + } else { + this._elements[key] = [hook]; + } + } + } + } + + collapseAllOtherItems(itemId) { + var itemToToggle = this.getItemById(itemId); + var itemList = this.getCachedItems(); + for (var i = 0; i < itemList.length; i++) { + if (itemList[i] !== itemToToggle) { + var expanded = this.isItemExpanded(itemList[i]); + if (expanded) { + this.collapseItem(this.getCachedItems()[i]); + } + } + } + } + + /** + * General handler for toggle of an item. + * + * @private + * @param {Number} id The id of the item to toggle + */ + toggle(id) { + var itemToToggle = this.getItemById(id); + if (itemToToggle) { + (this.isItemExpanded(itemToToggle) === false) ? this.expandItem(itemToToggle) : this.collapseItem(itemToToggle); + } + } + + + /** + * Refreshes an item based on its expanded state. + * + * @private + * @param {HTMLElement} item The item to refresh + */ + refreshItem(item) { + var expanded = this.isItemExpanded(item); + if (expanded) { + this.expandItem(item); + } else { + this.collapseItem(item); + } + } + + /** + * Refreshes all items based on their expanded state. + * + * @private + */ + refreshItems() { + for (var i = 0; i < this.getCachedItems().length; i++) { + this.refreshItem(this.getCachedItems()[i]); + } + } + + + /** + * Annotates the item and its internals with + * the necessary style and accessibility attributes to indicate it is expanded. + * + * @private + * @param {HTMLElement} item The item to annotate as expanded + */ + expandItem(item) { + var index = this.getCachedItems().indexOf(item); + if (index > -1) { + item.setAttribute(this.constructor.dataAttributes.item.expanded, ""); + var button = this.getCachedButtons()[index]; + var panel = this.getCachedPanels()[index]; + button.classList.add(this.constructor.cssClasses.button.expanded); + // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute + // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934 + setTimeout(function () { + button.setAttribute("aria-expanded", true); + }, this.constructor.delay); + panel.classList.add(this.constructor.cssClasses.panel.expanded); + panel.classList.remove(this.constructor.cssClasses.panel.hidden); + panel.setAttribute("aria-hidden", false); + } + } + + /** + * Annotates the item and its internals with + * the necessary style and accessibility attributes to indicate it is not expanded. + * + * @private + * @param {HTMLElement} item The item to annotate as not expanded + */ + collapseItem(item) { + var index = this.getCachedItems().indexOf(item); + if (index > -1) { + item.removeAttribute(this.constructor.dataAttributes.item.expanded); + var button = this.getCachedButtons()[index]; + var panel = this.getCachedPanels()[index]; + button.classList.remove(this.constructor.cssClasses.button.expanded); + // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute + // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934 + setTimeout(function () { + button.setAttribute("aria-expanded", false); + }, this.constructor.delay); + panel.classList.add(this.constructor.cssClasses.panel.hidden); + panel.classList.remove(this.constructor.cssClasses.panel.expanded); + panel.setAttribute("aria-hidden", true); + } + } + } + } + + window.Forms = window.Forms || {}; + window.Forms.CoreComponentsCommons = window.Forms.CoreComponentsCommons || {}; + window.Forms.CoreComponentsCommons.AccordionMixin = AccordionMixin; + +}()); diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/contentframe/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/contentframe/.content.xml new file mode 100644 index 0000000000..3f029c4283 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/contentframe/.content.xml @@ -0,0 +1,6 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/contentframe/js.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/contentframe/js.txt new file mode 100644 index 0000000000..093ea3890d --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/contentframe/js.txt @@ -0,0 +1,18 @@ +############################################################################### +# Copyright 2023 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 +accordion.js \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/contentframe/js/accordion.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/contentframe/js/accordion.js new file mode 100644 index 0000000000..145a0f21f3 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/contentframe/js/accordion.js @@ -0,0 +1,104 @@ +/******************************************************************************* + * 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 () { + + const AccordionMixin = window.Forms.CoreComponentsCommons.AccordionMixin; + + class Accordion extends AccordionMixin(class {}) { + + constructor(params) { + super(params); + const { element } = params; + this.cacheElements(element); + if (this.getCachedItems()) { + var expandedItems = this.getExpandedItems(); + // multiple expanded items annotated, display the last item open. + if (expandedItems.length > 1) { + var lastExpandedItem = expandedItems[expandedItems.length - 1] + this.expandItem(lastExpandedItem); + this.collapseAllOtherItems(lastExpandedItem.id); + } + this.refreshItems(); + } + element.removeAttribute("data-" + this.constructor.NS + "-is"); + + if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { + /* + * Editor message handling: + * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame + * - check that the message data panel container type is correct and that the id (path) matches this specific Accordion component + * - if so, route the "navigate" operation to enact a navigation of the Accordion based on index data + */ + window.CQ.CoreComponents.MESSAGE_CHANNEL = window.CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); + var _self = this; + window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function (message) { + if (message.data && message.data.type === "cmp-accordion" && message.data.id === _self._elements.self.dataset["cmpPanelcontainerId"]) { + if (message.data.operation === "navigate" && _self.getCachedItems()[message.data.index] !== undefined) { + _self.toggle(_self.getCachedItems()[message.data.index].id); + _self.collapseAllOtherItems(_self.getCachedItems()[message.data.index].id); + } + } + }); + } + } + } + + /** + * Document ready handler and DOM mutation observers. Initializes Tabs components as necessary. + * + * @private + */ + function onDocumentReady() { + + var elements = document.querySelectorAll(Accordion.selectors.self); + for (var i = 0; i < elements.length; i++) { + new Accordion({ element: elements[i] }); + } + + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; + var body = document.querySelector("body"); + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + // needed for IE + var nodesArray = [].slice.call(mutation.addedNodes); + if (nodesArray.length > 0) { + nodesArray.forEach(function(addedNode) { + if (addedNode.querySelectorAll) { + var elementsArray = [].slice.call(addedNode.querySelectorAll(Accordion.selectors.self)); + elementsArray.forEach(function(element) { + new Accordion({ element: element }); + }); + } + }); + } + }); + }); + + observer.observe(body, { + subtree: true, + childList: true, + characterData: true + }); + } + + if (document.readyState !== "loading") { + onDocumentReady(); + } else { + document.addEventListener("DOMContentLoaded", onDocumentReady); + } +}()); diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/site/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/site/.content.xml index e052b09882..b9ceb86eb4 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/site/.content.xml +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/site/.content.xml @@ -19,4 +19,4 @@ allowProxy="{Boolean}true" categories="[core.forms.components.accordion.v1.runtime]" jsProcessor="[default:none,min:none]" - dependencies="[core.wcm.components.commons.site.container,core.forms.components.runtime.base,core.forms.components.container.v2.runtime,core.wcm.components.accordion.v1]"/> + dependencies="[core.wcm.components.commons.site.container,core.forms.components.runtime.base,core.forms.components.container.v2.runtime,core.forms.components.accordion.v1.commons]"/> diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/site/js/accordionview.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/site/js/accordionview.js index c624dea2a6..a31e0146f8 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/site/js/accordionview.js +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/site/js/accordionview.js @@ -15,11 +15,11 @@ ******************************************************************************/ (function () { - class Accordion extends FormView.FormPanel { + const AccordionMixin = window.Forms.CoreComponentsCommons.AccordionMixin; + + class Accordion extends AccordionMixin(FormView.FormPanel) { static NS = FormView.Constants.NS; - static IS = "adaptiveFormAccordion"; - static bemBlock = 'cmp-accordion'; static DATA_ATTRIBUTE_VISIBLE = 'data-cmp-visible'; _templateHTML = {}; static selectors = { @@ -32,29 +32,6 @@ item: `.${Accordion.bemBlock}__item` }; - static idSuffixes = { - item: "-item", - button: "-button", - panel: "-panel" - } - - static cacheKeys = { - buttonKey: "button", - panelKey: "panel", - itemKey: "item" - } - - static cssClasses = { - button: { - disabled: "cmp-accordion__button--disabled", - expanded: "cmp-accordion__button--expanded" - }, - panel: { - hidden: "cmp-accordion__panel--hidden", - expanded: "cmp-accordion__panel--expanded" - } - }; - static keyCodes = { ENTER: 13, SPACE: 32, @@ -66,47 +43,21 @@ ARROW_DOWN: 40 }; - static delay = 100; - - static dataAttributes = { - item: { - expanded: "data-cmp-expanded" - } - }; - constructor(params) { super(params); const {element} = params; - this.#cacheElements(element); - if (this.#getCachedItems()) { - var expandedItems = this.#getExpandedItems(); + this.cacheElements(element); + if (this.getCachedItems()) { + var expandedItems = this.getExpandedItems(); // multiple expanded items annotated, display the last item open. if (expandedItems.length > 1) { var lastExpandedItem = expandedItems[expandedItems.length - 1] - this.#expandItem(lastExpandedItem); - this.#collapseAllOtherItems(lastExpandedItem.id); + this.expandItem(lastExpandedItem); + this.collapseAllOtherItems(lastExpandedItem.id); } - this.#refreshItems(); + this.refreshItems(); this.#bindEvents(); } - if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { - /* - * Editor message handling: - * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame - * - check that the message data panel container type is correct and that the id (path) matches this specific Accordion component - * - if so, route the "navigate" operation to enact a navigation of the Accordion based on index data - */ - window.CQ.CoreComponents.MESSAGE_CHANNEL = window.CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); - var _self = this; - window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function (message) { - if (message.data && message.data.type === "cmp-accordion" && message.data.id === _self._elements.self.dataset["cmpPanelcontainerId"]) { - if (message.data.operation === "navigate") { - _self.#toggle(_self.#getCachedItems()[message.data.index].id); - _self.#collapseAllOtherItems(_self.#getCachedItems()[message.data.index].id); - } - } - }); - } } getClass() { @@ -141,98 +92,37 @@ super.setFocus(id); this.setActive(); this.#collapseAllItems(); - const item = this.#getItemById(id + '-item'); - this.#expandItem(item) + const item = this.getItemById(id + '-item'); + this.expandItem(item) } #collapseAllItems() { - var items = this.#getCachedItems(); + var items = this.getCachedItems(); if (items) { for (var i = 0; i < items.length; i++) { - if (this.#isItemExpanded(items[i])) { - this.#collapseItem(items[i]) - } - } - } - } - - /** - * Caches the Accordion elements as defined via the {@code data-accordion-hook="ELEMENT_NAME"} markup API. - * - * @private - * @param {HTMLElement} wrapper The Accordion wrapper element - */ - #cacheElements(wrapper) { - this._elements = {}; - this._elements.self = wrapper; - var hooks = this._elements.self.querySelectorAll("[data-" + Accordion.NS + "-hook-" + Accordion.IS + "]"); - - for (var i = 0; i < hooks.length; i++) { - var hook = hooks[i]; - if (hook.closest("[data-cmp-is=" + Accordion.IS + "]") === this._elements.self) { // only process own accordion elements - var lowerCased = Accordion.IS.toLowerCase(); - var capitalized = lowerCased.charAt(0).toUpperCase() + lowerCased.slice(1); - var key = hook.dataset[Accordion.NS + "Hook" + capitalized]; - if (this._elements[key]) { - if (!Array.isArray(this._elements[key])) { - var tmp = this._elements[key]; - this._elements[key] = [tmp]; - } - this._elements[key].push(hook); - } else { - this._elements[key] = [hook]; + if (this.isItemExpanded(items[i])) { + this.collapseItem(items[i]) } } } } - /** - * Returns all expanded items. - * - * @private - * @returns {HTMLElement[]} The expanded items - */ - #getExpandedItems() { - var expandedItems = []; - - for (var i = 0; i < this.#getCachedItems().length; i++) { - var item = this.#getCachedItems()[i]; - var expanded = this.#isItemExpanded(item); - if (expanded) { - expandedItems.push(item); - } - } - - return expandedItems; - } - - /** - * Gets an item's expanded state. - * - * @private - * @param {HTMLElement} item The item for checking its expanded state - * @returns {Boolean} true if the item is expanded, false otherwise - */ - #isItemExpanded(item) { - return item && item.dataset && item.dataset["cmpExpanded"] !== undefined; - } - /** * Binds Accordion event handling. * * @private */ #bindEvents() { - var buttons = this.#getCachedButtons(); + var buttons = this.getCachedButtons(); if (buttons) { var _self = this; for (var i = 0; i < buttons.length; i++) { (function (index) { buttons[index].addEventListener("click", function (event) { var itemDivId = _self.#convertToItemDivId(buttons[index].id); - _self.#toggle(itemDivId); - _self.#collapseAllOtherItems(itemDivId); + _self.toggle(itemDivId); + _self.collapseAllOtherItems(itemDivId); _self.#focusButton(buttons[index].id); }); buttons[index].addEventListener("keydown", function (event) { @@ -254,8 +144,8 @@ var button = this.#getButtonById(buttonId); button.addEventListener("click", function (event) { var itemDivId = _self.#convertToItemDivId(buttonId); - _self.#toggle(itemDivId); - _self.#collapseAllOtherItems(itemDivId); + _self.toggle(itemDivId); + _self.collapseAllOtherItems(itemDivId); _self.#focusButton(buttonId); }); button.addEventListener("keydown", function (event) { @@ -271,7 +161,7 @@ * @param {Number} id The id of the button triggering the event */ #onButtonKeyDown(event, id) { - var buttons = this.#getCachedButtons(); + var buttons = this.getCachedButtons(); var lastIndex = buttons.length - 1; var index = this.#getButtonIndexById(id); @@ -302,8 +192,8 @@ case Accordion.keyCodes.SPACE: event.preventDefault(); var itemDivId = this.#convertToItemDivId(buttons[index].id); - this.#toggle(itemDivId); - this.#collapseAllOtherItems(itemDivId); + this.toggle(itemDivId); + this.collapseAllOtherItems(itemDivId); this.#focusButton(buttons[index].id); break; default: @@ -311,96 +201,6 @@ } } - /** - * General handler for toggle of an item. - * - * @private - * @param {Number} id The id of the item to toggle - */ - #toggle(id) { - var itemToToggle = this.#getItemById(id); - if (itemToToggle) { - (this.#isItemExpanded(itemToToggle) === false) ? this.#expandItem(itemToToggle) : this.#collapseItem(itemToToggle); - } - } - - /** - * Refreshes an item based on its expanded state. - * - * @private - * @param {HTMLElement} item The item to refresh - */ - #refreshItem(item) { - var expanded = this.#isItemExpanded(item); - if (expanded) { - this.#expandItem(item); - } else { - this.#collapseItem(item); - } - } - - /** - * Refreshes all items based on their expanded state. - * - * @private - */ - #refreshItems() { - for (var i = 0; i < this.#getCachedItems().length; i++) { - this.#refreshItem(this.#getCachedItems()[i]); - } - } - - - /** - * Annotates the item and its internals with - * the necessary style and accessibility attributes to indicate it is expanded. - * - * @private - * @param {HTMLElement} item The item to annotate as expanded - */ - #expandItem(item) { - var index = this.#getCachedItems().indexOf(item); - if (index > -1) { - item.setAttribute(Accordion.dataAttributes.item.expanded, ""); - var button = this.#getCachedButtons()[index]; - var panel = this.#getCachedPanels()[index]; - button.classList.add(Accordion.cssClasses.button.expanded); - // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute - // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934 - setTimeout(function () { - button.setAttribute("aria-expanded", true); - }, Accordion.delay); - panel.classList.add(Accordion.cssClasses.panel.expanded); - panel.classList.remove(Accordion.cssClasses.panel.hidden); - panel.setAttribute("aria-hidden", false); - } - } - - /** - * Annotates the item and its internals with - * the necessary style and accessibility attributes to indicate it is not expanded. - * - * @private - * @param {HTMLElement} item The item to annotate as not expanded - */ - #collapseItem(item) { - var index = this.#getCachedItems().indexOf(item); - if (index > -1) { - item.removeAttribute(Accordion.dataAttributes.item.expanded); - var button = this.#getCachedButtons()[index]; - var panel = this.#getCachedPanels()[index]; - button.classList.remove(Accordion.cssClasses.button.expanded); - // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute - // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934 - setTimeout(function () { - button.setAttribute("aria-expanded", false); - }, Accordion.delay); - panel.classList.add(Accordion.cssClasses.panel.hidden); - panel.classList.remove(Accordion.cssClasses.panel.expanded); - panel.setAttribute("aria-hidden", true); - } - } - /** * Focuses the button at the provided index. * @@ -446,31 +246,31 @@ handleChildAddition(childView) { var itemDivToExpand; - this.#cacheElements(this._elements.self); + this.cacheElements(this._elements.self); this.#bindEventsToAddedChild(childView.id); if (childView.getInstanceManager().getModel().minOccur != undefined && childView.getInstanceManager().children.length > childView.getInstanceManager().getModel().minOccur) { - itemDivToExpand = this.#getItemById(childView.id + Accordion.idSuffixes.item); + itemDivToExpand = this.getItemById(childView.id + Accordion.idSuffixes.item); } else { //this will run at initial runtime loading when the repeatable panel is being added minOccur no of times. // in this case we want the focus to stay at first tab - itemDivToExpand = this.findFirstVisibleChild(this.#getCachedItems()); + itemDivToExpand = this.findFirstVisibleChild(this.getCachedItems()); } - this.#expandItem(itemDivToExpand); - this.#collapseAllOtherItems(itemDivToExpand.id); + this.expandItem(itemDivToExpand); + this.collapseAllOtherItems(itemDivToExpand.id); this.#showHideRepeatableButtons(childView.getInstanceManager()); } handleChildRemoval(removedInstanceView) { var removedAccordionItemDivId = removedInstanceView.element.id + Accordion.idSuffixes.item; - var removedAccordionItemDiv = this.#getItemById(removedAccordionItemDivId); + var removedAccordionItemDiv = this.getItemById(removedAccordionItemDivId); removedAccordionItemDiv.remove(); this.children.splice(this.children.indexOf(removedInstanceView), 1); - this.#cacheElements(this._elements.self); - var cachedItems = this.#getCachedItems(); + this.cacheElements(this._elements.self); + var cachedItems = this.getCachedItems(); if (cachedItems && cachedItems.length > 0) { var firstItem = cachedItems[0]; - this.#expandItem(firstItem); - this.#collapseAllOtherItems(firstItem.id); + this.expandItem(firstItem); + this.collapseAllOtherItems(firstItem.id); } this.#showHideRepeatableButtons(removedInstanceView.getInstanceManager()); } @@ -485,13 +285,13 @@ syncMarkupWithModel() { super.syncMarkupWithModel(); this.#syncLabel(); - for (var itemDiv of this.#getCachedItems()) { + for (var itemDiv of this.getCachedItems()) { this.#syncAccordionMarkup(itemDiv); } } getChildViewByIndex(index) { - var accordionPanels = this.#getCachedPanels(); + var accordionPanels = this.getCachedPanels(); var fieldId = accordionPanels[index].id.substring(0, accordionPanels[index].id.lastIndexOf("-")); return this.getChild(fieldId); } @@ -504,7 +304,7 @@ var closestRepeatableFieldInstanceManagerIds = this._templateHTML[instanceManagerId]['closestRepeatableFieldInstanceManagerIds']; var indexToInsert = this.getIndexToInsert(closestNonRepeatableFieldId, closestRepeatableFieldInstanceManagerIds); if (indexToInsert > 0) { - result.beforeViewElement = this.#getCachedItems()[indexToInsert - 1]; + result.beforeViewElement = this.getCachedItems()[indexToInsert - 1]; } else { result.parentElement = this.element; } @@ -559,51 +359,14 @@ if (childView.getInstanceManager() != null && (this._templateHTML == null || this._templateHTML[childView.getInstanceManager().getId()] == null)) { var accordionItemDivId = childView.element.id + Accordion.idSuffixes.item; var instanceManagerId = childView.getInstanceManager().getId(); - var accordionItemDiv = this.#getItemById(accordionItemDivId); + var accordionItemDiv = this.getItemById(accordionItemDivId); this._templateHTML[instanceManagerId] = {}; this._templateHTML[instanceManagerId]['accordionItemDiv'] = accordionItemDiv; } } - #collapseAllOtherItems(itemId) { - var itemToToggle = this.#getItemById(itemId); - var itemList = this.#getCachedItems(); - for (var i = 0; i < itemList.length; i++) { - if (itemList[i] !== itemToToggle) { - var expanded = this.#isItemExpanded(itemList[i]); - if (expanded) { - this.#collapseItem(this.#getCachedItems()[i]); - } - } - } - } - - - #getCachedItems() { - return (this._elements[Accordion.cacheKeys.itemKey] != null) ? this._elements[Accordion.cacheKeys.itemKey] : []; - } - - #getCachedPanels() { - return this._elements[Accordion.cacheKeys.panelKey]; - } - - #getCachedButtons() { - return this._elements[Accordion.cacheKeys.buttonKey] - } - - #getItemById(itemId) { - var items = this.#getCachedItems(); - if (items) { - for (var i = 0; i < items.length; i++) { - if (items[i].id === itemId) { - return items[i]; - } - } - } - } - #getButtonById(buttonId) { - var buttons = this.#getCachedButtons(); + var buttons = this.getCachedButtons(); if (buttons) { for (var i = 0; i < buttons.length; i++) { if (buttons[i].id === buttonId) { @@ -614,7 +377,7 @@ } #getButtonIndexById(buttonId) { - var buttons = this.#getCachedButtons(); + var buttons = this.getCachedButtons(); if (buttons) { for (var i = 0; i < buttons.length; i++) { if (buttons[i].id === buttonId) { @@ -656,17 +419,17 @@ } updateChildVisibility(visible, state) { - this.updateVisibilityOfNavigationElement(this.#getItemById(state.id + Accordion.idSuffixes.item), visible); + this.updateVisibilityOfNavigationElement(this.getItemById(state.id + Accordion.idSuffixes.item), visible); if (!visible) { - var expandedItems = this.#getExpandedItems(); + var expandedItems = this.getExpandedItems(); for (let i = 0; i < expandedItems.length; i++) { if (expandedItems[i].getAttribute(Accordion.DATA_ATTRIBUTE_VISIBLE) === 'false') { - this.#collapseItem(expandedItems[i]); + this.collapseItem(expandedItems[i]); } } - let child = this.findFirstVisibleChild(this.#getCachedItems()); + let child = this.findFirstVisibleChild(this.getCachedItems()); if (child) { - this.#expandItem(child); + this.expandItem(child); } } } diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/checkbox/v1/checkbox/_cq_dialog/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/checkbox/v1/checkbox/_cq_dialog/.content.xml index c2c65748b0..4af47cd900 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/checkbox/v1/checkbox/_cq_dialog/.content.xml +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/checkbox/v1/checkbox/_cq_dialog/.content.xml @@ -162,7 +162,7 @@ + value="label"/> + value="label"/> + value="label"/> + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/contentframe/js.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/contentframe/js.txt new file mode 100644 index 0000000000..7cb945cd83 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/contentframe/js.txt @@ -0,0 +1,18 @@ +############################################################################### +# Copyright 2023 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 +tabs.js \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/contentframe/js/tabs.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/contentframe/js/tabs.js new file mode 100644 index 0000000000..be37e3d278 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/contentframe/js/tabs.js @@ -0,0 +1,101 @@ +/******************************************************************************* + * 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 () { + + const TabsMixin = window.Forms.CoreComponentsCommons.TabsMixin; + + class Tabs extends TabsMixin(class {}) { + static NS = "cmp"; + static IS = "adaptiveFormTabs"; + static bemBlock = "cmp-tabs"; + static selectors = { + self: "[data-" + this.NS + '-is="' + this.IS + '"]', + active: { + tab: "cmp-tabs__tab--active", + tabpanel: "cmp-tabs__tabpanel--active" + }, + }; + + constructor(params) { + super(params, Tabs.NS, Tabs.IS, Tabs.selectors) + params.element.removeAttribute("data-" + Tabs.NS + "-is"); + + if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { + /* + * Editor message handling: + * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame + * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component + * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data + */ + CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); + var _self = this; + CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function (message) { + if (message.data && message.data.type === "cmp-tabs" && message.data.id === _self._elements.self.dataset["cmpPanelcontainerId"]) { + if (message.data.operation === "navigate" && _self._elements["tab"][message.data.index] !== undefined) { + _self.navigate(_self._elements["tab"][message.data.index].id); + } + } + }); + } + } + } + + /** + * Document ready handler and DOM mutation observers. Initializes Tabs components as necessary. + * + * @private + */ + function onDocumentReady() { + + var elements = document.querySelectorAll(Tabs.selectors.self); + for (var i = 0; i < elements.length; i++) { + new Tabs({ element: elements[i] }); + } + + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; + var body = document.querySelector("body"); + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + // needed for IE + var nodesArray = [].slice.call(mutation.addedNodes); + if (nodesArray.length > 0) { + nodesArray.forEach(function(addedNode) { + if (addedNode.querySelectorAll) { + var elementsArray = [].slice.call(addedNode.querySelectorAll(Tabs.selectors.self)); + elementsArray.forEach(function(element) { + new Tabs({ element: element }); + }); + } + }); + } + }); + }); + + observer.observe(body, { + subtree: true, + childList: true, + characterData: true + }); + } + + if (document.readyState !== "loading") { + onDocumentReady(); + } else { + document.addEventListener("DOMContentLoaded", onDocumentReady); + } +}()); diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/site/js/tabs.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/site/js/tabs.js index 13c323cb4f..4dd738e70b 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/site/js/tabs.js +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/clientlibs/site/js/tabs.js @@ -37,23 +37,6 @@ constructor(params) { super(params, Tabs.NS, Tabs.IS, Tabs.selectors); - if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { - /* - * Editor message handling: - * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame - * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component - * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data - */ - CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); - var _self = this; - CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function (message) { - if (message.data && message.data.type === "cmp-tabs" && message.data.id === _self._elements.self.dataset["cmpPanelcontainerId"]) { - if (message.data.operation === "navigate" && _self._elements["tab"][message.data.index] != undefined) { - _self.navigate(_self._elements["tab"][message.data.index].id); - } - } - }); - } } diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/tabsontop.html b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/tabsontop.html index ab946ee8f5..c86f0a19c5 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/tabsontop.html +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tabsontop/v1/tabsontop/tabsontop.html @@ -63,6 +63,6 @@ data-sly-test="${(wcmmode.edit || wcmmode.preview)}"> - + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/contentframe/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/contentframe/.content.xml new file mode 100644 index 0000000000..edb9403aa3 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/contentframe/.content.xml @@ -0,0 +1,6 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/contentframe/js.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/contentframe/js.txt new file mode 100644 index 0000000000..2702421cd2 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/contentframe/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 +verticaltabs.js \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/contentframe/js/verticaltabs.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/contentframe/js/verticaltabs.js new file mode 100644 index 0000000000..4db1b36749 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/contentframe/js/verticaltabs.js @@ -0,0 +1,101 @@ +/******************************************************************************* + * 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 () { + + const TabsMixin = window.Forms.CoreComponentsCommons.TabsMixin; + + class VerticalTabs extends TabsMixin(class {}) { + static NS = "cmp"; + static IS = "adaptiveFormVerticalTabs"; + static bemBlock = "cmp-verticaltabs"; + static selectors = { + self: "[data-" + this.NS + '-is="' + this.IS + '"]', + active: { + tab: "cmp-verticaltabs__tab--active", + tabpanel: "cmp-verticaltabs__tabpanel--active" + }, + }; + + constructor(params) { + super(params, VerticalTabs.NS, VerticalTabs.IS, VerticalTabs.selectors) + params.element.removeAttribute("data-" + VerticalTabs.NS + "-is"); + + if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { + /* + * Editor message handling: + * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame + * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component + * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data + */ + CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); + var _self = this; + CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function (message) { + if (message.data && message.data.type === "cmp-verticaltabs" && message.data.id === _self._elements.self.dataset["cmpPanelcontainerId"]) { + if (message.data.operation === "navigate" && _self._elements["tab"][message.data.index] !== undefined) { + _self.navigate(_self._elements["tab"][message.data.index].id); + } + } + }); + } + } + } + + /** + * Document ready handler and DOM mutation observers. Initializes Tabs components as necessary. + * + * @private + */ + function onDocumentReady() { + + var elements = document.querySelectorAll(VerticalTabs.selectors.self); + for (var i = 0; i < elements.length; i++) { + new VerticalTabs({ element: elements[i] }); + } + + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; + var body = document.querySelector("body"); + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + // needed for IE + var nodesArray = [].slice.call(mutation.addedNodes); + if (nodesArray.length > 0) { + nodesArray.forEach(function(addedNode) { + if (addedNode.querySelectorAll) { + var elementsArray = [].slice.call(addedNode.querySelectorAll(VerticalTabs.selectors.self)); + elementsArray.forEach(function(element) { + new VerticalTabs({ element: element }); + }); + } + }); + } + }); + }); + + observer.observe(body, { + subtree: true, + childList: true, + characterData: true + }); + } + + if (document.readyState !== "loading") { + onDocumentReady(); + } else { + document.addEventListener("DOMContentLoaded", onDocumentReady); + } +}()); diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/site/js/verticaltabs.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/site/js/verticaltabs.js index 3b272b089d..69667ae682 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/site/js/verticaltabs.js +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/clientlibs/site/js/verticaltabs.js @@ -37,23 +37,6 @@ constructor(params) { super(params, VerticalTabs.NS, VerticalTabs.IS, VerticalTabs.selectors); - if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { - /* - * Editor message handling: - * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame - * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component - * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data - */ - CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); - var _self = this; - CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function (message) { - if (message.data && message.data.type === "cmp-verticaltabs" && message.data.id === _self._elements.self.dataset["cmpPanelcontainerId"]) { - if (message.data.operation === "navigate" && _self._elements["tab"][message.data.index] != undefined) { - _self.navigate(_self._elements["tab"][message.data.index].id); - } - } - }); - } } getClass() { @@ -63,7 +46,9 @@ setFocus(id) { super.setFocus(id); this.setActive(); - this.navigateAndFocusTab(id + '__tab'); + if(id) { + this.navigateAndFocusTab(id + '__tab'); + } } getWidget() { diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/verticaltabs.html b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/verticaltabs.html index e6c810e3df..ea042f2561 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/verticaltabs.html +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/verticaltabs/v1/verticaltabs/verticaltabs.html @@ -64,6 +64,6 @@ data-sly-test="${(wcmmode.edit || wcmmode.preview)}"> - + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/commons/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/commons/.content.xml new file mode 100644 index 0000000000..118c0b592c --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/commons/.content.xml @@ -0,0 +1,6 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/commons/js.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/commons/js.txt new file mode 100644 index 0000000000..90e023d251 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/commons/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 +common.js \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/commons/js/common.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/commons/js/common.js new file mode 100644 index 0000000000..061e27e306 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/commons/js/common.js @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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 () { + + function WizardMixin(Base) { + return class extends Base { + static NS = "cmp"; + static IS = "adaptiveFormWizard"; + static bemBlock = "cmp-adaptiveform-wizard"; + static DATA_ATTRIBUTE_VISIBLE = 'data-cmp-visible'; + + static selectors = { + self: "[data-" + this.NS + '-is="' + this.IS + '"]', + active: { + tab: "cmp-adaptiveform-wizard__tab--active", + wizardpanel: "cmp-adaptiveform-wizard__wizardpanel--active" + } + }; + + _active; + + constructor(params) { + super(params); + } + + /** + * Caches the Tabs elements as defined via the {@code data-tabs-hook="ELEMENT_NAME"} markup API + * + * @private + * @param {HTMLElement} wrapper The Tabs wrapper element + */ + cacheElements(wrapper) { + this._elements = {}; + this._elements.self = wrapper; + const hooks = this._elements.self.querySelectorAll("[data-" + this.constructor.NS + "-hook-" + this.constructor.IS + "]"); + + for (let i = 0; i < hooks.length; i++) { + let hook = hooks[i]; + if (hook.closest("[data-cmp-is=" + this.constructor.IS + "]") === this._elements.self) { // only process own tab elements + let key = hook.dataset[this.constructor.NS + "Hook" + "Adaptiveformwizard"]; + if (this._elements[key]) { + if (!Array.isArray(this._elements[key])) { + let tmp = this._elements[key]; + this._elements[key] = [tmp]; + } + this._elements[key].push(hook); + } else { + this._elements[key] = [hook]; + } + } + } + } + + setActive(tabs) { + if (tabs) { + tabs[0].classList.add(this.constructor.selectors.active.tab); + } + } + + /** + * Returns the index of the active tab, if no tab is active returns 0 + * + * @param {Array} tabs Tab elements + * @returns {Number} Index of the active tab, 0 if none is active + */ + getActiveIndex(tabs) { + if (tabs) { + for (let i = 0; i < tabs.length; i++) { + if (tabs[i].classList.contains(this.constructor.selectors.active.tab)) { + return i; + } + } + } + return 0; + } + + getCachedTabs() { + return this._elements["tab"]; + } + + getCachedWizardPanels() { + return this._elements["wizardpanel"] + } + + /** + * Navigates to the tab at the provided index + * + * @private + * @param {Number} index The index of the tab to navigate to + */ + navigate(index) { + this._active = index; + this.refreshActive(); + } + + /** + * Refreshes the tab markup based on the current {@code Tabs_active} index + * + * @private + */ + refreshActive() { + const wizardPanels = this.getCachedWizardPanels(); + const tabs = this.getCachedTabs(); + if (wizardPanels) { + for (let i = 0; i < wizardPanels.length; i++) { + if( wizardPanels[i] && tabs[i]) { + if (i === parseInt(this._active)) { + wizardPanels[i].classList.add(this.constructor.selectors.active.wizardpanel); + wizardPanels[i].removeAttribute("aria-hidden"); + tabs[i].classList.add(this.constructor.selectors.active.tab); + tabs[i].setAttribute("aria-selected", true); + tabs[i].setAttribute("tabindex", "0"); + } else { + wizardPanels[i].classList.remove(this.constructor.selectors.active.wizardpanel); + wizardPanels[i].setAttribute("aria-hidden", true); + tabs[i].classList.remove(this.constructor.selectors.active.tab); + tabs[i].setAttribute("aria-selected", false); + tabs[i].setAttribute("tabindex", "-1"); + } + } + } + } + if (this.hideUnhideNavButtons) { + this.hideUnhideNavButtons(this._active); + } + } + } + } + + window.Forms = window.Forms || {}; + window.Forms.CoreComponentsCommons = window.Forms.CoreComponentsCommons || {}; + window.Forms.CoreComponentsCommons.WizardMixin = WizardMixin; + +}()); diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/contentframe/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/contentframe/.content.xml new file mode 100644 index 0000000000..abbbd1c97a --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/contentframe/.content.xml @@ -0,0 +1,6 @@ + + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/contentframe/js.txt b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/contentframe/js.txt new file mode 100644 index 0000000000..fd5429b6f7 --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/contentframe/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 +wizard.js \ No newline at end of file diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/contentframe/js/wizard.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/contentframe/js/wizard.js new file mode 100644 index 0000000000..bb8a2932ef --- /dev/null +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/contentframe/js/wizard.js @@ -0,0 +1,97 @@ +/******************************************************************************* + * 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 () { + + const WizardMixin = window.Forms.CoreComponentsCommons.WizardMixin; + + class Wizard extends WizardMixin(class {}) { + + constructor(params) { + super(params); + const {element} = params; + this.cacheElements(element); + this.setActive(this.getCachedTabs()) + this._active = this.getActiveIndex(this.getCachedTabs()); + this.refreshActive(); + + element.removeAttribute("data-" + Wizard.NS + "-is"); + + if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { + /* + * Editor message handling: + * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame + * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component + * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data + */ + CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); + const _self = this; + CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function (message) { + if (message.data && message.data.type === "cmp-adaptiveform-wizard" && message.data.id === _self._elements.self.dataset["cmpPanelcontainerId"]) { + if (message.data.operation === "navigate") { + _self.navigate(message.data.index); + } + } + }); + } + } + } + + /** + * Document ready handler and DOM mutation observers. Initializes Tabs components as necessary. + * + * @private + */ + function onDocumentReady() { + + var elements = document.querySelectorAll(Wizard.selectors.self); + for (var i = 0; i < elements.length; i++) { + new Wizard({ element: elements[i] }); + } + + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; + var body = document.querySelector("body"); + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + // needed for IE + var nodesArray = [].slice.call(mutation.addedNodes); + if (nodesArray.length > 0) { + nodesArray.forEach(function(addedNode) { + if (addedNode.querySelectorAll) { + var elementsArray = [].slice.call(addedNode.querySelectorAll(Wizard.selectors.self)); + elementsArray.forEach(function(element) { + new Wizard({ element: element }); + }); + } + }); + } + }); + }); + + observer.observe(body, { + subtree: true, + childList: true, + characterData: true + }); + } + + if (document.readyState !== "loading") { + onDocumentReady(); + } else { + document.addEventListener("DOMContentLoaded", onDocumentReady); + } +}()); diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/site/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/site/.content.xml index ca7663dbfc..f783007439 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/site/.content.xml +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/site/.content.xml @@ -19,5 +19,5 @@ allowProxy="{Boolean}true" categories="[core.forms.components.wizard.v1.runtime]" jsProcessor="[default:none,min:none]" - dependencies="[core.forms.components.runtime.base,core.forms.components.container.v2.runtime,core.wcm.components.commons.site.container]" + dependencies="[core.forms.components.runtime.base,core.forms.components.container.v2.runtime,core.wcm.components.commons.site.container,core.forms.components.wizard.v1.commons]" /> diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/site/js/wizardview.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/site/js/wizardview.js index 815bd476fa..903a16b793 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/site/js/wizardview.js +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/site/js/wizardview.js @@ -25,20 +25,15 @@ ARROW_DOWN: 40 }; + const WizardMixin = window.Forms.CoreComponentsCommons.WizardMixin; - class Wizard extends FormView.FormPanel { + class Wizard extends WizardMixin(FormView.FormPanel) { _templateHTML = {}; - #_active; static NS = FormView.Constants.NS; - static IS = "adaptiveFormWizard"; - static bemBlock = "cmp-adaptiveform-wizard"; static #tabIdSuffix = "_wizard-item-nav"; static #wizardPanelIdSuffix = "__wizardpanel"; - maxEnabledTab = 0; - minEnabledTab = 0; - static DATA_ATTRIBUTE_VISIBLE = 'data-cmp-visible'; static selectors = { self: "[data-" + Wizard.NS + '-is="' + Wizard.IS + '"]', @@ -63,59 +58,14 @@ constructor(params) { super(params); const {element} = params; - this.#cacheElements(element); - this.#setActive(this.#getCachedTabs()) - this.#_active = this.#getActiveIndex(this.#getCachedTabs()); + this.cacheElements(element); + this.setActive(this.getCachedTabs()) + this._active = this.getActiveIndex(this.getCachedTabs()); this.#setNavigationRange(); - this.#hideUnhideNavButtons(this.#_active); - this.#refreshActive(); + this.#hideUnhideNavButtons(this._active); + this.refreshActive(); this.#bindEvents(); - if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { - /* - * Editor message handling: - * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame - * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component - * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data - */ - CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); - const _self = this; - CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function (message) { - if (message.data && message.data.type === "cmp-adaptiveform-wizard" && message.data.id === _self._elements.self.dataset["cmpPanelcontainerId"]) { - if (message.data.operation === "navigate") { - _self.#navigate(message.data.index); - } - } - }); - } - } - - /** - * Caches the Tabs elements as defined via the {@code data-tabs-hook="ELEMENT_NAME"} markup API - * - * @private - * @param {HTMLElement} wrapper The Tabs wrapper element - */ - #cacheElements(wrapper) { - this._elements = {}; - this._elements.self = wrapper; - const hooks = this._elements.self.querySelectorAll("[data-" + Wizard.NS + "-hook-" + Wizard.IS + "]"); - - for (let i = 0; i < hooks.length; i++) { - let hook = hooks[i]; - if (hook.closest("[data-cmp-is=" + Wizard.IS + "]") === this._elements.self) { // only process own tab elements - let key = hook.dataset[Wizard.NS + "Hook" + "Adaptiveformwizard"]; - if (this._elements[key]) { - if (!Array.isArray(this._elements[key])) { - let tmp = this._elements[key]; - this._elements[key] = [tmp]; - } - this._elements[key].push(hook); - } else { - this._elements[key] = [hook]; - } - } - } } getClass() { @@ -126,7 +76,7 @@ super.setFocus(id); this.setActive(); const index = this.#getTabIndexById(id + '_wizard-item-nav'); - this.#navigate(index); + this.navigate(index); } getWidget() { @@ -183,30 +133,6 @@ } - /** - * Returns the index of the active tab, if no tab is active returns 0 - * - * @param {Array} tabs Tab elements - * @returns {Number} Index of the active tab, 0 if none is active - */ - #getActiveIndex(tabs) { - if (tabs) { - for (let i = 0; i < tabs.length; i++) { - if (tabs[i].classList.contains(Wizard.selectors.active.tab)) { - return i; - } - } - } - return 0; - } - - - #setActive(tabs) { - if (tabs) { - tabs[0].classList.add(Wizard.selectors.active.tab); - } - } - /** * Handles tab keydown events * @@ -214,9 +140,9 @@ * @param {Object} event The keydown event */ #onKeyDown(event) { - const index = this.#_active; + const index = this._active; - const lastIndex = this.#getCachedTabs().length - 1; + const lastIndex = this.getCachedTabs().length - 1; switch (event.keyCode) { case keyCodes.ARROW_LEFT: @@ -247,33 +173,13 @@ } /** - * Refreshes the tab markup based on the current {@code Tabs#_active} index + * Refreshes the tab markup based on the current {@code Tabs_active} index * * @private */ - #refreshActive() { - const wizardPanels = this.#getCachedWizardPanels(); - const tabs = this.#getCachedTabs(); - if (wizardPanels) { - for (let i = 0; i < wizardPanels.length; i++) { - if(tabs[i]) { - if (i === parseInt(this.#_active)) { - wizardPanels[i].classList.add(Wizard.selectors.active.wizardpanel); - wizardPanels[i].removeAttribute(FormView.Constants.ARIA_HIDDEN); - tabs[i].classList.add(Wizard.selectors.active.tab); - tabs[i].setAttribute(FormView.Constants.ARIA_SELECTED, true); - tabs[i].setAttribute(FormView.Constants.TABINDEX, "0"); - } else { - wizardPanels[i].classList.remove(Wizard.selectors.active.wizardpanel); - wizardPanels[i].setAttribute(FormView.Constants.ARIA_HIDDEN, true); - tabs[i].classList.remove(Wizard.selectors.active.tab); - tabs[i].setAttribute(FormView.Constants.ARIA_SELECTED, false); - tabs[i].setAttribute(FormView.Constants.TABINDEX, "-1"); - } - } - } - } - this.#hideUnhideNavButtons(this.#_active); + refreshActive() { + super.refreshActive(); + this.#hideUnhideNavButtons(this._active); } /** @@ -290,8 +196,8 @@ #navigateToNextTab() { - const activeIndex = this.#_active; - const activeTabElement = this.#getCachedTabs()[activeIndex]; + const activeIndex = this._active; + const activeTabElement = this.getCachedTabs()[activeIndex]; const activeChildId = activeTabElement.id.substring(0, activeTabElement.id.lastIndexOf(Wizard.#tabIdSuffix)); const activeChildView = this.getChild(activeChildId); let activeChildModel; @@ -306,13 +212,13 @@ validationErrorList = activeChildModel.validate(); } if (validationErrorList === undefined || validationErrorList.length == 0) { - let tabs = this.#getCachedTabs(); + let tabs = this.getCachedTabs(); let nextVisibleIndex = this.#findNextVisibleChildIndex(activeIndex); if (tabs && nextVisibleIndex >= 0) { this.#navigateAndFocusTab(nextVisibleIndex); } } - this.#hideUnhideNavButtons(this.#_active); + this.#hideUnhideNavButtons(this._active); } #isAuthoring() { @@ -320,13 +226,13 @@ } #navigateToPreviousTab() { - const activeIndex = this.#_active; - const tabs = this.#getCachedTabs(); + const activeIndex = this._active; + const tabs = this.getCachedTabs(); const lastVisibleIndex = this.#findLastVisibleChildIndex(activeIndex); if (tabs && lastVisibleIndex >= 0) { this.#navigateAndFocusTab(lastVisibleIndex); } - this.#hideUnhideNavButtons(this.#_active); + this.#hideUnhideNavButtons(this._active); } /** @@ -337,7 +243,7 @@ * @param {Number} total number of tabs */ #hideUnhideNavButtons(activeTabIndex) { - const tabsLength = this.#getCachedTabs() ? this.#getCachedTabs().length : 0; + const tabsLength = this.getCachedTabs() ? this.getCachedTabs().length : 0; const nextVisible = this.#findNextVisibleChildIndex(activeTabIndex); const previousVisible = this.#findLastVisibleChildIndex(activeTabIndex); @@ -364,26 +270,26 @@ } #setNavigationRange() { - const wizardPanels = this.#getCachedWizardPanels(); + const wizardPanels = this.getCachedWizardPanels(); if(wizardPanels) { this.maxEnabledTab = wizardPanels.length-1; this.minEnabledTab = 0; for (let i = 0; i < wizardPanels.length; i++) { - if(!this.#childComponentVisible(this.#getCachedWizardPanels()[i])) { + if(!this.#childComponentVisible(this.getCachedWizardPanels()[i])) { this.minEnabledTab = i+1; } else { break; } } for (let i = wizardPanels.length - 1; i >= 0; i--) { - if(!this.#childComponentVisible(this.#getCachedWizardPanels()[i])) { + if(!this.#childComponentVisible(this.getCachedWizardPanels()[i])) { this.maxEnabledTab = i; } else { break; } } this.minEnabledTab = Math.max(0, this.minEnabledTab); - this.maxEnabledTab = Math.min(this.#getCachedTabs().length-1, this.maxEnabledTab); + this.maxEnabledTab = Math.min(this.getCachedTabs().length-1, this.maxEnabledTab); } } @@ -392,7 +298,7 @@ } #findNextVisibleChildIndex(currentIndex) { - const tabs = this.#getCachedTabs(); + const tabs = this.getCachedTabs(); const tabsLength = tabs? tabs.length : 0; for (let i = currentIndex + 1; i < tabsLength; i++) { let isVisible = tabs[i].getAttribute(Wizard.DATA_ATTRIBUTE_VISIBLE); @@ -404,7 +310,7 @@ } #findLastVisibleChildIndex(currentIndex) { - const tabs = this.#getCachedTabs(); + const tabs = this.getCachedTabs(); if(tabs) { for (let i = currentIndex - 1; i >= 0; i--) { let isVisible = tabs[i].getAttribute(Wizard.DATA_ATTRIBUTE_VISIBLE); @@ -416,18 +322,6 @@ return -1; } - - /** - * Navigates to the tab at the provided index - * - * @private - * @param {Number} index The index of the tab to navigate to - */ - #navigate(index) { - this.#_active = index; - this.#refreshActive(); - } - /** * Navigates to the item at the provided index and ensures the active tab gains focus * @@ -435,13 +329,13 @@ * @param {Number} index The index of the item to navigate to */ #navigateAndFocusTab(index) { - this.#navigate(index); - this.focusWithoutScroll(this.#getCachedTabs()[index]); + this.navigate(index); + this.focusWithoutScroll(this.getCachedTabs()[index]); } #syncWizardNavLabels() { - const tabs = this.#getCachedTabs(); - const wizardPanels = this.#getCachedWizardPanels(); + const tabs = this.getCachedTabs(); + const wizardPanels = this.getCachedWizardPanels(); if (tabs) { for (let i = 0; i < tabs.length; i++) { let id = wizardPanels[i].querySelectorAll("[data-cmp-is]")[0].id; @@ -452,7 +346,7 @@ } #syncWizardPanels() { - const wizardPanels = this.#getCachedWizardPanels(); + const wizardPanels = this.getCachedWizardPanels(); if (wizardPanels) { for (let i = 0; i < wizardPanels.length; i++) { let id = wizardPanels[i].querySelectorAll("[data-cmp-is]")[0].id; @@ -484,7 +378,7 @@ let tabListParentElement = this.#getTabListElement(); tabListParentElement.insertBefore(navigationTabToBeRepeated, tabListParentElement.firstChild); } else { - let beforeElement = this.#getCachedTabs()[indexToInsert - 1]; + let beforeElement = this.getCachedTabs()[indexToInsert - 1]; beforeElement.after(navigationTabToBeRepeated); } } else { @@ -492,10 +386,10 @@ let beforeElement = this.#getTabNavElementById(beforeTabNavElementId); beforeElement.after(navigationTabToBeRepeated); } - this.#cacheElements(this._elements.self); + this.cacheElements(this._elements.self); let repeatedWizardPanel = this.#getWizardPanelElementById(childView.id + Wizard.#wizardPanelIdSuffix); repeatedWizardPanel.setAttribute("aria-labelledby", childView.id + Wizard.#tabIdSuffix); - this.#refreshActive(); + this.refreshActive(); this.#getTabIndexById(); if (childView.getInstanceManager().getModel().minOccur != undefined && childView.getInstanceManager().children.length > childView.getInstanceManager().getModel().minOccur) { this.#navigateAndFocusTab(this.#getTabIndexById(navigationTabToBeRepeated.id)); @@ -511,9 +405,9 @@ tabNavElement.remove(); wizardPanelElement.remove(); this.children.splice(this.children.indexOf(removedInstanceView), 1); - this.#cacheElements(this._elements.self); - this.#_active = this.#getActiveIndex(this.#getCachedTabs()); - this.#refreshActive(); + this.cacheElements(this._elements.self); + this._active = this.getActiveIndex(this.getCachedTabs()); + this.refreshActive(); } addChild(childView) { @@ -525,11 +419,11 @@ this.handleHiddenChildrenVisibility(); } this.#setNavigationRange(); - this.#hideUnhideNavButtons(this.#_active); + this.#hideUnhideNavButtons(this._active); } getChildViewByIndex(index) { - let wizardPanels = this.#getCachedWizardPanels(); + let wizardPanels = this.getCachedWizardPanels(); let fieldId = wizardPanels[index].id.substring(0, wizardPanels[index].id.lastIndexOf("__")); return this.getChild(fieldId); } @@ -563,16 +457,8 @@ } } - #getCachedTabs() { - return this._elements["tab"]; - } - - #getCachedWizardPanels() { - return this._elements["wizardpanel"] - } - #getTabNavElementById(tabId) { - let tabs = this.#getCachedTabs(); + let tabs = this.getCachedTabs(); if (tabs) { for (let i = 0; i < tabs.length; i++) { if (tabs[i].id === tabId) { @@ -583,7 +469,7 @@ } #getWizardPanelElementById(wizardPanelId) { - let wizardPanels = this.#getCachedWizardPanels(); + let wizardPanels = this.getCachedWizardPanels(); if (wizardPanels) { for (let i = 0; i < wizardPanels.length; i++) { if (wizardPanels[i].id === wizardPanelId) { @@ -594,7 +480,7 @@ } #getTabIndexById(tabId) { - let tabs = this.#getCachedTabs(); + let tabs = this.getCachedTabs(); if (tabs) { for (let i = 0; i < tabs.length; i++) { if (tabs[i].id === tabId) { @@ -612,7 +498,7 @@ let closestNonRepeatableFieldId = this._templateHTML[instanceManagerId]['closestNonRepeatableFieldId']; let closestRepeatableFieldInstanceManagerIds = this._templateHTML[instanceManagerId]['closestRepeatableFieldInstanceManagerIds']; let indexToInsert = this.getIndexToInsert(closestNonRepeatableFieldId, closestRepeatableFieldInstanceManagerIds); - let wizardPanels = this.#getCachedWizardPanels(); + let wizardPanels = this.getCachedWizardPanels(); if (indexToInsert > 0) { result.beforeViewElement = this.#getWizardPanelElementById(wizardPanels[indexToInsert - 1].id); } else { @@ -632,11 +518,11 @@ updateChildVisibility(visible, state) { this.updateVisibilityOfNavigationElement(this.#getTabNavElementById(state.id + Wizard.#tabIdSuffix), visible); - let activeTabNavElement = this.#getCachedTabs()[this.#_active]; + let activeTabNavElement = this.getCachedTabs()[this._active]; this.#setNavigationRange(); - this.#hideUnhideNavButtons(this.#_active); + this.#hideUnhideNavButtons(this._active); if (!visible && activeTabNavElement.id === state.id + Wizard.#tabIdSuffix) { - let child = this.findFirstVisibleChild(this.#getCachedTabs()); + let child = this.findFirstVisibleChild(this.getCachedTabs()); if (child) { this.#navigateAndFocusTab(this.#getTabIndexById(child.id)); } diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/wizard.html b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/wizard.html index 2e0967c8ec..565ee7130d 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/wizard.html +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/wizard.html @@ -77,6 +77,6 @@ - + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v2/wizard/wizard.html b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v2/wizard/wizard.html index a052d40b54..b377027624 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v2/wizard/wizard.html +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v2/wizard/wizard.html @@ -78,6 +78,6 @@ - + diff --git a/ui.frontend/package-lock.json b/ui.frontend/package-lock.json index 05996ad7fe..d8e70d1762 100644 --- a/ui.frontend/package-lock.json +++ b/ui.frontend/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@aemforms/af-core": "^0.22.98", - "@aemforms/af-custom-functions": "1.0.9", - "@aemforms/af-formatters": "^0.22.98" + "@aemforms/af-core": "^0.22.100", + "@aemforms/af-custom-functions": "1.0.10", + "@aemforms/af-formatters": "^0.22.100" }, "devDependencies": { "@babel/preset-env": "^7.18.2", @@ -61,23 +61,23 @@ } }, "node_modules/@aemforms/af-core": { - "version": "0.22.98", - "resolved": "https://registry.npmjs.org/@aemforms/af-core/-/af-core-0.22.98.tgz", - "integrity": "sha512-Ij4ENmVCuuqa881IEewPo9fMOo9/0uhc8V2C41yVECDRyfLqFonE9ni5ibq677z79qlysr3l40qKM/6duq/JdQ==", + "version": "0.22.100", + "resolved": "https://registry.npmjs.org/@aemforms/af-core/-/af-core-0.22.100.tgz", + "integrity": "sha512-k/w5NeupZ5sl8hCWwB3e1RJ3CxLGf301ABqmxlNURh6+0HzHACTByje+NjpYrgWBYS5WgYAZPNylWFekZuPDEA==", "dependencies": { "@adobe/json-formula": "0.1.50", - "@aemforms/af-formatters": "^0.22.98" + "@aemforms/af-formatters": "^0.22.100" } }, "node_modules/@aemforms/af-custom-functions": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@aemforms/af-custom-functions/-/af-custom-functions-1.0.9.tgz", - "integrity": "sha512-ZDWTUNAbzNsfK7kTVSRyMQiFh0ypz0cBY10cr6N1py6CJFa8VFIpxznPRL0FzdOp6wtIZtMusw0Uloh98G2+RA==" + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@aemforms/af-custom-functions/-/af-custom-functions-1.0.10.tgz", + "integrity": "sha512-n3w9tHkJOI5ISVYAK2cCi5k/oTu3rGgByDmMIgOH1+Ry4mL9nM3cxBTKEkPF8Y8JiKF1aUHIKM+MeP6u5PiiUA==" }, "node_modules/@aemforms/af-formatters": { - "version": "0.22.98", - "resolved": "https://registry.npmjs.org/@aemforms/af-formatters/-/af-formatters-0.22.98.tgz", - "integrity": "sha512-ZDgaK0mYlndf3Q7CxmF2euEwkfW7TqTNS5QZ4z/DOWl/eqEzM/v4KIzK+9J7obVC9qWYxjc0lRZAC6YC5ynwqg==" + "version": "0.22.100", + "resolved": "https://registry.npmjs.org/@aemforms/af-formatters/-/af-formatters-0.22.100.tgz", + "integrity": "sha512-Rp6WrqUuRyzlaljID60OW7onxcxsUou7LlCGoneZDgR/bQw8DTiI1r1ppnKJOwK3pvy6f1ZEqbCw66kUWqt4hg==" }, "node_modules/@ampproject/remapping": { "version": "2.2.1", @@ -11076,23 +11076,23 @@ "integrity": "sha512-dmlLYfbty8NPVIdxvI9cJ+ZdXsrRCFrCdmL1+aR2auEzXJ86rD0bm1qu+S4NOpFiZLKIyx0zvUTykms40vNjsA==" }, "@aemforms/af-core": { - "version": "0.22.98", - "resolved": "https://registry.npmjs.org/@aemforms/af-core/-/af-core-0.22.98.tgz", - "integrity": "sha512-Ij4ENmVCuuqa881IEewPo9fMOo9/0uhc8V2C41yVECDRyfLqFonE9ni5ibq677z79qlysr3l40qKM/6duq/JdQ==", + "version": "0.22.100", + "resolved": "https://registry.npmjs.org/@aemforms/af-core/-/af-core-0.22.100.tgz", + "integrity": "sha512-k/w5NeupZ5sl8hCWwB3e1RJ3CxLGf301ABqmxlNURh6+0HzHACTByje+NjpYrgWBYS5WgYAZPNylWFekZuPDEA==", "requires": { "@adobe/json-formula": "0.1.50", - "@aemforms/af-formatters": "^0.22.98" + "@aemforms/af-formatters": "^0.22.100" } }, "@aemforms/af-custom-functions": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@aemforms/af-custom-functions/-/af-custom-functions-1.0.9.tgz", - "integrity": "sha512-ZDWTUNAbzNsfK7kTVSRyMQiFh0ypz0cBY10cr6N1py6CJFa8VFIpxznPRL0FzdOp6wtIZtMusw0Uloh98G2+RA==" + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@aemforms/af-custom-functions/-/af-custom-functions-1.0.10.tgz", + "integrity": "sha512-n3w9tHkJOI5ISVYAK2cCi5k/oTu3rGgByDmMIgOH1+Ry4mL9nM3cxBTKEkPF8Y8JiKF1aUHIKM+MeP6u5PiiUA==" }, "@aemforms/af-formatters": { - "version": "0.22.98", - "resolved": "https://registry.npmjs.org/@aemforms/af-formatters/-/af-formatters-0.22.98.tgz", - "integrity": "sha512-ZDgaK0mYlndf3Q7CxmF2euEwkfW7TqTNS5QZ4z/DOWl/eqEzM/v4KIzK+9J7obVC9qWYxjc0lRZAC6YC5ynwqg==" + "version": "0.22.100", + "resolved": "https://registry.npmjs.org/@aemforms/af-formatters/-/af-formatters-0.22.100.tgz", + "integrity": "sha512-Rp6WrqUuRyzlaljID60OW7onxcxsUou7LlCGoneZDgR/bQw8DTiI1r1ppnKJOwK3pvy6f1ZEqbCw66kUWqt4hg==" }, "@ampproject/remapping": { "version": "2.2.1", @@ -11368,8 +11368,7 @@ }, "@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", - "dev": true, - "requires": {} + "dev": true }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", @@ -13307,8 +13306,7 @@ }, "@webpack-cli/configtest": { "version": "1.2.0", - "dev": true, - "requires": {} + "dev": true }, "@webpack-cli/info": { "version": "1.5.0", @@ -13319,8 +13317,7 @@ }, "@webpack-cli/serve": { "version": "1.7.0", - "dev": true, - "requires": {} + "dev": true }, "@xtuc/ieee754": { "version": "1.2.0", @@ -13354,8 +13351,7 @@ }, "acorn-import-assertions": { "version": "1.9.0", - "dev": true, - "requires": {} + "dev": true }, "acorn-walk": { "version": "7.2.0", @@ -13391,8 +13387,7 @@ }, "ajv-keywords": { "version": "3.5.2", - "dev": true, - "requires": {} + "dev": true }, "ansi-escapes": { "version": "4.3.2", @@ -15758,8 +15753,7 @@ }, "jest-pnp-resolver": { "version": "1.2.3", - "dev": true, - "requires": {} + "dev": true }, "jest-regex-util": { "version": "26.0.0", @@ -18241,8 +18235,7 @@ }, "ws": { "version": "8.16.0", - "dev": true, - "requires": {} + "dev": true }, "xml-name-validator": { "version": "4.0.0", diff --git a/ui.frontend/package.json b/ui.frontend/package.json index 0f7ab36b8c..8242b416c5 100644 --- a/ui.frontend/package.json +++ b/ui.frontend/package.json @@ -23,8 +23,8 @@ "webpack-merge": "^5.8.0" }, "dependencies": { - "@aemforms/af-core": "^0.22.98", - "@aemforms/af-formatters": "^0.22.98", - "@aemforms/af-custom-functions": "1.0.9" + "@aemforms/af-core": "^0.22.100", + "@aemforms/af-formatters": "^0.22.100", + "@aemforms/af-custom-functions": "1.0.10" } } diff --git a/ui.frontend/src/view/FormContainer.js b/ui.frontend/src/view/FormContainer.js index 540f7901dc..bfa2a5e0ee 100644 --- a/ui.frontend/src/view/FormContainer.js +++ b/ui.frontend/src/view/FormContainer.js @@ -35,6 +35,23 @@ class FormContainer { this._fields = {}; this._deferredParents = {}; this._element = params._element; + + // Prevent default behaviour on form container. + this.#preventDefaultSubmit(); + } + + /** + * Prevents the default behavior of the Enter key on components within the formContainer + * from triggering a form submission and redirecting to the Thank-You Page. + */ + #preventDefaultSubmit(){ + if(this._element) { + this._element.addEventListener('keydown', (event) => { + if (event.key === 'Enter') { + event.preventDefault(); + } + }); + } } /** diff --git a/ui.frontend/src/view/FormFieldBase.js b/ui.frontend/src/view/FormFieldBase.js index 9a2e33b8f6..72da2c75d6 100644 --- a/ui.frontend/src/view/FormFieldBase.js +++ b/ui.frontend/src/view/FormFieldBase.js @@ -208,6 +208,7 @@ class FormFieldBase extends FormField { } if (widgetElement) { + if (this.getDescription()) { const descriptionDiv = this.getDescription(); if (!(descriptionDiv.innerHTML.trim() === '' || descriptionDiv.children.length === 0)) { @@ -222,10 +223,27 @@ class FormFieldBase extends FormField { if (this.getErrorDiv() && this.getErrorDiv().innerHTML) { appendDescription('errormessage', this.getId()); } + widgetElement.setAttribute('aria-describedby', ariaDescribedby); } } + #syncAriaLabel() { + let widgetElement = typeof this.getWidget === 'function' ? this.getWidget() : null; + let widgetElements = typeof this.getWidgets === 'function' ? this.getWidgets() : null; + widgetElement = widgetElements || widgetElement; + const model = this.getModel?.(); + + if (widgetElement && model?.screenReaderText) { + // Use DOMPurify to sanitize and strip HTML tags + const screenReaderText = window.DOMPurify ? window.DOMPurify.sanitize(model.screenReaderText, { ALLOWED_TAGS: [] }) : model.screenReaderText; + widgetElement.setAttribute('aria-label', screenReaderText); + } + } + + + + /** * Synchronizes the markup with the model. * @method @@ -237,6 +255,7 @@ class FormFieldBase extends FormField { this. #syncLongDesc() this.#syncAriaDescribedBy() this.#syncError() + this.#syncAriaLabel() } /** @@ -606,19 +625,34 @@ class FormFieldBase extends FormField { * Updates the HTML state based on the description state of the field. * @param {string} descriptionText - The description. */ + updateDescription(descriptionText) { if (typeof descriptionText !== 'undefined') { - const sanitizedDescriptionText = window.DOMPurify ? window.DOMPurify.sanitize(descriptionText) : descriptionText; + const sanitizedDescriptionText = window.DOMPurify ? window.DOMPurify.sanitize(descriptionText, { ALLOWED_TAGS: [] }).trim() : descriptionText; let descriptionElement = this.getDescription(); + if (descriptionElement) { - let pElement = descriptionElement.querySelector("p"); - if (!pElement) { - // If the description is updated via rule then it might not have

tags + // Check if the content inside the descriptionElement needs updating + let currentTextContent = descriptionElement.innerText.trim(); + + if (currentTextContent === sanitizedDescriptionText) { + // No update needed if the text content already matches + return; + } + + // Find the existing

element + let pElement = descriptionElement.querySelector('p'); + + if (!pElement) { + // If no

tag exists, create one and set it as the content pElement = document.createElement('p'); + descriptionElement.innerHTML = ''; // Clear existing content descriptionElement.appendChild(pElement); } - pElement.textContent = sanitizedDescriptionText; - } else { + + // Update the

element's content with sanitized content + pElement.innerHTML = sanitizedDescriptionText; + } else { // If no description was set during authoring this.#addDescriptionInRuntime(sanitizedDescriptionText); } diff --git a/ui.tests/test-module/libs/commons/localeDataSets.js b/ui.tests/test-module/libs/commons/localeDataSets.js index 317249caf7..076e36233e 100644 --- a/ui.tests/test-module/libs/commons/localeDataSets.js +++ b/ui.tests/test-module/libs/commons/localeDataSets.js @@ -80,7 +80,7 @@ const languages = [ I18N_STRINGS: { "FileCloseAccessText" : "Drücken Sie die Eingabetaste, um die Datei zu löschen ", "FileSizeGreater" : "Dateien ${0} übersteigen die erwartete Größe: ${1}MB.", - "FileNameInvalid" : "Datei(en) ${0} hat/haben ungültige Zeichen in ihrem Namen. Es werden nur alphanumerische Zeichen unterstützt", + "FileNameInvalid" : 'Hängen Sie keine Dateien an, deren Dateiname mit (.) beginnt, \\ / : * ? " < > | ; % oder $ enthält oder ein reserviertes Schlüsselwort wie nul, prn, con, lpt oder com ist.', "FileMimeTypeInvalid" : "Datei(en) ${0} ist/sind nicht unterstützte(r) Dateityp(en)", "InternalFormSubmissionError" : "Beim Übermitteln des Formulars ist ein interner Fehler aufgetreten." } @@ -110,7 +110,7 @@ const languages = [ I18N_STRINGS: { "FileCloseAccessText" : "ファイルを削除するには Enter を押します ", "FileSizeGreater" : "ファイル「${0}」は予期されたサイズを超えています :${1} MB。", - "FileNameInvalid" : "${0} ファイルの名前に無効な文字が含まれています。サポートされるのは英数字のみになります", + "FileNameInvalid" : 'ファイル名が (.) で始まる、\\ / : * ? " < > | ; % $ を含む、または nul、prn、con、lpt、com などの予約キーワードを含むファイルは添付しないでください。', "FileMimeTypeInvalid" : "${0} ファイルの形式はサポートされていません", "InternalFormSubmissionError" : "フォームを送信中に内部エラーが発生しました。" } @@ -140,7 +140,7 @@ const languages = [ I18N_STRINGS: { "FileCloseAccessText" : "Premete Invio per eliminare il file ", "FileSizeGreater" : "I file ${0} superano le dimensioni previste: ${1} MB.", - "FileNameInvalid" : "I file ${0} contengono caratteri non validi nel nome. Sono supportati solo caratteri alfanumerici", + "FileNameInvalid" : 'Non allegare file il cui nome inizia con (.), contiene \\ / : * ? " < > | ; % $ oppure è una parola chiave riservata come nul, prn, con, lpt o com.', "FileMimeTypeInvalid" : "I file ${0} non sono tipi di file supportati", "InternalFormSubmissionError" : "Errore interno durante l'invio del modulo." } @@ -169,8 +169,8 @@ const languages = [ }, I18N_STRINGS: { "FileCloseAccessText" : "Appuyer sur Entrée pour supprimer le fichier ", - "FileSizeGreater" : "FLes fichiers ${0} font plus que la taille attendue : ${1} Mo.", - "FileNameInvalid" : "Le nom du ou des fichiers ${0} comportent des caractères non valides. Seuls les caractères alphanumériques sont pris en charge", + "FileSizeGreater" : '${0} fichier(s) dépasse(nt) la taille attendue : ${1} Mo.', + "FileNameInvalid" : "Ne joignez pas de fichiers dont le nom commence par (.), contient \\ / : * ? \" < > | ; % $, ou est un mot-clé réservé comme nul, prn, con, lpt ou com.", "FileMimeTypeInvalid" : "Le ou les fichiers ${0} sont des types de fichiers non pris en charge", "InternalFormSubmissionError" : "Une erreur interne s'est produite lors de l'envoi du formulaire." } @@ -199,8 +199,8 @@ const languages = [ }, I18N_STRINGS: { "FileCloseAccessText" : "Presione Intro para eliminar el archivo ", - "FileSizeGreater" : "FLos archivos ${0} tienen un tamaño superior al esperado: ${1}MB.", - "FileNameInvalid" : "El nombre de los archivos ${0} contiene caracteres no válidos. Solo se admiten caracteres alfanuméricos", + "FileSizeGreater" : "Los archivos ${0} son mayores que el tamaño esperado: ${1} MB.", + "FileNameInvalid" : 'No adjunte archivos cuyo nombre comience con (.), contenga \\ / : * ? " < > | ; % $, o sea una palabra clave reservada como nul, prn, con, lpt o com.', "FileMimeTypeInvalid" : "Los tipos de archivo ${0} no son compatibles", "InternalFormSubmissionError" : "Error interno al enviar el formulario." } @@ -230,7 +230,7 @@ const languages = [ I18N_STRINGS: { "FileCloseAccessText" : "Enter 키를 눌러 파일 삭제", "FileSizeGreater" : "파일 ${0}이(가) 예상 크기 ${1}MB를 초과합니다.", - "FileNameInvalid" : "파일 ${0}의 이름에 잘못된 문자가 포함되어 있습니다. 영숫자만 지원됩니다.", + "FileNameInvalid" : '파일 이름이 (.)으로 시작하거나, \\ / : * ? " < > | ; % $를 포함하거나, nul, prn, con, lpt 또는 com과 같이 예약된 키워드인 파일은 첨부하지 마십시오.', "FileMimeTypeInvalid" : "파일 ${0}은(는) 지원되지 않는 파일 유형입니다.", "InternalFormSubmissionError" : "양식을 제출하는 중 내부 오류가 발생했습니다." } @@ -260,7 +260,7 @@ const languages = [ I18N_STRINGS: { "FileCloseAccessText" : "按 Enter 以刪除檔案", "FileSizeGreater" : "檔案 ${0} 的大小比預期大: ${1}MB。", - "FileNameInvalid" : "${0} 檔案名稱中包含無效字元。僅支援字母數字字元", + "FileNameInvalid" : '不要附加以下檔案:檔案名稱以 (.) 開頭且包含 \\ / : * ? " < > | ; % $,或檔案名稱為保留關鍵字,例如 nul、prn、con、lpt 或 com。', "FileMimeTypeInvalid" : "${0} 檔案的檔案類型不受支援", "InternalFormSubmissionError" : "提交表單時發生內部錯誤。" } @@ -291,7 +291,7 @@ const languages = [ I18N_STRINGS: { "FileCloseAccessText" : "按 Enter 可删除文件", "FileSizeGreater" : "文件 ${0} 大于预期大小: ${1}MB。", - "FileNameInvalid" : "文件 ${0} 的名称中包含无效字符。仅支持字母数字字符", + "FileNameInvalid" : '请勿附加文件名以 (.) 开头、包含 \\ / : * ? " < > | ; % $ 或为保留关键字(如 nul、prn、con、lpt 或 com)的文件。', "FileMimeTypeInvalid" : "文件 ${0} 的类型不受支持", "InternalFormSubmissionError" : "提交表单时遇到内部错误。" } @@ -321,7 +321,7 @@ const languages = [ I18N_STRINGS: { "FileCloseAccessText" : "Pressione Enter para excluir o arquivo ", "FileSizeGreater" : "Fos arquivos ${0} são maiores do que o tamanho esperado: ${1}MB.", - "FileNameInvalid" : "O(s) arquivo(s) ${0} tem caracteres inválidos em seu nome. Somente caracteres alfanuméricos são suportados", + "FileNameInvalid" : 'Não anexe arquivos cujos nomes comecem com (.), contenham \\ / : * ? " < > | ; % $, ou sejam palavras-chave reservadas, como nul, prn, con, lpt ou com.', "FileMimeTypeInvalid" : "O(s) arquivo(s) ${0} não é(são) suportado(s)", "InternalFormSubmissionError" : "Encontrou um erro interno ao enviar o formulário." } diff --git a/ui.tests/test-module/package-lock.json b/ui.tests/test-module/package-lock.json index 11fbffd3e0..5c54673beb 100644 --- a/ui.tests/test-module/package-lock.json +++ b/ui.tests/test-module/package-lock.json @@ -14913,8 +14913,7 @@ "version": "10.0.0", "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", - "dev": true, - "requires": {} + "dev": true }, "backo2": { "version": "1.0.2", @@ -15927,8 +15926,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz", "integrity": "sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==", - "dev": true, - "requires": {} + "dev": true }, "cypress-iframe": { "version": "1.0.1", @@ -20788,8 +20786,7 @@ "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true, - "requires": {} + "dev": true }, "xhr": { "version": "2.6.0", diff --git a/ui.tests/test-module/specs/accordion/accordion.authoring.spec.js b/ui.tests/test-module/specs/accordion/accordion.authoring.spec.js index 468cfa396c..d5831802a1 100644 --- a/ui.tests/test-module/specs/accordion/accordion.authoring.spec.js +++ b/ui.tests/test-module/specs/accordion/accordion.authoring.spec.js @@ -84,6 +84,22 @@ describe('Page - Authoring', function () { cy.deleteComponentByPath(accordionEditPath); }); + it('runtime time library should not be loaded', function() { + cy.intercept('GET', /jcr:content\/guideContainer\/accordion\.html/).as('accordionRequest'); + dropAccordionInContainer() + cy.wait('@accordionRequest').then((interception) => { + const htmlContent = interception.response.body; + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlContent, 'text/html'); + const runtimeUrlPattern = /core\/fd\/af-clientlibs\/core-forms-components-runtime-base/; + const scriptTags = Array.from(doc.querySelectorAll('script[src]')); + console.log("tags ", scriptTags); + const isClientLibraryLoaded = scriptTags.some(script => runtimeUrlPattern.test(script.src)); + expect(isClientLibraryLoaded).to.be.false; + }) + cy.deleteComponentByPath(accordionEditPath); + }) + it('open edit dialog of Accordion', {retries: 3}, function () { cy.cleanTest(accordionEditPath).then(function() { testAccordionBehaviour(accordionPathSelector, accordionEditPath); diff --git a/ui.tests/test-module/specs/datepicker/datepicker.runtime.localisation.spec.js b/ui.tests/test-module/specs/datepicker/datepicker.runtime.localisation.spec.js index c62f3e2b45..544032d85d 100644 --- a/ui.tests/test-module/specs/datepicker/datepicker.runtime.localisation.spec.js +++ b/ui.tests/test-module/specs/datepicker/datepicker.runtime.localisation.spec.js @@ -45,7 +45,7 @@ describe("Form Runtime with Date Picker", () => { cy.get(`#${datePicker5}`).find("input").should('have.attr',"type", "text"); cy.get(`#${datePicker5}`).find("input").clear().type(incorrectInput).blur().then(x => { - cy.get(`#${datePicker5}`).find(".cmp-adaptiveform-datepicker__errormessage").should('have.text',"Specify the value in allowed format : date.") + cy.get(`#${datePicker5}`).find(".cmp-adaptiveform-datepicker__errormessage").should('contain.text', 'la valeur au format') }) cy.get(`#${datePicker5}`).find("input").clear().type(correctInput).blur().then(x => { diff --git a/ui.tests/test-module/specs/datepicker/datepicker.runtime.spec.js b/ui.tests/test-module/specs/datepicker/datepicker.runtime.spec.js index e7d2a41103..6b9e2133b2 100644 --- a/ui.tests/test-module/specs/datepicker/datepicker.runtime.spec.js +++ b/ui.tests/test-module/specs/datepicker/datepicker.runtime.spec.js @@ -158,13 +158,13 @@ describe("Form Runtime with Date Picker", () => { cy.get(`#${datePicker4}`).find("input").clear().type(incorrectInput).blur().then(x => { cy.get(`#${datePicker4}`).find(".cmp-adaptiveform-datepicker__errormessage").should('have.text',"Please enter a valid value.") cy.get(`#${datePicker4} > div.${bemBlock}__errormessage`).should('have.attr', 'id', `${datePicker4}__errormessage`) - cy.get(`#${datePicker4} > .${bemBlock}__widget`).should('have.attr', 'aria-describedby', `${datePicker4}__longdescription ${datePicker4}__shortdescription ${datePicker4}__errormessage`) + cy.get(`#${datePicker4} > .${bemBlock}__widget`).should('have.attr', 'aria-describedby', `${datePicker4}__shortdescription ${datePicker4}__errormessage`) cy.get(`#${datePicker4} > .${bemBlock}__widget`).should('have.attr', 'aria-invalid', 'true') }) cy.get(`#${datePicker4}`).find("input").clear().type(correctInput).blur().then(x => { cy.get(`#${datePicker4}`).find(".cmp-adaptiveform-datepicker__errormessage").should('have.text',"") - cy.get(`#${datePicker4} > .${bemBlock}__widget`).should('have.attr', 'aria-describedby', `${datePicker4}__longdescription ${datePicker4}__shortdescription`) + cy.get(`#${datePicker4} > .${bemBlock}__widget`).should('have.attr', 'aria-describedby', `${datePicker4}__shortdescription`) cy.get(`#${datePicker4} > .${bemBlock}__widget`).should('have.attr', 'aria-invalid', 'false') }) }) diff --git a/ui.tests/test-module/specs/formcontainer.spec.js b/ui.tests/test-module/specs/formcontainer.spec.js index 803df179b2..584425d33c 100644 --- a/ui.tests/test-module/specs/formcontainer.spec.js +++ b/ui.tests/test-module/specs/formcontainer.spec.js @@ -287,4 +287,36 @@ describe('Page/Form Authoring', function () { }); }); + + context("Check default behaviour in Form Editor", function () { + const pagePath = "/content/forms/af/core-components-it/samples/numberinput/validation.html"; + + beforeEach(function () { + cy.previewForm(pagePath); + }); + + it('check the preventDefaultSubmit method by simulating keydown event on the form', function () { + cy.get('.cmp-adaptiveform-container').then((formContainer) => { + cy.stub(formContainer[0], 'onsubmit').as('submit'); + cy.get('form').trigger('keydown', {key: 'Enter'}); + cy.get('@submit').should('not.be.called'); + }); + }); + + it('should prevent form submission by default', function () { + cy.get('.cmp-adaptiveform-container').then((formContainer) => { + // Trigger enter on button + cy.get('.cmp-adaptiveform-container button').eq(0).type('{enter}'); + cy.get('.cmp-adaptiveform-container button').eq(0).should('be.visible'); + + // Trigger enter on first input where display:none is not present + cy.get('.cmp-adaptiveform-container input').eq(3).type('{enter}'); + cy.get('.cmp-adaptiveform-container input').eq(3).should('be.visible'); + + // Trigger enter on numberinput widget + cy.get('.cmp-adaptiveform-numberinput__widget').eq(0).type('{enter}'); + cy.get('.cmp-adaptiveform-numberinput__widget').eq(0).should('be.visible'); + }); + }); + }); }); diff --git a/ui.tests/test-module/specs/fragment/fragment.runtime.spec.js b/ui.tests/test-module/specs/fragment/fragment.runtime.spec.js index 549d92a703..a523a11e6b 100644 --- a/ui.tests/test-module/specs/fragment/fragment.runtime.spec.js +++ b/ui.tests/test-module/specs/fragment/fragment.runtime.spec.js @@ -46,7 +46,10 @@ describe("Form Runtime with Fragment", () => { cy.get('*').should(passVisibleCheck) cy.get('input') .should(passDisabledAttributeCheck, 'disabled'); - cy.get('input').should('have.value', value) + // now panel's also have value because of exportData support in container + if (value && (typeof value !== 'object' || Array.isArray(value))) { + cy.get('input').should('have.value', value) + } }) } @@ -89,7 +92,7 @@ describe("Form Runtime with Fragment", () => { return innerPromise; }; - it(" should get model and view initialized properly ", () => { + it.skip(" should get model and view initialized properly ", () => { expect(formContainer, "formcontainer is initialized").to.not.be.null; const fields = formContainer.getAllFields(); @@ -110,7 +113,7 @@ describe("Form Runtime with Fragment", () => { }); }) - it(" model's changes are reflected in the html ", () => { + it.skip(" model's changes are reflected in the html ", () => { const fragmentId = formContainer._model.items[0].items[0].id; const model = formContainer._model.getElement(fragmentId); checkHTML(model.id, model.getState()).then(() => { @@ -122,7 +125,7 @@ describe("Form Runtime with Fragment", () => { }); }); - it("responsive component in fragment", () => { + it.skip("responsive component in fragment", () => { const responsiveTextInputId = formContainer._model.items[0].items[0].items[1].id; cy.get(`#${responsiveTextInputId}`).should('be.visible'); cy.get(`#${responsiveTextInputId}`).parent() diff --git a/ui.tests/test-module/specs/numberinput/numberinput.runtime.spec.js b/ui.tests/test-module/specs/numberinput/numberinput.runtime.spec.js index bfdecd20c9..7097ab14c2 100644 --- a/ui.tests/test-module/specs/numberinput/numberinput.runtime.spec.js +++ b/ui.tests/test-module/specs/numberinput/numberinput.runtime.spec.js @@ -166,13 +166,13 @@ describe("Form with Number Input", () => { cy.get(`#${numberInput4}`).find("input").clear().type(incorrectInput).blur().then(x => { cy.get(`#${numberInput4}`).find(".cmp-adaptiveform-numberinput__errormessage").should('have.text',"Please enter a valid value.") cy.get(`#${numberInput4} > div.${bemBlock}__errormessage`).should('have.attr', 'id', `${numberInput4}__errormessage`) - cy.get(`#${numberInput4} > .${bemBlock}__widget`).should('have.attr', 'aria-describedby', `${numberInput4}__longdescription ${numberInput4}__shortdescription ${numberInput4}__errormessage`) + cy.get(`#${numberInput4} > .${bemBlock}__widget`).should('have.attr', 'aria-describedby', `${numberInput4}__shortdescription ${numberInput4}__errormessage`) cy.get(`#${numberInput4} > .${bemBlock}__widget`).should('have.attr', 'aria-invalid', 'true') }) cy.get(`#${numberInput4}`).find("input").clear().type(correctInput).blur().then(x => { cy.get(`#${numberInput4}`).find(".cmp-adaptiveform-numberinput__errormessage").should('have.text',"") - cy.get(`#${numberInput4} > .${bemBlock}__widget`).should('have.attr', 'aria-describedby', `${numberInput4}__longdescription ${numberInput4}__shortdescription`) + cy.get(`#${numberInput4} > .${bemBlock}__widget`).should('have.attr', 'aria-describedby', `${numberInput4}__shortdescription`) cy.get(`#${numberInput4} > .${bemBlock}__widget`).should('have.attr', 'aria-invalid', 'false') }) }) diff --git a/ui.tests/test-module/specs/screenreadertext.runtime.spec.js b/ui.tests/test-module/specs/screenreadertext.runtime.spec.js new file mode 100644 index 0000000000..997cd0ad9e --- /dev/null +++ b/ui.tests/test-module/specs/screenreadertext.runtime.spec.js @@ -0,0 +1,89 @@ +/******************************************************************************* + * 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 screen reader text", () => { + + const pagePath = "content/forms/af/core-components-it/samples/accessibility/screenreadertext.html"; + let formContainer = null; + + /** + * initialization of form container before every test + * */ + beforeEach(() => { + cy.previewForm(pagePath).then(p => { + formContainer = p; + }); + }); + + it("Aria label should be present for screen reader text in date picker component", () => { + const [datePicker1, datePicker1FieldView] = Object.entries(formContainer._fields)[0]; + const model = formContainer._model.getElement(datePicker1); + cy.get(`#${datePicker1}`).find("input").focus().blur().then(x => { + cy.get(`#${datePicker1}`).find(".cmp-adaptiveform-datepicker__widget").should('have.attr','aria-label', model.getState().screenReaderText); + }); + }); + + it("Aria label should be present for screen reader text in text input component", () => { + const [textbox1, textbox1FieldView] = Object.entries(formContainer._fields)[2]; + cy.get(`#${textbox1}`).find("input").focus().blur().then(x => { + cy.get(`#${textbox1}`).find(".cmp-adaptiveform-textinput__widget").should('have.attr','aria-label', 'Long Text Box\n'); + }); + }); + + it("Aria label should be present for screen reader text in email input component", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[3]; + const model = formContainer._model.getElement(id); + cy.get(`#${id}`).find("input").focus().blur().then(x => { + cy.get(`#${id}`).find(".cmp-adaptiveform-emailinput__widget").should('have.attr','aria-label', model.getState().screenReaderText); + }); + }); + + it("Aria label should be present for screen reader text in radio-button component", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[5]; + const model = formContainer._model.getElement(id); + cy.get(`#${id}`).find(".cmp-adaptiveform-radiobutton__widget").should('have.attr','aria-label', model.getState().screenReaderText); + }); + + it("Aria label should be present for screen reader text in checkbox group component", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[6]; + const model = formContainer._model.getElement(id); + cy.get(`#${id}`).find(".cmp-adaptiveform-checkboxgroup__widget").should('have.attr','aria-label', model.getState().screenReaderText); + }); + + it("Aria label should be present for screen reader text in dropdown component", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[7]; + const model = formContainer._model.getElement(id); + cy.get(`#${id}`).find(".cmp-adaptiveform-dropdown__widget").should('have.attr','aria-label', model.getState().screenReaderText); + }); + + it("Aria label should be present for screen reader text in tabs on top component", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[8]; + const model = formContainer._model.getElement(id); + cy.get(`#${id} > .cmp-tabs__tablist`).should('have.attr','aria-label', model.getState().screenReaderText); + }); + + it("Aria label should be present for screen reader text in wizard component", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[9]; + const model = formContainer._model.getElement(id); + cy.get(`#${id} > .cmp-adaptiveform-wizard__widget`).should('have.attr','aria-label', model.getState().screenReaderText); + }); + + it("Aria label should be present for screen reader text in checkbox component", () => { + const [id, fieldView] = Object.entries(formContainer._fields)[10]; + const model = formContainer._model.getElement(id); + cy.get(`#${id}`).find(".cmp-adaptiveform-checkbox__widget").should('have.attr','aria-label', model.getState().screenReaderText); + }); + +}); \ No newline at end of file diff --git a/ui.tests/test-module/specs/tabsontop/tabsontop.authoring.spec.js b/ui.tests/test-module/specs/tabsontop/tabsontop.authoring.spec.js index f7dade60a6..9f3fd84df3 100644 --- a/ui.tests/test-module/specs/tabsontop/tabsontop.authoring.spec.js +++ b/ui.tests/test-module/specs/tabsontop/tabsontop.authoring.spec.js @@ -98,6 +98,21 @@ describe.only('Page - Authoring', function () { cy.deleteComponentByPath(tabsPath); }); + it('runtime library should not be loaded', function() { + cy.intercept('GET', /jcr:content\/guideContainer\/tabsontop\.html/).as('tabsRequest'); + dropTabsInContainer() + cy.wait('@tabsRequest').then((interception) => { + const htmlContent = interception.response.body; + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlContent, 'text/html'); + const runtimeUrlPattern = /core\/fd\/af-clientlibs\/core-forms-components-runtime-base/; + const scriptTags = Array.from(doc.querySelectorAll('script[src]')); + const isClientLibraryLoaded = scriptTags.some(script => runtimeUrlPattern.test(script.src)); + expect(isClientLibraryLoaded).to.be.false; + }) + cy.deleteComponentByPath(tabsPath); + }) + it('drop element in tabs on top', {retries: 3}, function () { cy.cleanTest(tabsPath).then(function () { diff --git a/ui.tests/test-module/specs/verticaltabs/verticaltabs.authoring.spec.js b/ui.tests/test-module/specs/verticaltabs/verticaltabs.authoring.spec.js index 9670a02ebd..411753b934 100644 --- a/ui.tests/test-module/specs/verticaltabs/verticaltabs.authoring.spec.js +++ b/ui.tests/test-module/specs/verticaltabs/verticaltabs.authoring.spec.js @@ -97,6 +97,21 @@ describe.only('Page - Authoring', function () { cy.deleteComponentByPath(tabsPath); }); + it('runtime library should not be loaded', function() { + cy.intercept('GET', /jcr:content\/guideContainer\/verticaltabs\.html/).as('verticaltabsRequest'); + dropTabsInContainer() + cy.wait('@verticaltabsRequest').then((interception) => { + const htmlContent = interception.response.body; + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlContent, 'text/html'); + const runtimeUrlPattern = /core\/fd\/af-clientlibs\/core-forms-components-runtime-base/; + const scriptTags = Array.from(doc.querySelectorAll('script[src]')); + const isClientLibraryLoaded = scriptTags.some(script => runtimeUrlPattern.test(script.src)); + expect(isClientLibraryLoaded).to.be.false; + }) + cy.deleteComponentByPath(tabsPath); + }) + it ('open edit dialog of Vertical Tabs',{ retries: 3 }, function(){ cy.cleanTest(tabsPath).then(function() { testPanelBehaviour(tabsContainerPathSelector, tabsPath); diff --git a/ui.tests/test-module/specs/wizard/wizard.authoring.spec.js b/ui.tests/test-module/specs/wizard/wizard.authoring.spec.js index 2d4ad015df..29d4290f94 100644 --- a/ui.tests/test-module/specs/wizard/wizard.authoring.spec.js +++ b/ui.tests/test-module/specs/wizard/wizard.authoring.spec.js @@ -66,6 +66,21 @@ describe('Page - Authoring', function () { cy.openAuthoring(pagePath); }); + it('runtime library should not be loaded', function() { + cy.intercept('GET', /jcr:content\/guideContainer\/wizard\.html/).as('wizardRequest'); + dropWizardInContainer(); + cy.wait('@wizardRequest').then((interception) => { + const htmlContent = interception.response.body; + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlContent, 'text/html'); + const runtimeUrlPattern = /core\/fd\/af-clientlibs\/core-forms-components-runtime-base/; + const scriptTags = Array.from(doc.querySelectorAll('script[src]')); + const isClientLibraryLoaded = scriptTags.some(script => runtimeUrlPattern.test(script.src)); + expect(isClientLibraryLoaded).to.be.false; + }) + cy.deleteComponentByPath(wizardLayoutDrop); + }) + it('verify Basic tab in edit dialog of Wizard', function () { dropWizardInContainer(); cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + wizardEditPathSelector).then(() => {