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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
<servers>
<server>
<id>ossrh</id>
<username>${env.SONATYPE_USER}</username>
<username>${env.SONATYPE_USERNAME}</username>
<password>${env.SONATYPE_PASSWORD}</password>
</server>
<server>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.forms.core.components.internal.form;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
Expand All @@ -34,11 +32,10 @@
import com.adobe.cq.forms.core.components.models.form.FormStructureParser;
import com.adobe.cq.forms.core.components.util.ComponentUtils;
import com.adobe.cq.forms.core.components.views.Views;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.core.SerializableString;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ObjectWriter;

@Model(
adaptables = { SlingHttpServletRequest.class, Resource.class },
Expand Down Expand Up @@ -122,25 +119,42 @@ public String getFormDefinition() {
String result = null;
FormContainer formContainer = resource.adaptTo(FormContainer.class);
try {
HTMLCharacterEscapes htmlCharacterEscapes = new HTMLCharacterEscapes();
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new SimpleModule().addSerializer(String.class, new FormStructureParserImpl.EncodeHTMLSerializer()));
Writer writer = new StringWriter();
ObjectWriter objectWriter = mapper.writerWithView(Views.Publish.class);
objectWriter.getFactory().setCharacterEscapes(htmlCharacterEscapes);
// return publish view specific properties only for runtime
mapper.writerWithView(Views.Publish.class).writeValue(writer, formContainer);
objectWriter.writeValue(writer, formContainer);
result = writer.toString();
} catch (IOException e) {
logger.error("Unable to generate json from resource");
} catch (Exception e) {
logger.error("Unable to generate json from resource", e);
}
return result;
}

