Skip to content

Commit

Permalink
Merge pull request #14 from Netcentric/KAW-7706-form
Browse files Browse the repository at this point in the history
Kaw 7706 form
  • Loading branch information
lakshmishriaswathnarayana authored Aug 15, 2024
2 parents 75a1796 + 788c10f commit 80a2b4a
Show file tree
Hide file tree
Showing 5 changed files with 457 additions and 3 deletions.
3 changes: 2 additions & 1 deletion blocks/footer/footer.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ footer {
margin: 0;
}

.footer .footer-column {
.footer .columns .footer-column {
padding-bottom: 60px;
position: relative;
margin: 0;
}

.footer .footer-column:last-child {
Expand Down
224 changes: 224 additions & 0 deletions blocks/form/form-fields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import { toClassName } from '../../scripts/aem.js';

function createFieldWrapper(fd) {
const fieldWrapper = document.createElement('div');
if (fd.Style) fieldWrapper.className = fd.Style;
fieldWrapper.classList.add('field-wrapper', `${fd.Type}-wrapper`);

fieldWrapper.dataset.fieldset = fd.Fieldset;

return fieldWrapper;
}

const linkHandler = (plainText) => {
// regexp to find markdown links
const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;

// replace makrdown links with HTML links
const textWithLinks = plainText.replace(markdownLinkRegex, (match, linkText, url) => `<a href="${url}">${linkText}</a>`);

return textWithLinks;
};

const ids = [];
function generateFieldId(fd, suffix = '') {
const slug = toClassName(`form-${fd.Name}${suffix}`);
ids[slug] = ids[slug] || 0;
const idSuffix = ids[slug] ? `-${ids[slug]}` : '';
ids[slug] += 1;
return `${slug}${idSuffix}`;
}

function createLabel(fd) {
const label = document.createElement('label');
label.id = generateFieldId(fd, '-label');
label.innerHTML = linkHandler(fd.Label || fd.Name);
label.setAttribute('for', fd.Id);
if (fd.Mandatory.toLowerCase() === 'true' || fd.Mandatory.toLowerCase() === 'x') {
label.dataset.required = true;
}
return label;
}

function createErrorMessage() {
const errorWrapper = document.createElement('div');
errorWrapper.classList.add('form-error-message-wrapper');
const errorMessage = document.createElement('span');
errorMessage.classList.add('form-error-message');

errorWrapper.append(errorMessage);

return errorWrapper;
}

function setCommonAttributes(field, fd) {
const isMandator = fd.Mandatory && (fd.Mandatory.toLowerCase() === 'true' || fd.Mandatory.toLowerCase() === 'x');

field.id = fd.Id;
field.name = fd.Name;
field.required = isMandator;
field.placeholder = fd.Placeholder;
field.value = fd.Value;

field.oninvalid = (e) => {
e.preventDefault();

const fieldWrapper = e.target.closest('.field-wrapper');
const errorMesageEl = fieldWrapper.querySelector('.form-error-message');

fieldWrapper.classList.add('error');

if (errorMesageEl) {
errorMesageEl.textContent = e.target.validationMessage;
}
};

field.oninput = (e) => {
const fieldWrapper = e.target.closest('.field-wrapper');
const errorMesageEl = fieldWrapper.querySelector('.form-error-message');

fieldWrapper.classList.remove('error');

if (errorMesageEl) {
errorMesageEl.textContent = '';
}
};
}

const createPlaintext = (fd) => {
const fieldWrapper = createFieldWrapper(fd);

const text = document.createElement('p');
text.innerHTML = linkHandler(fd.Value || fd.Label);
text.id = fd.Id;

fieldWrapper.append(text);

return { field: text, fieldWrapper };
};

const createSelect = async (fd) => {
const select = document.createElement('select');
setCommonAttributes(select, fd);
const addOption = ({ text, value }) => {
const option = document.createElement('option');
option.text = text.trim();
option.value = value.trim();
if (option.value === select.value) {
option.setAttribute('selected', '');
}
select.add(option);
return option;
};

if (fd.Placeholder) {
const ph = addOption({ text: fd.Placeholder, value: '' });
ph.setAttribute('disabled', '');
}

if (fd.Options) {
let options = [];
if (fd.Options.startsWith('https://')) {
const optionsUrl = new URL(fd.Options);
const resp = await fetch(`${optionsUrl.pathname}${optionsUrl.search}`);
const json = await resp.json();
json.data.forEach((opt) => {
options.push({
text: opt.Option,
value: opt.Value || opt.Option,
});
});
} else {
options = fd.Options.split(',').map((opt) => ({
text: opt.trim(),
value: opt.trim().toLowerCase(),
}));
}

options.forEach((opt) => addOption(opt));
}

const fieldWrapper = createFieldWrapper(fd);
fieldWrapper.append(select);
fieldWrapper.prepend(createLabel(fd));
fieldWrapper.append(createErrorMessage());

return { field: select, fieldWrapper };
};

const createConfirmation = (fd, form) => {
form.dataset.confirmation = new URL(fd.Value).pathname;

return {};
};

const createSubmit = (fd) => {
const button = document.createElement('button');
button.textContent = fd.Label || fd.Name;
button.classList.add('button');
button.type = 'submit';

const fieldWrapper = createFieldWrapper(fd);
fieldWrapper.append(button);
return { field: button, fieldWrapper };
};

const createTextArea = (fd) => {
const field = document.createElement('textarea');
setCommonAttributes(field, fd);

const fieldWrapper = createFieldWrapper(fd);
const label = createLabel(fd);
field.setAttribute('aria-labelledby', label.id);
field.setAttribute('rows', 5);
fieldWrapper.append(field);
fieldWrapper.prepend(label);
fieldWrapper.append(createErrorMessage());

return { field, fieldWrapper };
};

const createInput = (fd) => {
const field = document.createElement('input');
field.type = fd.Type;
setCommonAttributes(field, fd);

const fieldWrapper = createFieldWrapper(fd);
const label = createLabel(fd);
field.setAttribute('aria-labelledby', label.id);
fieldWrapper.append(field);
if (fd.Type === 'radio' || fd.Type === 'checkbox') {
fieldWrapper.append(label);
} else {
fieldWrapper.prepend(label);
}
fieldWrapper.append(createErrorMessage());

return { field, fieldWrapper };
};

const createCheckbox = (fd) => {
const { field, fieldWrapper } = createInput(fd);
if (!field.value) field.value = 'checked';
fieldWrapper.classList.add('selection-wrapper');

return { field, fieldWrapper };
};

const FIELD_CREATOR_FUNCTIONS = {
select: createSelect,
plaintext: createPlaintext,
submit: createSubmit,
confirmation: createConfirmation,
checkbox: createCheckbox,
textarea: createTextArea,
};

export default async function createField(fd, form) {
fd.Id = fd.Id || generateFieldId(fd);
const type = fd.Type.toLowerCase();
const createFieldFunc = FIELD_CREATOR_FUNCTIONS[type] || createInput;
const fieldElements = await createFieldFunc(fd, form);

return fieldElements.fieldWrapper;
}
104 changes: 104 additions & 0 deletions blocks/form/form.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
.form {
margin-bottom: 20px;
}

.form label {
padding-right: 10px;
}

.form .text-wrapper label,
.form .textarea-wrapper label,
.form .field-wrapper.email-wrapper label {
display: none;
}

.form input,
.form textarea {
box-sizing: border-box;
height: 40px;
max-width: 100%;
padding: 0 10px;
width: 100%;
background: rgb(255 255 255 / 0%);
color: #1d1d1b;
transition: 0.2s ease-in-out;
transition-property: color, background-color, border-color, box-shadow;
border: 1px solid rgb(29 29 27);
font-size: 16px;
line-height: 24px;
font-family: var(--body-font-family);
}

.form textarea {
height: auto;
padding: 10px;
}

.form input[type="checkbox"] {
width: 16px;
height: 16px;
}

.form input:focus {
outline: 0;
background-color: rgb(255 255 255 / 0%);
color: red;
border-color: red;
}

input::placeholder,
textarea::placeholder {
color: #1d1d1b;
}

.form.block .field-wrapper {
padding-top: 20px;
}

.form .field-wrapper.plaintext-wrapper {
padding-top: 0;
}

.form .field-wrapper.plaintext-wrapper p {
margin: 0;
}

.form .field-wrapper input[type="checkbox"] {
margin-left: 16px;
}


.field-wrapper.submit-wrapper {
display: flex;
justify-content: center;
}

.form .field-wrapper.error input,
.form .field-wrapper.error textarea {
border-color: red;
}

.form .field-wrapper.error input[type="checkbox"] {
outline: solid 1px red;
}

.form .field-wrapper .form-error-message-wrapper {
display: none;
padding: 0;
color: red;
}

.form .error .form-error-message-wrapper {
display: flex;
}

.form .form-general-error-wrapper {
border: 2px solid rgb(255 185 0);
padding: 8px 16px;
margin-top: 20px;
display: none;
}

.form .form-general-error-wrapper.show {
display: flex;
}
Loading

0 comments on commit 80a2b4a

Please sign in to comment.