Skip to content

FORMS-26630: Add terms and conditions component - #179

Open
akshayvas wants to merge 4 commits into
adobe:mainfrom
akshayvas:tnc-copy
Open

FORMS-26630: Add terms and conditions component#179
akshayvas wants to merge 4 commits into
adobe:mainfrom
akshayvas:tnc-copy

Conversation

@akshayvas

Copy link
Copy Markdown

Description

Related Issue

Motivation and Context

How Has This Been Tested?

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have signed the Adobe Open Source CLA.
  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@armaang1729 armaang1729 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall: good addition of the TnC component. A few bugs and one security issue need addressing before merge.

const { id, label, required, enumNames, enum: enums, value, name, readOnly, visible, enabled, appliedCssClassNames, valid } = props;
const options = enumNames && enumNames.length ? enumNames : enums || [];
const orientation = props.layout?.orientation.toUpperCase();
const isToggleableLink = props[':type'].includes('toggleablelink');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug (crash risk): props[':type'] is optional — .includes() will throw if it's undefined (e.g. plain checkbox-group without :type set).

const isToggleableLink = props[':type']?.includes('toggleablelink') ?? false;

/>
{richTextString(item)}
{isToggleableLink ?
<a className='cmp-adaptiveform-checkboxgroup__links'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security: target='_blank' without rel opens reverse tabnapping. Add rel="noopener noreferrer".

<a
  className='cmp-adaptiveform-checkboxgroup__links'
  target='_blank'
  rel='noopener noreferrer'
  href={enums![index]}
  title={item || ''}
  onClick={() => linkClickHandler(index)}
>

const closeIconLabel = i18n.formatMessage({ id: 'termsAndConditions.closeButton.ariaLabel', defaultMessage: 'Close terms and conditions document' });

useEffect(() => {
const textItem = getElementByFieldType('plain-text');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug (ReferenceError): getElementByFieldType is a const arrow function declared at line 64 — const is NOT hoisted. Calling it here at line 38 (inside useEffect) will throw ReferenceError: Cannot access 'getElementByFieldType' before initialization at runtime.

Move both getElementByFieldType and getElementsByFieldType declarations above this useEffect.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not break, as function is being called from inside useEffect, but still will move this upwards for readability

if(itemInForm) {
form.getElement(checkbox.id).enabled = true;
}
observer.unobserve(node);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: observer.unobserve(node) is inside the forEach loop — it fires on every checkbox iteration, not just once after all are processed. If checkboxList has more than one entry, subsequent iterations still run but the observer is already detached. Move unobserve outside the forEach:

checkboxList.forEach((checkbox: any) => {
  if (checkbox) {
    const itemInForm = form.getElement(checkbox.id);
    if (itemInForm) {
      form.getElement(checkbox.id).enabled = true;
    }
  }
});
observer.unobserve(node);

<button type="button" className='cmp-adaptiveform-termsandcondition__close-button'
aria-label={closeIconLabel}
onClick={()=>toggleModal(false)}>X</button>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Close button text is a literal X. Visually poor and not consistent with other AEM components. Use &times; or an SVG icon. The aria-label is correct so accessibility is fine, but the visual presentation needs improvement.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This markup is kept same as in core components, let me know if we should update in this PR or do it after updating in CC

items.map((item: any, index) => {
// text or link render below
const classSuffix = item.fieldType === 'plain-text' ? 'text' : item.fieldType === 'checkbox-group' ? 'link' : null;
{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Extra {} block inside the .map() body does nothing — it creates a new block scope but doesn't change behaviour. Remove it:

return (classSuffix && (
  <div key={item.id} className={`cmp-adaptiveform-termsandcondition__${classSuffix}`}>
    ...
  </div>
));

<div
className='cmp-adaptiveform-termsandcondition__approvalcheckbox'
onClick={handleApprovalCheckboxClick}>
{approvalCheckboxItem && getChild(approvalCheckboxItem, 1, mappings)}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: getChild(approvalCheckboxItem, 1, mappings) hardcodes index 1. The approval checkbox may not always be at position 1 in items. Use the actual index:

const approvalCheckboxIndex = items.findIndex(item => item.fieldType === 'checkbox');
// then:
getChild(approvalCheckboxItem, approvalCheckboxIndex, mappings)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not under a loop, and was for single element found after using find. GetChild makes key using this element's id and index. I will still implement fix for clarity

@@ -0,0 +1,219 @@
/*

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong license header. This file has the closed-source Adobe beta license. All other test files in this repo use Apache 2.0. Replace with the standard Apache 2.0 header matching the rest of the codebase (e.g. see CheckBoxGroup.test.tsx).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This text is same as other files, could be false positive as i updated year to 2026

@armaang1729 armaang1729 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see the review comments !

@akshayvas

Copy link
Copy Markdown
Author

Will update other suggestions and share implementation screenshots of all modes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants