diff --git a/blocks/form/_form.json b/blocks/form/_form.json index a2ca8dd..ebd2440 100644 --- a/blocks/form/_form.json +++ b/blocks/form/_form.json @@ -198,7 +198,8 @@ "telephone-input", "text-input", "tnc", - "wizard" + "wizard", + "review" ] } ] diff --git a/blocks/form/components/review/_review.json b/blocks/form/components/review/_review.json new file mode 100644 index 0000000..0fe1907 --- /dev/null +++ b/blocks/form/components/review/_review.json @@ -0,0 +1,140 @@ +{ + "definitions": [ + { + "title": "Review", + "id": "review", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/fd/components/form/panelcontainer/v1/panelcontainer", + "template": { + "jcr:title": "Review", + "fieldType": "panel", + "fd:viewType": "review" + } + } + } + } + } + ], + "models": [ + { + "id": "review", + "fields": [ + { + "component": "container", + "name": "basic", + "label": "Basic", + "collapsible": false, + "fields": [ + { + "component": "text", + "name": "name", + "label": "Name", + "valueType": "string", + "required": true, + "valueFormat": "regexp", + "validation": { + "regExp": "^[^$].*", + "customErrorMsg": "Name cannot start with $" + } + }, + { + "component": "text", + "name": "jcr:title", + "label": "Title", + "valueType": "string" + }, + { + "component": "boolean", + "name": "hideTitle", + "label": "Hide title", + "valueType": "boolean" + }, + { + "component": "boolean", + "name": "unboundFormElement", + "label": "Mark as Unbound Form Element", + "valueType": "boolean" + }, + { + "component": "boolean", + "name": "visible", + "label": "Show Component", + "valueType": "boolean", + "value": true + }, + { + "component": "select", + "name": "colspan", + "label": "Column Span", + "valueType": "string", + "value": "12", + "options": [ + { + "name": "1 column", + "value": "1" + }, + { + "name": "2 column", + "value": "2" + }, + { + "name": "3 column", + "value": "3" + }, + { + "name": "4 column", + "value": "4" + }, + { + "name": "5 column", + "value": "5" + }, + { + "name": "6 column", + "value": "6" + }, + { + "name": "7 column", + "value": "7" + }, + { + "name": "8 column", + "value": "8" + }, + { + "name": "9 column", + "value": "9" + }, + { + "name": "10 column", + "value": "10" + }, + { + "name": "11 column", + "value": "11" + }, + { + "name": "12 column", + "value": "12" + } + ] + } + ] + }, + { + "component": "text", + "name": "panelNames", + "label": "Panel Names", + "valueType": "string", + "multi": true, + "required": true + }, + { + "...": "../../models/form-common/_help-container.json" + } + ] + } + ] +} \ No newline at end of file diff --git a/blocks/form/components/review/review.css b/blocks/form/components/review/review.css new file mode 100644 index 0000000..2166007 --- /dev/null +++ b/blocks/form/components/review/review.css @@ -0,0 +1,41 @@ +:root { + --color-action-border-primary-disabled: #a3baf9; +} + +main .form form .review-container { + display: block; +} + +main .form .review-container label { + margin:0; + position: relative; +} + +main .form form:not(.edit-mode) .review-container .field-wrapper:not(.panel-wrapper, .plain-text-wrapper, .file-wrapper) { + border-left: 3px solid var(--color-action-border-primary-disabled); + padding-left: 1rem; + padding-right: 1rem; + +} + +main .form .review-panel-wrapper { + display: grid; +} + +main .form .review-panel-wrapper .checkbox-wrapper { + margin: 5px; +} + +main .form form.edit-mode .review-container .field-wrapper { + display: none; +} + +main .form form.edit-mode .review-container::after { + content: 'Review of selected panels will appear here'; + font-size: 20px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + +} \ No newline at end of file diff --git a/blocks/form/components/review/review.js b/blocks/form/components/review/review.js new file mode 100644 index 0000000..eb89524 --- /dev/null +++ b/blocks/form/components/review/review.js @@ -0,0 +1,111 @@ +import { subscribe } from '../../rules/index.js'; +import { generateFormRendition } from '../../form.js'; + +function createReviewFieldElement(item) { + const { + value, name, fieldType, enumNames, + } = item; + const divElement = document.createElement('div'); + divElement.className = `review-field-value ${name}`; + if (fieldType === 'radio-group' || fieldType === 'checkbox-group' || fieldType === 'drop-down') { + const index = item?.enum?.indexOf(value); + if (index !== -1) { + divElement.textContent = enumNames[index] || item?.enum?.[index] || value; + } + } else { + divElement.textContent = value || ''; + } + + return divElement; +} + +function replaceInputs(element, model) { + function processItem(item) { + if (item.isContainer) { + item.items?.forEach(processItem); + return; + } + const { fieldType, id } = item; + + if (fieldType === 'button') { + element.querySelector(`[data-id="${id}"]`)?.remove(); + return; + } + + if (!item.value || !item.id) return; + const divElement = createReviewFieldElement(item); + + if (fieldType === 'radio-group' || fieldType === 'checkbox-group') { + const group = element.querySelector(`fieldset[data-id="${id}"]`); + if (group) { + group.querySelectorAll('.radio-wrapper, .checkbox-wrapper').forEach((wrapper) => wrapper.remove()); + group.appendChild(divElement); + } + } else if (fieldType === 'checkbox') { + const input = element.querySelector(`input[id="${id}"]`); + if (input) { + const label = input.parentNode.querySelector('label'); + input.parentNode.insertBefore(divElement, label.nextSibling); + input.remove(); + } + } else { + const input = element.querySelector(`input[id="${id}"], select[id="${id}"], textarea[id="${id}"]`); + input?.parentNode.replaceChild(divElement, input); + } + } + + model.items?.forEach(processItem); + return element; +} + +function render(element, fd, model) { + if (!model) return; + + const { form } = model; + const { properties } = fd; + + if (!properties?.panelNames) return; + + element.innerHTML = ''; + + const panelModels = []; + form.visit((field) => { + if (properties.panelNames.includes(field.name)) { + panelModels.push(field); + } + }); + + panelModels.forEach(async (field) => { + if (!field.isContainer) return; + + const panelWrapper = document.createElement('div'); + panelWrapper.className = `review-panel-wrapper ${field.name}`; + + await generateFormRendition(field.getState(), panelWrapper, form.id); + const decoratedPanel = replaceInputs(panelWrapper, field); + + if (!decoratedPanel) return; + + element.appendChild(panelWrapper); + }); +} + +export default function decorate(element, fd, container, formId) { + element.classList.add('review-container'); + let fieldModel; + + subscribe(element, formId, (_element, model) => { + fieldModel = model; + }); + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + render(element, fd, fieldModel); + } + }); + }, { threshold: 0.1 }); + + observer.observe(element); + return element; +} diff --git a/blocks/form/mappings.js b/blocks/form/mappings.js index 26de8c0..50581fe 100644 --- a/blocks/form/mappings.js +++ b/blocks/form/mappings.js @@ -1,6 +1,6 @@ import { loadCSS } from '../../scripts/aem.js'; -let customComponents = []; +let customComponents = ['review']; const OOTBComponentDecorators = ['file-input', 'wizard', 'modal', 'tnc', 'toggleable-link', 'rating', 'datetime', 'list', 'location', 'accordion', 'password']; export function setCustomComponents(components) { diff --git a/component-definition.json b/component-definition.json index 2f82028..4c744fa 100644 --- a/component-definition.json +++ b/component-definition.json @@ -547,6 +547,22 @@ } } }, + { + "title": "Review", + "id": "review", + "plugins": { + "xwalk": { + "page": { + "resourceType": "core/fd/components/form/panelcontainer/v1/panelcontainer", + "template": { + "jcr:title": "Review", + "fieldType": "panel", + "fd:viewType": "review" + } + } + } + } + }, { "title": "Terms and conditions", "id": "tnc", @@ -659,22 +675,6 @@ } } } - }, - { - "title": "Order Accordion", - "id": "order-accordion", - "plugins": { - "xwalk": { - "page": { - "resourceType": "core/franklin/components/block/v1/block", - "template": { - "name": "Order Accordion", - "filter": "order-accordion", - "fd:viewType": "order-accordion" - } - } - } - } } ] } diff --git a/component-filters.json b/component-filters.json index 7dcaef2..d580bdf 100644 --- a/component-filters.json +++ b/component-filters.json @@ -17,8 +17,7 @@ "columns", "fragment", "form", - "embed-adaptive-form", - "order-accordion" + "embed-adaptive-form" ] }, { @@ -68,7 +67,8 @@ "telephone-input", "text-input", "tnc", - "wizard" + "wizard", + "review" ] } ] \ No newline at end of file diff --git a/component-models.json b/component-models.json index 9e94d1e..70c64c1 100644 --- a/component-models.json +++ b/component-models.json @@ -4679,6 +4679,141 @@ } ] }, + { + "id": "review", + "fields": [ + { + "component": "container", + "name": "basic", + "label": "Basic", + "collapsible": false, + "fields": [ + { + "component": "text", + "name": "name", + "label": "Name", + "valueType": "string", + "required": true, + "valueFormat": "regexp", + "validation": { + "regExp": "^[^$].*", + "customErrorMsg": "Name cannot start with $" + } + }, + { + "component": "text", + "name": "jcr:title", + "label": "Title", + "valueType": "string" + }, + { + "component": "boolean", + "name": "hideTitle", + "label": "Hide title", + "valueType": "boolean" + }, + { + "component": "boolean", + "name": "unboundFormElement", + "label": "Mark as Unbound Form Element", + "valueType": "boolean" + }, + { + "component": "boolean", + "name": "visible", + "label": "Show Component", + "valueType": "boolean", + "value": true + }, + { + "component": "select", + "name": "colspan", + "label": "Column Span", + "valueType": "string", + "value": "12", + "options": [ + { + "name": "1 column", + "value": "1" + }, + { + "name": "2 column", + "value": "2" + }, + { + "name": "3 column", + "value": "3" + }, + { + "name": "4 column", + "value": "4" + }, + { + "name": "5 column", + "value": "5" + }, + { + "name": "6 column", + "value": "6" + }, + { + "name": "7 column", + "value": "7" + }, + { + "name": "8 column", + "value": "8" + }, + { + "name": "9 column", + "value": "9" + }, + { + "name": "10 column", + "value": "10" + }, + { + "name": "11 column", + "value": "11" + }, + { + "name": "12 column", + "value": "12" + } + ] + } + ] + }, + { + "component": "text", + "name": "panelNames", + "label": "Panel Names", + "valueType": "string", + "multi": true, + "required": true + }, + { + "component": "container", + "name": "help", + "label": "Help Content", + "collapsible": true, + "fields": [ + { + "component": "richtext", + "name": "description", + "label": "Help text", + "valueType": "string" + }, + { + "component": "richtext", + "name": "tooltip", + "label": "Short description", + "valueType": "string" + } + ] + } + ] + }, { "id": "tnc", "fields": [ @@ -5030,24 +5165,5 @@ "valueType": "string" } ] - }, - { - "id": "order-accordion", - "fields": [ - { - "component": "reference", - "valueType": "string", - "name": "image", - "label": "Image", - "multi": false - }, - { - "component": "richtext", - "name": "text", - "value": "", - "label": "Text", - "valueType": "string" - } - ] } ] \ No newline at end of file