Skip to content

Commit

Permalink
added validation, form data, and listeners
Browse files Browse the repository at this point in the history
  • Loading branch information
rrusher committed Feb 14, 2024
1 parent d699d69 commit eae9440
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 125 deletions.
4 changes: 2 additions & 2 deletions blocks/contact-form/contact-form.css
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
gap: 10px;
}

.contact-form.block .contact-form form .agent input[type="checkbox"] {
.contact-form.block .contact-form form .agent input[type="radio"] {
height: 24px;
min-width: 24px;
width: 24px;
Expand Down Expand Up @@ -126,7 +126,7 @@
left: 5px;
}

.contact-form.block .contact-form form .agent input[type="checkbox"]:checked+.checkbox svg {
.contact-form.block .contact-form form .agent input[type="radio"]:checked+.checkbox svg {
display: block;
}

Expand Down
206 changes: 92 additions & 114 deletions blocks/contact-form/contact-form.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
import { hideSideModal, i18nLookup } from '../../scripts/util.js';
import { hideSideModal, i18nLookup, getCookieValue } from '../../scripts/util.js';

const LOGIN_ERROR = 'There was a problem processing your request.';
const i18n = await i18nLookup();
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const phoneRegex = /^\d{10}$/;
const phoneRegex = /^[+]?[ (]?\d{3}[)]?[-.\s]?\d{3}[-.\s]?\d{4}$/;

/**
* Adds customID and recipientID cookie values to the request body based on the form name.
*
* @param {FormData} form - The FormData object representing the form data.
*/
function addFranchiseData(form) {
console.log('add data to form');

Check warning on line 14 in blocks/contact-form/contact-form.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
const formName = form.body.id;
const customID = getCookieValue('customerID');

form.append('customID', customID);
if (formName === 'contactForm') {
form.append('recipientId', 'https://ma312.bhhs.hsfaffiliates.com/profile/card#me');
form.append('recipientName', 'Commonwealth Real Estate');
form.append('recipientType', 'organization');
form.append('text', `Name: ${form.get('first_name')} ${form.get('last_name')}\n
Email: ${form.get('email')}\n
Phone: ${form.get('phone')}\n\n
${form.get('comments')}`);
}
if (formName === 'makeOffer') {
form.append('recipientID', 'recipientID');
}
}

function displayError(errors) {
const message = document.body.querySelector('.contact-form.block').querySelector('.message');
Expand All @@ -18,7 +43,7 @@ function displayError(errors) {
message.classList.add('error');
}

function isValid(form) {
function validateFormInputs(form) {
const errors = [];
const firstName = form.querySelector('input[name="first_name"]');
if (!firstName.value || firstName.value.trim().length === 0) {
Expand All @@ -44,7 +69,7 @@ function isValid(form) {

const phone = form.querySelector('input[name="phone"]');
if (!phone.value || phone.value.trim().length === 0) {
errors.push(i18n('Email address is required.'));
errors.push(i18n('Phone number is required.'));
phone.classList.add('error');
}
if (!phoneRegex.test(phone.value)) {
Expand All @@ -56,14 +81,10 @@ function isValid(form) {
displayError(errors);
return false;
}
console.log('validation passed');

Check warning on line 84 in blocks/contact-form/contact-form.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
return true;
}

function submitContactForm(form) {
console.log('submitted');
return isValid(form);
}

// eslint-disable no-console
const addForm = async (block) => {
const displayValue = block.style.display;
Expand All @@ -81,16 +102,70 @@ const addForm = async (block) => {

block.innerHTML = await data.text();

const submitBtn = block.querySelector('.cta a.submit');
const form = block.querySelector('form#contactForm');

if (thankYou) {
const oldSubmit = form.onsubmit;
thankYou.classList.add('form-thank-you');
form.onsubmit = function handleSubmit() {
console.log('Handle submit'); // Check if this log appears

Check warning on line 111 in blocks/contact-form/contact-form.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
if (oldSubmit.call(this)) {
console.log('Form submitted'); // Check if this log appears

Check warning on line 113 in blocks/contact-form/contact-form.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
const body = new FormData(this);
addFranchiseData(body);
const { action, method } = this;
fetch(action, { method, body, redirect: 'manual' }).then((resp) => {
/* eslint-disable-next-line no-console */
if (!resp.ok) console.error(`Form submission failed: ${resp.status} / ${resp.statusText}`);
const firstContent = thankYou.firstElementChild;
if (firstContent.tagName === 'A') {
// redirect to thank you page
window.location.href = firstContent.href;
} else {
// show thank you content
const btn = thankYou.querySelector('a');
const sideModal = document.querySelector('.side-modal-form');
if (btn && sideModal) {
btn.setAttribute('href', '#');
btn.addEventListener('click', (e) => {
e.preventDefault();
hideSideModal();
});
sideModal?.replaceChildren(thankYou);
} else {
block.replaceChildren(thankYou);
}
}
});
}
return false;
};
}

const inputs = block.querySelectorAll('input');
inputs.forEach((formEl) => {
formEl.placeholder = i18n(formEl.placeholder);
formEl.ariaLabel = i18n(formEl.ariaLabel);
});

const taEl = block.querySelector('textarea');
if (taEl && taEl.placeholder) taEl.placeholder = i18n(taEl.placeholder);

block.style.display = displayValue;

const submitBtn = block.querySelector('.contact-form.block .cta a.submit');
if (submitBtn) {
submitBtn.addEventListener('click', (e) => {
console.log('button clicked');

Check warning on line 159 in blocks/contact-form/contact-form.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
e.preventDefault();
e.stopPropagation();
submitContactForm(block.querySelector('form'));
if (validateFormInputs(form)) {
form.submit();
}
});
}

const cancelBtn = block.querySelector('.cta a.cancel');
const cancelBtn = block.querySelector('.contact-form.block .cta a.cancel');
if (cancelBtn) {
cancelBtn.addEventListener('click', (e) => {
e.preventDefault();
Expand Down Expand Up @@ -121,6 +196,11 @@ const addForm = async (block) => {
e.currentTarget.classList.remove('error');
}
});
// create input mask
el.addEventListener('input', (e) => {
const x = e.target.value.replace(/\D/g, '').match(/(\d{0,3})(\d{0,3})(\d{0,4})/);
e.target.value = !x[2] ? x[1] : `(${x[1]}) ${x[2]}${x[3] ? `-${x[3]}` : ''}`;
});
});

[...block.querySelectorAll('input[name="email"]')]
Expand All @@ -134,108 +214,6 @@ const addForm = async (block) => {
}
});
});

if (thankYou) {
const form = block.querySelector('#contactForm');
const oldSubmit = form.onsubmit;
thankYou.classList.add('form-thank-you');
form.onsubmit = function handleSubmit() {
if (oldSubmit.call(form)) {
const body = new FormData(form);
const { action, method } = form;
fetch(action, { method, body, redirect: 'manual' }).then((resp) => {
/* eslint-disable-next-line no-console */
if (!resp.ok) console.error(`Form submission failed: ${resp.status} / ${resp.statusText}`);
const firstContent = thankYou.firstElementChild;
if (firstContent.tagName === 'A') {
// redirect to thank you page
window.location.href = firstContent.href;
} else {
// show thank you content
const btn = thankYou.querySelector('a');
const sideModal = document.querySelector('.side-modal-form');
if (btn && sideModal) {
btn.setAttribute('href', '#');
btn.addEventListener('click', (e) => {
e.preventDefault();
hideSideModal();
});
sideModal?.replaceChildren(thankYou);
} else {
block.replaceChildren(thankYou);
}
}
});
}
return false;
};
}

// If the form has it's own styles, add them.
const styles = block.querySelectorAll('style');
styles.forEach((styleSheet) => {
document.head.appendChild(styleSheet);
});

// If the form has it's own scripts, load them one by one to maintain execution order.
// eslint-disable-next-line no-restricted-syntax
for (const script of [...block.querySelectorAll('script')]) {
let waitForLoad = Promise.resolve();
// The script element added by innerHTML is NOT executed.
// The workaround is to create the new script tag, copy attibutes and content.
const newScript = document.createElement('script');
newScript.setAttribute('type', 'text/javascript');
// Copy script attributes to the new element.
script.getAttributeNames().forEach((attrName) => {
const attrValue = script.getAttribute(attrName);
newScript.setAttribute(attrName, attrValue);

if (attrName === 'src') {
waitForLoad = new Promise((resolve) => {
newScript.addEventListener('load', resolve);
});
}
});
newScript.innerHTML = script.innerHTML;
script.remove();
document.body.append(newScript);

// eslint-disable-next-line no-await-in-loop
await waitForLoad;
}

const inputs = block.querySelectorAll('input');
inputs.forEach((formEl) => {
formEl.placeholder = i18n(formEl.placeholder);
formEl.ariaLabel = i18n(formEl.ariaLabel);
});

const taEl = block.querySelector('textarea');
if (taEl && taEl.placeholder) taEl.placeholder = i18n(taEl.placeholder);

// Get all checkboxes with class 'checkbox'
const checkboxes = document.querySelectorAll('input[type="checkbox"]');

// Define a function declaration to handle the change event
function handleChange() {
// Store the clicked checkbox in a variable
const clickedCheckbox = this;

// Uncheck all checkboxes that are not the clicked checkbox
checkboxes.forEach((cb) => {
if (cb !== clickedCheckbox) {
cb.checked = false;
}
});
}

// Add the change event listener to each checkbox using the function declaration
checkboxes.forEach((checkbox) => {
checkbox.addEventListener('change', handleChange);
checkbox.nextElementSibling.addEventListener('change', handleChange);
});

block.style.display = displayValue;
};

export default async function decorate(block) {
Expand Down
14 changes: 8 additions & 6 deletions blocks/contact-form/forms/contact-us.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<div class="contact-form">
<form id="contactForm" class="form-elements" method="post" action="" onsubmit="submitContactForm(event)">
<form id="contactForm" class="form-elements" method="post"
action="https://www.commonmoves.com/bin/bhhs/websiteTopicServlet"
onsubmit="return validateFormInputs(this)">
<div class="message">
<svg class="icon error" role="presentation">
<use xlink:href="/icons/icons.svg#error"></use>
Expand All @@ -21,7 +23,7 @@
<div class="contact-info">
<input name="email" type="email" placeholder="Email Address*" aria-label="email address"
aria-required="true" autocomplete="email">
<input name="phone" type="text" placeholder="Phone Number*" aria-label="phone number"
<input type="text" name="phone" placeholder="Phone Number*" inputmode="tel" aria-label="phone number"
aria-required="true" autocomplete="tel-national">
</div>
<textarea name="comments"
Expand All @@ -32,7 +34,7 @@
<div>Are you currently working with an agent?</div>
<div class="agent-check">
<div>
<input type="checkbox" aria-label="checkbox" tabindex="-1" value="" autocomplete="off" checked>
<input type="radio" name="hasAgent" aria-label="radio" tabindex="-1" value="yes" autocomplete="off" checked>
<div class="checkbox">
<svg class="empty">
<use xlink:href="/icons/icons.svg#checkmark"></use>
Expand All @@ -41,7 +43,7 @@
<span class="label">yes</span>
</div>
<div>
<input type="checkbox" aria-label="checkbox" tabindex="-1" value="" autocomplete="off">
<input type="radio" name="hasAgent" aria-label="radio" tabindex="-1" value="no" autocomplete="off">
<div class="checkbox">
<svg class="empty">
<use xlink:href="/icons/icons.svg#checkmark"></use>
Expand All @@ -51,7 +53,7 @@
</div>
</div>
</div>
<div id="captcha-20285" class="g-recaptcha contact-form-captcha" captcha-render="1">
<!-- <div id="captcha-20285" class="g-recaptcha contact-form-captcha" captcha-render="1">
<div style="width: 304px; height: 78px;">
<iframe title="reCAPTCHA" width="304" height="78" role="presentation" name="a-duze5gwmci0x"
frameborder="0" scrolling="no"
Expand All @@ -62,7 +64,7 @@
style="width: 250px; height: 40px; border: 1px solid rgb(193, 193, 193); margin: 10px 25px; padding: 0px; resize: none; display: none;"></textarea>
</div>
<iframe style="display: none;"></iframe>
</div>
</div> -->
<div class="cta">
<div class="button-container">
<a href="" class="button primary submit">Submit</a>
Expand Down
28 changes: 25 additions & 3 deletions scripts/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,12 @@ export async function i18nLookup(prefix) {
};
}

/*
* Returns the environment type based on the hostname.
*/
/**
* Retrieves the environment type based on the provided hostname.
*
* @param {string} [hostname=window.location.hostname] - The hostname to determine the environment.
* @returns {string} The environment type ('live', 'preview', or 'dev').
*/
export function getEnvType(hostname = window.location.hostname) {
const fqdnToEnvType = {
'commonmoves.com': 'live',
Expand All @@ -131,11 +134,30 @@ export function getEnvType(hostname = window.location.hostname) {
return fqdnToEnvType[hostname] || 'dev';
}

/**
* Retrieves the value of a cookie by its name.
*
* @param {string} cookieName - The name of the cookie to retrieve.
* @returns {string|null} The value of the cookie, or null if not found.
*/
export function getCookieValue(cookieName) {
const cookies = document.cookie.split(';');
const foundCookie = cookies.find((cookie) => {
const trimmedCookie = cookie.trim();
return trimmedCookie.includes(cookieName);
});
if (foundCookie) {
return foundCookie.split('=', 2)[1];
}
return null;
}

const Util = {
getSpinner,
showModal,
i18nLookup,
getEnvType,
getCookieValue,
};

export default Util;

0 comments on commit eae9440

Please sign in to comment.