private static class EncodeHTMLSerializer extends JsonSerializer<String> {
private static final class HTMLCharacterEscapes extends CharacterEscapes {
private final int[] asciiEscapes;

public HTMLCharacterEscapes() {
// start with set of characters known to require escaping (double-quote, backslash etc)
int[] esc = CharacterEscapes.standardAsciiEscapesForJSON();
// and force escaping of a few others:
esc['<'] = CharacterEscapes.ESCAPE_STANDARD;
esc['>'] = CharacterEscapes.ESCAPE_STANDARD;
esc['&'] = CharacterEscapes.ESCAPE_STANDARD;
esc['\''] = CharacterEscapes.ESCAPE_STANDARD;
asciiEscapes = esc;
}

@Override
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (value != null) {
String escapedValue = StringEscapeUtils.escapeHtml4(value);
jsonGenerator.writeString(escapedValue);
}
public int[] getEscapeCodesForAscii() {
return asciiEscapes;
}

@Override
public SerializableString getEscapeSequence(int ch) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.forms.core.components.internal.models.v1.form;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

Expand Down Expand Up @@ -110,8 +107,17 @@ void testFormDefinitionWithHTMLEncoding() throws JsonProcessingException {
new TypeReference<Map<String, Object>>() {});
Assertions.assertNotNull(formStructureParser.getFormDefinition());
Map<String, Object> datepicker = (Map<String, Object>) ((Map<String, Object>) formJson.get(":items")).get("datepicker");
Assertions.assertEquals(datepicker.get("description"), "&lt;p&gt;dummy&lt;/p&gt;");
Assertions.assertEquals(datepicker.get("tooltip"), "&lt;p&gt;test-short-description&lt;/p&gt;");
Assertions.assertEquals(datepicker.get("description"), "<p>dummy</p>");
Assertions.assertEquals(datepicker.get("tooltip"), "<p>test-short-description</p>");
Map<String, Object> textinput = (Map<String, Object>) ((Map<String, Object>) formJson.get(":items")).get("textinput");
Assertions.assertEquals(textinput.get("description"),
"&lt;ul>&#xa;&lt;li style=&quot;font-weight: bold;&quot;>&lt;strong>abc&lt;/strong>&lt;/li>&#xa;&lt;li style=&quot;font-weight: bold;&quot;>&lt;strong>def&lt;/strong>&lt;/li>&#xa;&lt;li style=&quot;font-weight: bold;&quot;>&lt;strong>xyz&lt;/strong>&lt;/li>&#xa;&lt;/ul>");
Assertions.assertEquals(textinput.get("pattern"),
"^(([^&lt;>()[]\\.,;:s@&quot;]+(.[^&lt;>()[]\\.,;:s@&quot;]+)*)|(&quot;.+&quot;))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$");
Map<String, Object> events = (Map<String, Object>) textinput.get("events");
String changeEvent = ((ArrayList<String>) events.get("change")).get(0);
Assertions.assertEquals(changeEvent,
"[if(contains($event.payload.changes[].propertyName, 'value'), if(!(!($field.$value)), request(externalize('/content/forms/af/secur-bank-sfdc/secur-bank-credit-card-application-eds/jcr:content/guideContainer.af.dermis'),'POST', {operationName:'GET Person /Peoples',input:toString({UserName: $field.$value}),functionToExecute:'invokeFDMOperation',apiVersion:'2',formDataModelId:'/content/dam/formsanddocuments-fdm/wknd-vacations/wknd-vacations-triprwodata-di',runValidation:'false',guideNodePath:'/content/forms/af/secur-bank-sfdc/secur-bank-credit-card-application-eds/jcr:content/guideContainer/panelcontainer_877002065/verticaltabs/panel_copy/textinput'}, {&quot;Content-Type&quot; : 'application/x-www-form-urlencoded'}, 'custom:wsdlSuccess_1719466874036','custom:wsdlError_1719466874036'), {}), {})]");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@
"visible": false,
"fieldType": "datepicker"
},
"textinput": {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType": "core/fd/components/form/textinput/v1/textinput",
"name": "abc",
"jcr:title": "def",
"hideTitle": false,
"description": "&lt;ul>&#xa;&lt;li style=&quot;font-weight: bold;&quot;>&lt;strong>abc&lt;/strong>&lt;/li>&#xa;&lt;li style=&quot;font-weight: bold;&quot;>&lt;strong>def&lt;/strong>&lt;/li>&#xa;&lt;li style=&quot;font-weight: bold;&quot;>&lt;strong>xyz&lt;/strong>&lt;/li>&#xa;&lt;/ul>",
"tooltip": "<p>test-short-description</p>",
"fieldType": "text-input",
"pattern": "^(([^&lt;>()[]\\.,;:s@&quot;]+(.[^&lt;>()[]\\.,;:s@&quot;]+)*)|(&quot;.+&quot;))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$",
"fd:rules" : {
"jcr:primaryType": "nt:unstructured"
},
"fd:events" : {
"jcr:primaryType": "nt:unstructured",
"change": "[if(contains($event.payload.changes[].propertyName, 'value'), if(!(!($field.$value)), request(externalize('/content/forms/af/secur-bank-sfdc/secur-bank-credit-card-application-eds/jcr:content/guideContainer.af.dermis'),'POST', {operationName:'GET Person /Peoples',input:toString({UserName: $field.$value}),functionToExecute:'invokeFDMOperation',apiVersion:'2',formDataModelId:'/content/dam/formsanddocuments-fdm/wknd-vacations/wknd-vacations-triprwodata-di',runValidation:'false',guideNodePath:'/content/forms/af/secur-bank-sfdc/secur-bank-credit-card-application-eds/jcr:content/guideContainer/panelcontainer_877002065/verticaltabs/panel_copy/textinput'}, {&quot;Content-Type&quot; : 'application/x-www-form-urlencoded'}, 'custom:wsdlSuccess_1719466874036','custom:wsdlError_1719466874036'), {}), {})]"
}
},
"container1": {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType": "wcm/foundation/components/responsivegrid",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:dam="http://www.day.com/dam/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:fd="http://www.adobe.com/aemfd/fd/1.0"
jcr:primaryType="dam:Asset">
<jcr:content
jcr:lastModified="{Date}2022-09-14T14:30:29.305+05:30"
jcr:primaryType="dam:AssetContent"
sling:resourceType="fd/fm/af/render"
guide="1"
type="guide">
<metadata
fd:version="2.1"
jcr:primaryType="nt:unstructured"
allowedRenderFormat="HTML"
author="admin"
availableInMobileApp="{Boolean}false"
dorType="none"
formmodel="none"
hasCustomThumbnail="{Boolean}false"
title="Date Picker inside tabs"/>
</jcr:content>
</jcr:root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:fd="http://www.adobe.com/aemfd/fd/1.0"
jcr:primaryType="cq:Page">
<jcr:content
cq:deviceGroups="[mobile/groups/responsive]"
cq:lastModified="{Date}2024-07-02T11:14:54.857+05:30"
cq:lastModifiedBy="admin"
cq:template="/conf/core-components-examples/settings/wcm/templates/af-blank-v2"
jcr:language="en"
jcr:primaryType="cq:PageContent"
jcr:title="Date Picker inside tabs"
sling:configRef="/conf/forms/core-components-it/samples/tabsontop/basic/"
sling:resourceType="forms-components-examples/components/page">
<guideContainer
fd:version="2.1"
jcr:primaryType="nt:unstructured"
sling:resourceType="forms-components-examples/components/form/container"
dorType="none"
fieldType="form"
schemaType="none">
<tabsontop
jcr:primaryType="nt:unstructured"
jcr:title="Panel"
sling:resourceType="forms-components-examples/components/form/tabsontop"
assistPriority="name"
description="&lt;p>panel long&lt;/p>&#xd;&#xa;"
enabled="{Boolean}true"
fieldType="panel"
maxOccur="4"
minOccur="0"
name="tabsontop1662449848339"
repeatable="{Boolean}true"
tooltip="&lt;p>panel short&lt;/p>&#xd;&#xa;"
visible="{Boolean}true">
<datepicker1
jcr:primaryType="nt:unstructured"
jcr:title="Date Input 1"
sling:resourceType="forms-components-examples/components/form/datepicker"
fieldType="date-input"
format="date"
name="datepicker1"
type="string"/>
<datepicker2
jcr:primaryType="nt:unstructured"
jcr:title="Date Input 2"
sling:resourceType="forms-components-examples/components/form/datepicker"
fieldType="date-input"
format="date"
name="datepicker2"
type="string"/>
<textinput
jcr:created="{Date}2024-07-02T11:11:54.526+05:30"
jcr:createdBy="admin"
jcr:lastModified="{Date}2024-07-02T11:11:54.526+05:30"
jcr:lastModifiedBy="admin"
jcr:primaryType="nt:unstructured"
jcr:title="Text Input"
sling:resourceType="forms-components-examples/components/form/textinput"
fieldType="text-input"
name="textinput1719898914667"/>
<panelcontainer
jcr:created="{Date}2024-07-02T11:12:39.027+05:30"
jcr:createdBy="admin"
jcr:lastModified="{Date}2024-07-02T11:12:39.027+05:30"
jcr:lastModifiedBy="admin"
jcr:primaryType="nt:unstructured"
jcr:title="Panel"
sling:resourceType="forms-components-examples/components/form/panelcontainer"
fieldType="panel"
name="panelcontainer1719898959105">
<textinput
jcr:created="{Date}2024-07-02T11:12:46.866+05:30"
jcr:createdBy="admin"
jcr:lastModified="{Date}2024-07-02T11:14:49.391+05:30"
jcr:lastModifiedBy="admin"
jcr:primaryType="nt:unstructured"
jcr:title="Text Input"
sling:resourceType="forms-components-examples/components/form/textinput"
enabled="{Boolean}true"
fieldType="text-input"
hideTitle="false"
name="textinput1719898966930"
readOnly="{Boolean}false"
required="true"
textIsRich="[true,true,true]"
unboundFormElement="{Boolean}false"
visible="{Boolean}true"/>
<datepicker
jcr:created="{Date}2024-07-02T11:12:52.007+05:30"
jcr:createdBy="admin"
jcr:lastModified="{Date}2024-07-02T11:14:54.815+05:30"
jcr:lastModifiedBy="admin"
jcr:primaryType="nt:unstructured"
jcr:title="Date Input"
sling:resourceType="forms-components-examples/components/form/datepicker"
displayFormat="MMMM d, y"
displayPatternType="MMMM d, y"
editFormat="MMMM d, y"
editPatternType="MMMM d, y"
enabled="{Boolean}true"
fieldType="date-input"
hideTitle="false"
name="datepicker1719898972079"
readOnly="{Boolean}false"
required="true"
textIsRich="[true,true,true]"
unboundFormElement="{Boolean}false"
visible="{Boolean}true"/>
</panelcontainer>
</tabsontop>
<tabsontop2
jcr:primaryType="nt:unstructured"
jcr:title="Panel"
sling:resourceType="forms-components-examples/components/form/tabsontop"
assistPriority="name"
description="This is long description"
enabled="{Boolean}true"
fieldType="panel"
id="tooltip_scenario_test"
maxOccur="4"
minOccur="0"
name="tabsontop3"
repeatable="{Boolean}true"
tooltip="This is short description"
tooltipVisible="true"
visible="{Boolean}true"/>
</guideContainer>
</jcr:content>
</jcr:root>
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@
setFocus(id) {
super.setFocus(id);
this.setActive();
this.navigateAndFocusTab(id + '__tab');
if (id) {
this.navigateAndFocusTab(id + '__tab');
}
}

getWidget() {
Expand Down
20 changes: 16 additions & 4 deletions ui.frontend/src/view/FormTabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ class FormTabs extends FormPanel {
var _self = this;
(function (index) {
tabs[index].addEventListener("click", function (event) {
_self.navigateAndFocusTab(tabs[index].id);
// Check if the clicked element is the tab itself
if (event?.target?.id === tabs[index].id) {
_self.navigateAndFocusTab(tabs[index].id);
}

});
}(i));
tabs[i].addEventListener("keydown", function (event) {
Expand Down Expand Up @@ -237,11 +241,16 @@ class FormTabs extends FormPanel {
* @param {string} tabId - The ID of the tab to navigate to.
*/
navigateAndFocusTab(tabId) {
this.navigate(tabId);
this.focusWithoutScroll(this.#getTabNavElementById(tabId));
this.setActive();
// if current active tab and the new tabId is not same, only then set the new tab as active
if (this.#_active !== tabId) {
this.navigate(tabId);
this.focusWithoutScroll(this.#getTabNavElementById(tabId));
}
}



#getTabNavElementById(tabId) {
var tabs = this.#getCachedTabs();
if (tabs) {
Expand Down Expand Up @@ -461,7 +470,10 @@ class FormTabs extends FormPanel {
var tabs = this.#getCachedTabs();
var index = this.#getTabIndexById(tabId);
tabs[index].addEventListener("click", function (event) {
_self.navigateAndFocusTab(tabId);
// Check if the clicked element is the tab itself
if (event?.target?.id === tabId) {
_self.navigateAndFocusTab(tabId);
}
});
tabs[index].addEventListener("keydown", function (event) {
_self.#onKeyDown(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ describe("Form Runtime with Date Picker", () => {

it("Test custom error message when incorrect date format is entered", () => {
const [datePicker7, datePicker7FieldView] = Object.entries(formContainer._fields)[6];
const incorrectInputs = ["adfasdfa", "2023/11/08", "1-1-2023"];
const incorrectInputs = ["adfasdfa", "2023/11/08", "1-1-2023", "10/2" ];
incorrectInputs.forEach(incorrectInput => {
cy.get(`#${datePicker7}`).find("input").should('have.attr',"type", "text");
cy.get(`#${datePicker7}`).find("input").clear().wait(1000).type(incorrectInput).trigger('input').blur()
Expand Down
Loading