diff --git a/.github/workflows/run-inside.yml b/.github/workflows/run-inside.yml index ddaaf171..7cfe0b77 100644 --- a/.github/workflows/run-inside.yml +++ b/.github/workflows/run-inside.yml @@ -28,5 +28,5 @@ jobs: - name: Trigger Jenkins Job run: | - curl -X POST 'https://dc.ci.corp.adobe.com/job/DC%20Release%20-%20Run%20Nala/buildWithParameters?token=${{ secrets.JNK_JOB_TOKEN }}&branch=${{ github.head_ref || github.ref_name }}' \ + curl -X POST 'https://dcx.ci.corp.adobe.com/job/DC%20Release%20-%20Run%20Nala/buildWithParameters?token=${{ secrets.JNK_JOB_TOKEN }}&branch=${{ github.head_ref || github.ref_name }}' \ -u '${{ secrets.JNK_USER }}:${{ secrets.JNK_API_TOKEN }}' diff --git a/acrobat/blocks/nonprofit/constants.js b/acrobat/blocks/nonprofit/constants.js deleted file mode 100644 index 8d68a177..00000000 --- a/acrobat/blocks/nonprofit/constants.js +++ /dev/null @@ -1,427 +0,0 @@ -// eslint-disable-next-line import/prefer-default-export -export const countries = [ - { - code: 'AFG', - name: 'Afghanistan', - }, - { - code: 'ALA', - name: 'Åland Islands', - }, - { - code: 'ALB', - name: 'Albania', - }, - { - code: 'DZA', - name: 'Algeria', - }, - { - code: 'ASM', - name: 'American Samoa', - }, - { - code: 'AND', - name: 'Andorra', - }, - { - code: 'AGO', - name: 'Angola', - }, - { - code: 'AIA', - name: 'Anguilla', - }, - { - code: 'ATA', - name: 'Antarctica', - }, - { - code: 'ARG', - name: 'Argentina', - }, - { - code: 'ABW', - name: 'Aruba', - }, - { - code: 'AUS', - name: 'Australia', - }, - { - code: 'AUT', - name: 'Austria', - }, - { - code: 'BHR', - name: 'Bahrain', - }, - { - code: 'BGD', - name: 'Bangladesh', - }, - { - code: 'BRB', - name: 'Barbados', - }, - { - code: 'BLR', - name: 'Belarus', - }, - { - code: 'BEL', - name: 'Belgium', - }, - { - code: 'BMU', - name: 'Bermuda', - }, - { - code: 'BTN', - name: 'Bhutan', - }, - { - code: 'BIH', - name: 'Bosnia and Herzegovina', - }, - { - code: 'BWA', - name: 'Botswana', - }, - { - code: 'BRA', - name: 'Brazil', - }, - { - code: 'BGR', - name: 'Bulgaria', - }, - { - code: 'CPV', - name: 'Cabo Verde', - }, - { - code: 'KHM', - name: 'Cambodia', - }, - { - code: 'CAN', - name: 'Canada', - }, - { - code: 'CHL', - name: 'Chile', - }, - { - code: 'COL', - name: 'Colombia', - }, - { - code: 'COM', - name: 'Comoros (the)', - }, - { - code: 'COG', - name: 'Congo (the)', - }, - { - code: 'COD', - name: 'Congo (the Democratic Republic of the)', - }, - { - code: 'COK', - name: 'Cook Islands (the)', - }, - { - code: 'HRV', - name: 'Croatia', - }, - { - code: 'CYP', - name: 'Cyprus', - }, - { - code: 'CZE', - name: 'Czechia', - }, - { - code: 'DNK', - name: 'Denmark', - }, - { - code: 'DJI', - name: 'Djibouti', - }, - { - code: 'DMA', - name: 'Dominica', - }, - { - code: 'DOM', - name: 'Dominican Republic (the)', - }, - { - code: 'ECU', - name: 'Ecuador', - }, - { - code: 'EGY', - name: 'Egypt', - }, - { - code: 'EST', - name: 'Estonia', - }, - { - code: 'FIN', - name: 'Finland', - }, - { - code: 'FRA', - name: 'France', - }, - { - code: 'GEO', - name: 'Georgia', - }, - { - code: 'DEU', - name: 'Germany', - }, - { - code: 'GHA', - name: 'Ghana', - }, - { - code: 'GGY', - name: 'Guernsey', - }, - { - code: 'GNB', - name: 'Guinea-Bissau', - }, - { - code: 'HKG', - name: 'Hong Kong', - }, - { - code: 'HUN', - name: 'Hungary', - }, - { - code: 'ISL', - name: 'Iceland', - }, - { - code: 'IND', - name: 'India', - }, - { - code: 'IRL', - name: 'Ireland', - }, - { - code: 'IMN', - name: 'Isle of Man', - }, - { - code: 'ISR', - name: 'Israel', - }, - { - code: 'ITA', - name: 'Italy', - }, - { - code: 'JPN', - name: 'Japan', - }, - { - code: 'JOR', - name: 'Jordan', - }, - { - code: 'XKX', - name: 'Kosovo', - }, - { - code: 'KGZ', - name: 'Kyrgyzstan', - }, - { - code: 'LVA', - name: 'Latvia', - }, - { - code: 'LBN', - name: 'Lebanon', - }, - { - code: 'LUX', - name: 'Luxembourg', - }, - { - code: 'MAC', - name: 'Macao', - }, - { - code: 'MKD', - name: 'Macedonia (the former Yugoslav Republic of)', - }, - { - code: 'MYS', - name: 'Malaysia', - }, - { - code: 'MLT', - name: 'Malta', - }, - { - code: 'MEX', - name: 'Mexico', - }, - { - code: 'FSM', - name: 'Micronesia (Federated States of)', - }, - { - code: 'MDA', - name: 'Moldova (the Republic of)', - }, - { - code: 'MNE', - name: 'Montenegro', - }, - { - code: 'MSR', - name: 'Montserrat', - }, - { - code: 'MAR', - name: 'Morocco', - }, - { - code: 'MOZ', - name: 'Mozambique', - }, - { - code: 'NPL', - name: 'Nepal', - }, - { - code: 'NLD', - name: 'Netherlands (the)', - }, - { - code: 'NZL', - name: 'New Zealand', - }, - { - code: 'NOR', - name: 'Norway', - }, - { - code: 'PSE', - name: 'Palestine, State of', - }, - { - code: 'PAN', - name: 'Panama', - }, - { - code: 'PHL', - name: 'Philippines (the)', - }, - { - code: 'POL', - name: 'Poland', - }, - { - code: 'ROU', - name: 'Romania', - }, - { - code: 'RUS', - name: 'Russian Federation (the)', - }, - { - code: 'SRB', - name: 'Serbia', - }, - { - code: 'SGP', - name: 'Singapore', - }, - { - code: 'SVK', - name: 'Slovakia', - }, - { - code: 'SVN', - name: 'Slovenia', - }, - { - code: 'ZAF', - name: 'South Africa', - }, - { - code: 'ESP', - name: 'Spain', - }, - { - code: 'LKA', - name: 'Sri Lanka', - }, - { - code: 'SWE', - name: 'Sweden', - }, - { - code: 'CHE', - name: 'Switzerland', - }, - { - code: 'TWN', - name: 'Taiwan (Province of China)', - }, - { - code: 'TZA', - name: 'Tanzania, United Republic of', - }, - { - code: 'TUV', - name: 'Tuvalu', - }, - { - code: 'UKR', - name: 'Ukraine', - }, - { - code: 'GBR', - name: 'United Kingdom of Great Britain and Northern Ireland (the)', - }, - { - code: 'USA', - name: 'United States of America (the)', - }, - { - code: 'VUT', - name: 'Vanuatu', - }, - { - code: 'VEN', - name: 'Venezuela (Bolivarian Republic of)', - }, - { - code: 'VNM', - name: 'Viet Nam', - }, - { - code: 'ESH', - name: 'Western Sahara*', - }, - { - code: 'YEM', - name: 'Yemen', - }, -]; diff --git a/acrobat/blocks/nonprofit/icons.js b/acrobat/blocks/nonprofit/icons.js deleted file mode 100644 index fe173edc..00000000 --- a/acrobat/blocks/nonprofit/icons.js +++ /dev/null @@ -1,28 +0,0 @@ -const nonprofitIcons = { - CHEVRON_DOWN: - '', - CHEVRON_RIGHT: - '', - BACK: '', - CLOSE: - '', - UPLOAD: - '', -}; - -export const NONPRFIT_ICONS = Object.freeze({ - CHEVRON_DOWN: 'CHEVRON_DOWN', - CHEVRON_RIGHT: 'CHEVRON_RIGHT', - BACK: 'BACK', - CLOSE: 'CLOSE', - UPLOAD: 'UPLOAD', -}); - -export function getNonprofitIconTag(type) { - const iconString = nonprofitIcons[type]; - const wrapper = document.createElement('div'); - wrapper.innerHTML = iconString; - const icon = wrapper.querySelector('svg'); - icon.classList.add('np-icon'); - return icon; -} diff --git a/acrobat/blocks/nonprofit/nonprofit-select.js b/acrobat/blocks/nonprofit/nonprofit-select.js deleted file mode 100644 index af1d5323..00000000 --- a/acrobat/blocks/nonprofit/nonprofit-select.js +++ /dev/null @@ -1,294 +0,0 @@ -/* eslint-disable chai-friendly/no-unused-expressions */ -/* eslint-disable max-len */ -import ReactiveStore from '../../scripts/reactiveStore.js'; -import { getNonprofitIconTag, NONPRFIT_ICONS } from './icons.js'; - -export default function nonprofitSelect(props) { - const { - createTag, - name, - label, - placeholder, - noOptionsText = window.mph['nonprofit-no-search-result-found'], - loadingText = window.mph['nonprofit-loading'], - required = true, - disabled = false, - hideIcon = false, - options = [], - store, - debounce, - labelKey = 'label', - valueKey = 'value', - renderOption, - footerTag, - } = props; - - let onInput; - let onSelect; - - const optionsStore = store || new ReactiveStore(options); - let localOptions = [...options]; - let localSelection = null; - - const controlTag = createTag('div', { class: 'np-control' }); - const labelTag = createTag('label', { class: 'np-label', for: name }, label); - const searchTag = createTag('input', { - class: 'np-input np-select-search', - type: 'text', - placeholder, - 'data-for': name, - }); - const valueTag = createTag('input', { - class: `np-select-value${required ? ' np-required-field' : ''}`, - name, - type: 'hidden', - }); - - if (required) { - searchTag.setAttribute('required', 'required'); - valueTag.setAttribute('required', 'required'); - } - if (disabled) { - searchTag.setAttribute('disabled', 'disabled'); - valueTag.setAttribute('disabled', 'disabled'); - } - - const listContainerTag = createTag('div', { class: 'np-select-list-container' }); - const listTag = createTag('ul', { class: 'np-select-list', 'data-for': name }); - - let searchTimeout; - let abortController; - - const showList = () => { - listContainerTag.style.display = 'block'; - }; - - const hideList = () => { - abortController?.abort(); - listTag.scrollTop = 0; - listContainerTag.style.display = 'none'; - - // Handle loss of focus depending on whether there's a selection - if (!localSelection) { - searchTag.value = ''; - if (!store) { - optionsStore.update(localOptions); - } - } else { - searchTag.value = localSelection[labelKey]; - optionsStore.update([localSelection]); - } - }; - - let hasNewInput = false; - - // Search onChange - searchTag.addEventListener('input', () => { - clearTimeout(searchTimeout); - abortController?.abort(); - - hasNewInput = true; - - abortController = new AbortController(); - searchTimeout = setTimeout(() => { - onInput && onInput(searchTag.value, abortController); - if (!store) { - const filteredOptions = localOptions.filter((option) => option[labelKey].toLowerCase().includes(searchTag.value.toLowerCase())); - optionsStore.update(filteredOptions); - } - showList(); - }, debounce); - }); - - let focusedFromList = false; - searchTag.addEventListener('focus', () => { - if (debounce && !searchTag.value) return; - if (!focusedFromList) searchTag.select(); - else focusedFromList = false; - showList(); - }); - - searchTag.addEventListener('keydown', (ev) => { - if (ev.code !== 'ArrowDown') return; - ev.preventDefault(); - if (ev.code === 'ArrowDown') { - const listItem = listContainerTag.querySelector('.np-select-item'); - if (listItem) listItem.focus(); - } - }); - - let rerendering = false; - const focusOut = (ev) => { - if (rerendering) return; - if (!ev.relatedTarget) { - hideList(); - return; - } - const selectTag = ev.relatedTarget.closest('.np-select-list-tag'); - // If the newly focused item is part of the select, don't hide list - if (selectTag || ev.relatedTarget === searchTag) return; - hideList(); - }; - - searchTag.addEventListener('focusout', focusOut); - - let keyboardFocusedId; - - // Render select elements - optionsStore.subscribe((storeOptions, loading) => { - rerendering = true; - - // Empty the list - listTag.replaceChildren(); - - storeOptions.forEach((option) => { - const itemTag = createTag('li', { - class: 'np-select-list-tag np-select-item', - tabindex: -1, - 'data-value': option[valueKey], - }); - - if (renderOption) { - renderOption(option, itemTag); - } else { - itemTag.textContent = option[labelKey]; - } - - // Keyboard navigation and selection handing - - const selectItem = () => { - onSelect && onSelect(option); - searchTag.value = option[labelKey]; - valueTag.value = option[valueKey]; - valueTag.dispatchEvent(new Event('input')); - localSelection = option; - hasNewInput = false; - hideList(); - }; - - itemTag.addEventListener('keydown', (ev) => { - if (!['ArrowDown', 'ArrowUp', 'Enter'].includes(ev.code)) { - focusedFromList = true; - searchTag.focus(); - return; - } - ev.preventDefault(); - - // Select on Enter - if (ev.code === 'Enter') { - selectItem(); - return; - } - - // Navigate on ArrowDown/Up - let sibling; - if (ev.code === 'ArrowDown') { - sibling = ev.target.nextElementSibling; - } - if (ev.code === 'ArrowUp') { - sibling = ev.target.previousElementSibling; - } - if (sibling && !sibling.classList.contains('np-select-loader')) { - sibling.focus(); - } - }); - - itemTag.addEventListener('click', selectItem); - - itemTag.addEventListener('focus', (ev) => { - keyboardFocusedId = ev.target.getAttribute('data-value'); - }); - - itemTag.addEventListener('focusout', (ev) => { - if (rerendering) return; - keyboardFocusedId = null; - focusOut(ev); - }); - - listTag.append(itemTag); - }); - - const infoTagKeydown = () => { - focusedFromList = true; - searchTag.focus(); - }; - - if (!loading && storeOptions.length === 0) { - const noOptionsTag = createTag( - 'div', - { class: 'np-select-list-tag np-select-no-options', tabindex: -1 }, - noOptionsText, - ); - noOptionsTag.addEventListener('keydown', infoTagKeydown); - noOptionsTag.addEventListener('focusout', focusOut); - listTag.append(noOptionsTag); - } - - if (loading) { - const loadingTag = createTag( - 'div', - { class: 'np-select-list-tag np-select-loader', tabindex: -1 }, - `${loadingText}...`, - ); - loadingTag.addEventListener('keydown', infoTagKeydown); - loadingTag.addEventListener('focusout', focusOut); - listTag.append(loadingTag); - } - - if (keyboardFocusedId) { - const itemToFocus = listTag.querySelector(`li[data-value='${keyboardFocusedId}']`); - if (itemToFocus) itemToFocus.focus(); - } - - rerendering = false; - }); - - listContainerTag.append(listTag); - if (footerTag) { - listContainerTag.append(footerTag); - footerTag.addEventListener('focusout', focusOut); - } - - controlTag.append(labelTag, searchTag, valueTag, listContainerTag); - - if (!hideIcon) { - const arrowIconTag = getNonprofitIconTag(NONPRFIT_ICONS.CHEVRON_DOWN); - controlTag.append(arrowIconTag); - } - - controlTag.onInput = (handler) => (onInput = handler); - controlTag.onSelect = (handler) => (onSelect = handler); - controlTag.onScroll = (handler) => { - listTag.addEventListener('scroll', (ev) => { - abortController = new AbortController(); - handler(ev.target, abortController, hasNewInput); - }); - }; - - controlTag.enable = () => { - searchTag.removeAttribute('disabled'); - valueTag.removeAttribute('disabled'); - }; - - controlTag.clear = (withFocus = true) => { - searchTag.value = ''; - valueTag.value = ''; - valueTag.dispatchEvent(new Event('input')); - localSelection = null; - if (!store) { - optionsStore.update(localOptions); - } - if (withFocus) { - searchTag.focus(); - } - }; - - controlTag.updateOptions = (newOptions) => { - optionsStore.update(newOptions); - localOptions = newOptions; - }; - - controlTag.getValue = () => valueTag.value; - - return controlTag; -} diff --git a/acrobat/blocks/nonprofit/nonprofit.css b/acrobat/blocks/nonprofit/nonprofit.css deleted file mode 100644 index ffaca851..00000000 --- a/acrobat/blocks/nonprofit/nonprofit.css +++ /dev/null @@ -1,504 +0,0 @@ -:root { - --np-content-width: 378px; - --np-container-padding: 86px; - --np-stepper-max-height: 27px; - --np-content-max-height: 727px; - --np-input-placeholder-color: #757575; - --np-organization-item-height: 72px; - --np-organization-cannot-find-height: 51px; - --np-selected-organization-fallback-background: #da408b; - --np-button-disabled-background: #e6e6e6; - --np-button-disabled-color: #b1b1b1; -} - -.np-container { - display: flex; - flex-direction: column; - align-items: center; - padding-block: var(--np-container-padding); - position: relative; - height: calc( - var(--np-stepper-max-height) + var(--np-content-max-height) + 2 * var(--np-container-padding) - ); - box-sizing: border-box; -} - -.np-icon path { - fill: var(--color-gray-600); -} - -.np-stepper-container { - display: flex; - align-items: center; - gap: 12px; - max-height: var(--np-stepper-max-height); -} - -.np-step-container { - display: flex; - align-items: center; - gap: 12px; - flex-shrink: 0; -} - -.np-step-container span { - user-select: none; -} - -.np-step-icon { - width: 20px; - height: 20px; - border-radius: 10px; - background-color: var(--color-gray-300); - color: var(--color-white); - font-weight: 700; - font-size: 16px; - line-height: 18px; - text-align: center; - vertical-align: middle; -} - -.np-step-name { - font-size: 16px; - color: var(--color-gray-300); -} - -.np-step-container.is-cleared .np-step-icon { - background-color: var(--color-gray-600); -} - -.np-step-container.is-cleared .np-step-name { - color: var(--color-gray-600); -} - -.np-step-container.is-active .np-step-icon { - background-color: var(--link-color); -} - -.np-step-container.is-active .np-step-name { - color: var(--link-color); -} - -.np-step-separator { - width: 12px; - height: 12px; -} - -.np-step-separator.np-icon path { - fill: none; -} - -.np-stepper-back { - position: absolute; - top: 440px; - left: 10%; - width: 50px; - height: 50px; - background-color: var(--color-gray-200); - border-radius: 25px; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - user-select: none; - transition: left 200ms; -} - -.np-stepper-back:hover, -.np-stepper-back:focus { - background-color: var(--color-gray-300); -} - -.np-stepper-back:hover .np-icon path, -.np-stepper-back:focus .np-icon path { - fill: var(--color-gray-800); -} - -.np-stepper-back.disabled { - background-color: var(--color-gray-100); -} - -.np-content-container { - display: flex; - flex-direction: column; - align-items: center; - max-height: var(--np-content-max-height); -} - -.np-description { - display: flex; - flex-direction: column; - align-items: center; - padding-block: 56px; - gap: 16px; -} - -.np-title { - font-weight: 700; - line-height: 35px; - font-size: 24px; - text-align: center; -} - -.np-subtitle { - text-align: center; - max-width: 800px; -} - -.np-form { - width: var(--np-content-width); - display: flex; - flex-direction: column; - gap: 25px; -} - -.np-button { - background-color: var(--link-color); - color: var(--color-white); - font-weight: 700; - font-size: 16px; - line-height: 20px; - border: none; - border-radius: 20px; - padding: 10px 20px; - cursor: pointer; -} - -.np-button:disabled { - background-color: var(--np-button-disabled-background); - color: var(--np-button-disabled-color); - cursor: default; -} - -.np-form .np-button { - align-self: start; -} - -.np-control { - display: flex; - flex-direction: column; - gap: 7px; - position: relative; -} - -.np-label { - font-weight: 400; - font-size: 14px; - line-height: 18px; - color: var(--color-gray-700); -} - -.np-input { - width: 100%; - border: 1px solid var(--color-gray-500); - line-height: 20px; - padding: 9px 15px; - box-sizing: border-box; - border-radius: 4px; - appearance: none; -} - -.np-input:disabled { - border: 1px solid var(--color-gray-300); -} - -.np-input::placeholder { - color: var(--np-input-placeholder-color); -} - -.np-control .np-icon { - position: absolute; - height: 12px; - right: 15px; - top: 39px; - display: flex; - cursor: pointer; - pointer-events: none; -} - -.np-input:disabled ~ .np-icon path, -.np-stepper-back.disabled .np-icon path { - fill: var(--color-gray-400); -} - -.np-input[type='file'] ~ .np-input { - cursor: pointer; -} - -.np-select-search { - appearance: none; - padding-right: 40px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.np-select-list-container { - display: none; - position: absolute; - list-style-type: none; - border: 1px solid var(--color-gray-500); - background-color: var(--color-white); - border-radius: 4px; - box-sizing: border-box; - width: 100%; - top: 100%; - margin-top: 4px; - min-height: var(--np-organization-cannot-find-height); - max-height: calc( - 4 * var(--np-organization-item-height) + var(--np-organization-cannot-find-height) + 1px - ); - overflow: hidden; - z-index: 1; - box-shadow: 0 2px 6px -1px rgb(0 0 0 / 10%); -} - -.np-select-list { - list-style-type: none; - width: 100%; - margin: 0; - padding: 5px 0; - max-height: calc(4 * var(--np-organization-item-height)); - overflow-x: hidden; - overflow-y: auto; -} - -.np-select-list-tag { - padding: 10px 15px; - font-size: 16px; - line-height: 21px; - cursor: pointer; - display: flex; - flex-direction: column; - outline: none; - overflow: hidden; -} - -.np-select-item:hover { - background-color: var(--color-gray-100); -} - -.np-select-item:focus { - background-color: var(--color-gray-200); -} - -.np-select-item span { - line-height: 21px; - width: 100%; -} - -.np-organization-select-name { - font-weight: 700; - padding-bottom: 10px; -} - -.np-organization-select-id { - font-weight: 400; -} - -.np-organization-select-name, -.np-organization-select-id { - text-wrap: nowrap; - text-overflow: ellipsis; - overflow: hidden; -} - -.np-select-no-options { - color: var(--np-input-placeholder-color); -} - -.np-select-no-options, -.np-select-loader { - user-select: none; -} - -.np-selected-organization-container { - display: none; - border: 1px solid var(--color-gray-500); - border-radius: 4px; - box-sizing: border-box; - flex-direction: column; - padding-inline: 15px; - position: relative; -} - -.np-selected-organization-container .np-selected-organization-detail { - font-size: 16px; - font-weight: 400; - line-height: 21px; - padding-block: 10px; -} - -.np-selected-organization-container > .np-selected-organization-detail { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; -} - -.np-selected-organization-header { - display: flex; - align-items: center; - padding-block: 10px; - gap: 10px; -} - -.np-selected-organization-header .np-selected-organization-detail { - padding-right: 15px; -} - -.np-selected-organization-avatar { - width: 40px; - height: 40px; - border-radius: 20px; - display: flex; - justify-content: center; - align-items: center; - flex-shrink: 0; -} - -.np-selected-organization-avatar.loading { - animation: loadingAvatar 1s infinite; -} - -.np-selected-organization-avatar.fallback { - background-color: var(--np-selected-organization-fallback-background); -} - -.np-selected-organization-logo { - object-fit: contain; -} - -.np-selected-organization-avatar.fallback .np-selected-organization-logo { - display: none; -} - -.np-selected-organization-initials { - color: var(--color-white); - font-size: 18px; - font-weight: 700; -} - -.np-selected-organization-avatar:not(.fallback) .np-selected-organization-initials { - display: none; -} - -.np-selected-organization-separator { - height: 1px; - background-color: var(--color-gray-300); -} - -.np-selected-organization-clear { - position: absolute; - display: flex; - top: 15px; - right: 15px; - cursor: pointer; -} - -.np-selected-organization-clear:hover .np-icon path, -.np-selected-organization-clear:focus .np-icon path { - fill: var(--color-gray-800); -} - -.np-personal-data-disclaimer { - font-size: 14px; - line-height: 18px; - padding-bottom: 15px; - color: var(--color-gray-600); -} - -.np-application-review-container { - display: flex; - flex-direction: column; - align-items: center; - margin-block: 56px; -} - -.np-application-review-container .np-title { - margin-bottom: 16px; -} - -.np-application-review-detail { - margin-block: 16px; - font-size: 18px; - line-height: 24px; - font-weight: 400; -} - -@media screen and (max-width: 900px) { - .np-stepper-back { - left: 4%; - } -} - -@keyframes loadingAvatar { - 0% { - background-color: var(--color-gray-200); - } - - 50% { - background-color: var(--color-gray-100); - } - - 100% { - background-color: var(--color-gray-200); - } -} - -/* Temporary stuff below (TODO - remove) */ -.nonprofit { - display: flex; - flex-direction: column; -} - -.np-controller-buffer { - flex-grow: 1; -} - -.np-controller-container { - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 50px; - gap: 10px; -} - -.np-controller-title { - font-weight: 700; - font-size: 16px; - color: var(--link-color); -} - -.np-controller-button { - border: 2px solid var(--link-color); - background-color: var(--color-white); - color: var(--link-color); - font-weight: 700; - font-size: 16px; - line-height: 20px; - border-radius: 20px; - padding: 10px 20px; - align-self: start; - cursor: pointer; - width: 200px; - margin-right: 12px; - white-space: nowrap; - - &.is-step { - width: 100px; - margin-right: 3px; - } - - &.selected { - background-color: var(--link-color); - color: var(--color-white); - } -} - -.np-controller-section { - display: flex; - width: 415px; - - & .np-controller-button:last-child { - margin: 0; - } -} diff --git a/acrobat/blocks/nonprofit/nonprofit.js b/acrobat/blocks/nonprofit/nonprofit.js deleted file mode 100644 index 57a7f24b..00000000 --- a/acrobat/blocks/nonprofit/nonprofit.js +++ /dev/null @@ -1,1043 +0,0 @@ -/* eslint-disable no-alert */ -/* eslint-disable no-plusplus */ -/* eslint-disable no-underscore-dangle */ -/* eslint-disable compat/compat */ -/* eslint-disable max-len */ -import ReactiveStore from '../../scripts/reactiveStore.js'; -import { setLibs } from '../../scripts/utils.js'; -import { countries } from './constants.js'; -import { getNonprofitIconTag, NONPRFIT_ICONS } from './icons.js'; -import nonprofitSelect from './nonprofit-select.js'; - -const miloLibs = setLibs('/libs'); -const { createTag } = await import(`${miloLibs}/utils/utils.js`); - -const removeOptionElements = (element) => { - const children = element.querySelectorAll(':scope > div'); - children.forEach((child) => { - child.remove(); - }); -}; - -// #region Constants - -const PERCENT_API_URL = 'https://sandbox-api.poweredbypercent.com/v1'; -const PERCENT_VALIDATION_API_URL = 'https://sandbox-validate.poweredbypercent.com/adobe-acrobat'; -const PERCENT_PUBLISHABLE_KEY = 'sandbox_pk_8b320cc4-5950-4263-a3ac-828c64f6e19b'; -export const SCENARIOS = Object.freeze({ - FOUND_IN_SEARCH: 'FOUND_IN_SEARCH', - NOT_FOUND_IN_SEARCH: 'NOT_FOUND_IN_SEARCH', -}); -const SEARCH_DEBOUNCE = 500; // ms -const FETCH_ON_SCROLL_OFFSET = 100; // px - -// #endregion - -const nonprofitFormData = JSON.parse('{}'); - -// #region Stores - -export const stepperStore = new ReactiveStore({ - step: 1, - scenario: SCENARIOS.FOUND_IN_SEARCH, - pending: false, -}); - -export const organizationsStore = new ReactiveStore([]); - -export const registriesStore = new ReactiveStore([]); - -const selectedOrganizationStore = new ReactiveStore(); - -// #endregion - -// #region Percent API integration - -// #region Helpers - -function getPercentErrorString(result) { - return `${result.error.title}: ${result.error.message}${result.error.reasons ? ` (${result.error.reasons.join(', ')})` : ''}`; -} - -async function validatePercentResponse(response) { - const result = await response.json(); - - if (!response.ok) { - throw new Error(getPercentErrorString(result)); - } - - return result; -} - -// #endregion - -let nextOrganizationsPageUrl; - -async function fetchOrganizations(search, countryCode, abortController) { - try { - organizationsStore.startLoading(true); - const response = await fetch( - `${PERCENT_API_URL}/organisations?countryCode=${countryCode}&query=${search}`, - { - cache: 'force-cache', - signal: abortController.signal, - headers: { Authorization: PERCENT_PUBLISHABLE_KEY }, - }, - ); - - const result = await validatePercentResponse(response); - - if (!result._links) { - nextOrganizationsPageUrl = null; - window.lana?.log('No next organization page link provided.'); - } else nextOrganizationsPageUrl = result._links.next || null; - organizationsStore.update(result.data); - } catch (error) { - organizationsStore.update((prev) => prev); - window.lana?.log(`Could not fetch organizations: ${error}`); - } -} - -async function fetchNextOrganizations(abortController) { - if (!nextOrganizationsPageUrl) return; - try { - organizationsStore.startLoading(); - const response = await fetch(nextOrganizationsPageUrl, { - cache: 'force-cache', - signal: abortController.signal, - headers: { Authorization: PERCENT_PUBLISHABLE_KEY }, - }); - - const result = await validatePercentResponse(response); - - nextOrganizationsPageUrl = result._links.next; - organizationsStore.update((prev) => [...prev, ...result.data]); - } catch (error) { - organizationsStore.update((prev) => prev); - window.lana?.log(`Could not fetch next organizations: ${error}`); - } -} - -async function fetchRegistries(countryCode, abortController) { - try { - registriesStore.startLoading(true); - const response = await fetch(`${PERCENT_API_URL}/registries?countryCode=${countryCode}`, { - cache: 'force-cache', - signal: abortController.signal, - headers: { Authorization: PERCENT_PUBLISHABLE_KEY }, - }); - - const result = await validatePercentResponse(response); - - registriesStore.update(result.data); - } catch (error) { - registriesStore.update((prev) => prev); - window.lana?.log(`Could not fetch registries: ${error}`); - } -} - -async function sendOrganizationData() { - try { - const inviteResponse = await fetch(PERCENT_VALIDATION_API_URL, { - method: 'POST', - headers: { Authorization: `Bearer ${PERCENT_PUBLISHABLE_KEY}` }, - }); - - const inviteResult = await validatePercentResponse(inviteResponse); - - const { validationInviteId } = inviteResult.data; - - const foundInSearch = stepperStore.data.scenario === SCENARIOS.FOUND_IN_SEARCH; - - if (!foundInSearch) { - const evidenceUploadData = new FormData(); - evidenceUploadData.append('file', nonprofitFormData.evidenceNonProfitStatus); - evidenceUploadData.append('validationInviteId', validationInviteId); - - const uploadResponse = await fetch(`${PERCENT_API_URL}/validation-submission-documents`, { - method: 'POST', - headers: { Authorization: PERCENT_PUBLISHABLE_KEY }, - body: evidenceUploadData, - }); - - await validatePercentResponse(uploadResponse); - } - - let body; - if (foundInSearch) { - body = JSON.stringify({ - validationInviteId, - organisationId: nonprofitFormData.organizationId, - firstName: nonprofitFormData.firstName, - lastName: nonprofitFormData.lastName, - email: nonprofitFormData.email, - language: 'en-US', - }); - } else { - body = JSON.stringify({ - validationInviteId, - countryCode: nonprofitFormData.countryCode, - organisationName: nonprofitFormData.organizationName, - registryId: nonprofitFormData.organizationRegistrationId, - registryName: nonprofitFormData.registryName, - website: nonprofitFormData.website, - addressLine1: nonprofitFormData.streetAddress, - addressLine2: nonprofitFormData.addressDetails, - city: nonprofitFormData.city, - postal: nonprofitFormData.zipCode, - state: nonprofitFormData.state, - firstName: nonprofitFormData.firstName, - lastName: nonprofitFormData.lastName, - email: nonprofitFormData.email, - language: 'en-US', - }); - } - - const submissionResponse = await fetch(`${PERCENT_API_URL}/validation-submissions`, { - method: 'POST', - body, - headers: { - Authorization: PERCENT_PUBLISHABLE_KEY, - 'Content-Type': 'application/json; charset=utf-8', - }, - }); - - await validatePercentResponse(submissionResponse); - - return true; - } catch (error) { - window.lana?.log(`Could not send organization data: ${error}`); - return false; - } -} - -// #endregion - -// UI - -function getStepBackTag() { - const buttonTag = createTag('div', { class: 'np-stepper-back', tabindex: 0 }); - const backIconTag = getNonprofitIconTag(NONPRFIT_ICONS.BACK); - buttonTag.append(backIconTag); - - stepperStore.subscribe(({ step, scenario, pending }) => { - if (pending) buttonTag.classList.add('disabled'); - else buttonTag.classList.remove('disabled'); - if (step === 1 || (step === 3 && scenario === SCENARIOS.FOUND_IN_SEARCH) || step === 5) { - buttonTag.style.display = 'none'; - return; - } - buttonTag.style.display = 'flex'; - }); - - buttonTag.addEventListener('click', () => { - if (stepperStore.data.pending) return; - stepperStore.update((prev) => ({ ...prev, step: prev.step - 1 })); - }); - - buttonTag.addEventListener('keydown', (ev) => { - if (stepperStore.data.pending) return; - if (ev.code !== 'Enter') return; - ev.preventDefault(); - buttonTag.click(); - }); - - return buttonTag; -} - -function renderStepper(containerTag) { - const stepperContainerTag = createTag('div', { class: 'np-stepper-container' }); - const getStepTag = (number) => { - const stepContainerTag = createTag('div', { class: 'np-step-container', 'data-step': number }); - const stepIconTag = createTag('span', { class: 'np-step-icon' }, number); - const stepNameTag = createTag( - 'span', - { class: 'np-step-name' }, - window.mph[`nonprofit-step-${number}`], - ); - stepContainerTag.append(stepIconTag, stepNameTag); - return stepContainerTag; - }; - - const step1 = getStepTag(1); - const step2 = getStepTag(2); - const step3 = getStepTag(3); - - stepperStore.subscribe(({ step, scenario }) => { - // Reset steps - step1.classList.remove('is-cleared', 'is-active'); - step2.classList.remove('is-cleared', 'is-active'); - step3.classList.remove('is-cleared', 'is-active'); - - if (step === 1) { - step1.classList.add('is-active'); - } - if (step === 2) { - step1.classList.add('is-cleared'); - step2.classList.add('is-active'); - } - if (step === 3) { - if (scenario === SCENARIOS.FOUND_IN_SEARCH) { - step1.classList.add('is-cleared'); - step2.classList.add('is-cleared'); - step3.classList.add('is-active'); - } else { - step1.classList.add('is-cleared'); - step2.classList.add('is-active'); - } - } - if (step === 4) { - step1.classList.add('is-cleared'); - step2.classList.add('is-active'); - } - if (step === 5) { - step1.classList.add('is-cleared'); - step2.classList.add('is-cleared'); - step3.classList.add('is-active'); - } - }); - - const separatorIconTag = getNonprofitIconTag(NONPRFIT_ICONS.CHEVRON_RIGHT); - separatorIconTag.classList.add('np-step-separator'); - - stepperContainerTag.append( - step1, - separatorIconTag.cloneNode(true), - step2, - separatorIconTag.cloneNode(true), - step3, - ); - - const stepBackTag = getStepBackTag(); - - containerTag.append(stepperContainerTag, stepBackTag); -} - -// #region Render form - -function getDescriptionTag(title, subtitle) { - const descriptionTag = createTag('div', { class: 'np-description' }); - const titleTag = createTag('span', { class: 'np-title' }, title); - - descriptionTag.append(titleTag); - - if (subtitle) { - const subtitleTag = createTag('span', { class: 'np-subtitle' }, subtitle); - - descriptionTag.append(subtitleTag); - } - - return descriptionTag; -} - -function getSubmitTag() { - return createTag('input', { - class: 'np-button', - type: 'submit', - value: window.mph['nonprofit-continue'], - disabled: 'disabled', - }); -} - -function getNonprofitInput(params) { - const { type, name, label, placeholder, required } = params; - const baseParams = { name, placeholder }; - if (required) baseParams.required = 'required'; - const controlTag = createTag('div', { class: 'np-control' }); - const labelTag = createTag('label', { class: 'np-label', for: name }, label); - const inputTag = createTag('input', { - class: `np-input${required ? ' np-required-field' : ''}`, - type, - ...baseParams, - }); - controlTag.append(labelTag, inputTag); - - // File validation - if (type === 'file') { - // Hide input and render a text one - inputTag.style.display = 'none'; - const textTag = createTag('input', { - type: 'text', - class: 'np-input', - placeholder, - readonly: 'readonly', - 'data-for': name, - }); - - textTag.addEventListener('click', () => { - inputTag.click(); - }); - - textTag.addEventListener('keypress', (ev) => { - if (ev.code !== 'Enter') return; - ev.preventDefault(); - inputTag.click(); - }); - - // Validation - inputTag.addEventListener('change', () => { - if (!inputTag.files || inputTag.files.length === 0) { - textTag.value = ''; - return; - } - - const file = inputTag.files[0]; - - // Percent only accepts jpg, png and pdf files - const extensionRegex = /(\.jpg|\.jpeg|\.png|\.pdf)$/i; - if (!extensionRegex.exec(file.name)) { - inputTag.value = ''; - inputTag.dispatchEvent(new Event('input')); - alert(window.mph['nonprofit-invalid-file-type']); - return; - } - - // Percent acceps files up to 5 mb - const size = file.size / 1024 ** 2; - if (size > 5) { - inputTag.value = ''; - inputTag.dispatchEvent(new Event('input')); - alert(window.mph['nonprofit-file-size-exceeded']); - return; - } - - textTag.value = file.name; - }); - - const uploadIconTag = getNonprofitIconTag(NONPRFIT_ICONS.UPLOAD); - - controlTag.append(textTag, uploadIconTag); - } - - return controlTag; -} - -function getSelectedOrganizationTag() { - const containerTag = createTag('div', { class: 'np-selected-organization-container' }); - - const headerTag = createTag('div', { class: 'np-selected-organization-header' }); - - const avatarTag = createTag('div', { class: 'np-selected-organization-avatar' }); - - const initialsTag = createTag('span', { class: 'np-selected-organization-initials' }); - const showInitials = () => { - avatarTag.classList.add('fallback'); - const initialWords = selectedOrganizationStore.data.name - .split(' ') - .filter((word) => Boolean(word)) - .slice(0, 2); - const initials = initialWords.map((word) => word.substring(0, 1).toUpperCase()).join(''); - initialsTag.textContent = initials; - }; - - const logoTag = createTag('img', { class: 'np-selected-organization-logo' }); - logoTag.addEventListener('error', () => { - avatarTag.classList.remove('loading'); - showInitials(); - }); - logoTag.addEventListener('load', () => { - avatarTag.classList.remove('loading'); - }); - - avatarTag.append(initialsTag, logoTag); - - const nameTag = createTag('span', { class: 'np-selected-organization-detail' }); - headerTag.append(avatarTag, nameTag); - - const separatorTag = createTag('div', { class: 'np-selected-organization-separator' }); - - const addressTag = createTag('span', { class: 'np-selected-organization-detail' }); - const idTag = createTag('span', { class: 'np-selected-organization-detail' }); - - const clearTag = createTag('div', { class: 'np-selected-organization-clear', tabindex: 0 }); - const clearIconTag = getNonprofitIconTag(NONPRFIT_ICONS.CLOSE); - clearTag.append(clearIconTag); - - clearTag.addEventListener('keydown', (ev) => { - if (ev.code !== 'Enter') return; - clearTag.click(); - }); - - containerTag.append(headerTag, separatorTag, addressTag, idTag, clearTag); - - selectedOrganizationStore.subscribe((organization) => { - if (!organization) { - containerTag.style.display = 'none'; - return; - } - - // Load avatar - if (organization.logo) { - avatarTag.classList.add('loading'); - avatarTag.classList.remove('fallback'); - logoTag.src = organization.logo; - } else { - showInitials(); - } - - nameTag.textContent = organization.name; - - addressTag.textContent = organization.address || '-'; - addressTag.setAttribute('title', organization.address); - idTag.textContent = organization.id; - idTag.setAttribute('title', organization.id); - - containerTag.style.display = 'flex'; - }, false); - - containerTag.onClear = (handler) => { - clearTag.addEventListener('click', handler); - }; - - return containerTag; -} - -function trackSubmitCondition(formTag) { - const requiredInputs = formTag.querySelectorAll('.np-required-field'); - const submitTag = formTag.querySelector('.np-button[type=submit]'); - - for (let index = 0; index < requiredInputs.length; index++) { - const requiredInput = requiredInputs[index]; - requiredInput.addEventListener('input', () => { - if (!requiredInput.value) { - submitTag.setAttribute('disabled', 'disabled'); - } else { - let hasEmptyFields = false; - requiredInputs.forEach((input) => { - if (input === requiredInput) return; - if (!input.value) hasEmptyFields = true; - }); - if (hasEmptyFields) { - submitTag.setAttribute('disabled', 'disabled'); - } else { - submitTag.removeAttribute('disabled'); - } - } - }); - } -} - -// Select non-profit -function renderSelectNonprofit(containerTag) { - // Description - const descriptionTag = getDescriptionTag( - window.mph['nonprofit-title-select-non-profit'], - window.mph['nonprofit-subtitle-select-non-profit'], - ); - - // Form - const formTag = createTag('form', { class: 'np-form' }); - - const countryTag = nonprofitSelect({ - createTag, - name: 'country', - label: window.mph['nonprofit-country'], - placeholder: window.mph['nonprofit-country-placeholder'], - options: countries, - labelKey: 'name', - valueKey: 'code', - }); - - // #region Organization select - const organizationTag = nonprofitSelect({ - createTag, - name: 'organizationId', - label: window.mph['nonprofit-organization-name-or-id'], - placeholder: window.mph['nonprofit-organization-name-or-id-search-placeholder'], - noOptionsText: window.mph['nonprofit-not-found-in-database'], - debounce: SEARCH_DEBOUNCE, - store: organizationsStore, - disabled: true, - hideIcon: true, - clearable: true, - labelKey: 'name', - valueKey: 'id', - renderOption: (option, itemTag) => { - const nameTag = createTag( - 'span', - { class: 'np-organization-select-name', title: option.name }, - option.name, - ); - const idTag = createTag( - 'span', - { class: 'np-organization-select-id', title: option.id }, - option.id, - ); - - itemTag.append(nameTag, idTag); - }, - footerTag: (() => { - const cannotFindTag = createTag('div', { class: 'np-select-list-tag np-organization-cannot-find' }); - const cannotFindLinkTag = createTag( - 'a', - { tabindex: 0 }, - window.mph['nonprofit-organization-cannot-find'], - ); - // Cannot find action handler - const switchToNotFound = () => { - stepperStore.update((prev) => ({ - ...prev, - step: 2, - scenario: SCENARIOS.NOT_FOUND_IN_SEARCH, - })); - }; - cannotFindLinkTag.addEventListener('click', switchToNotFound); - cannotFindLinkTag.addEventListener('keydown', (ev) => { - if (ev.code !== 'Enter') return; - switchToNotFound(); - }); - - cannotFindTag.append(cannotFindLinkTag); - - return cannotFindTag; - })(), - }); - - organizationTag.onInput((value, abortController) => { - if (!value) return; - fetchOrganizations(value, countryTag.getValue(), abortController); - }); - - organizationTag.onSelect((option) => { - selectedOrganizationStore.update(option); - }); - - organizationTag.onScroll((listTag, abortController, hasNewInput) => { - if ( - (Boolean(selectedOrganizationStore.data) && !hasNewInput) - || organizationsStore.loading - || !nextOrganizationsPageUrl - ) return; - if (listTag.scrollTop + listTag.clientHeight + FETCH_ON_SCROLL_OFFSET >= listTag.scrollHeight) { - fetchNextOrganizations(abortController); - } - }); - - countryTag.onSelect(() => { - organizationTag.enable(); - organizationTag.clear(); - if (selectedOrganizationStore.data) { - selectedOrganizationStore.update(null); - } - }); - - // #endregion - - const selectedOrganizationTag = getSelectedOrganizationTag(); - - selectedOrganizationTag.onClear(() => { - organizationTag.clear(); - selectedOrganizationStore.update(null); - }); - - const submitTag = getSubmitTag(); - - formTag.append(countryTag, organizationTag, selectedOrganizationTag, submitTag); - - trackSubmitCondition(formTag); - - formTag.addEventListener('submit', (ev) => { - ev.preventDefault(); - - const formData = new FormData(formTag); - nonprofitFormData.countryCode = formData.get('country'); - nonprofitFormData.organizationId = formData.get('organizationId'); - - stepperStore.update((prev) => ({ ...prev, scenario: SCENARIOS.FOUND_IN_SEARCH, step: 2 })); - }); - - containerTag.replaceChildren(descriptionTag, formTag); -} - -// Organization details -function renderOrganizationDetails(containerTag) { - // Description - const descriptionTag = getDescriptionTag(window.mph['nonprofit-title-organization-details']); - - // Form - const formTag = createTag('form', { class: 'np-form' }); - - let abortController; - - const countryTag = nonprofitSelect({ - createTag, - name: 'country', - label: window.mph['nonprofit-country'], - placeholder: window.mph['nonprofit-country-placeholder'], - options: countries, - labelKey: 'name', - valueKey: 'code', - }); - - countryTag.onSelect((option) => { - abortController?.abort(); - abortController = new AbortController(); - fetchRegistries(option.code, abortController); - }); - - const organizationNameTag = getNonprofitInput({ - type: 'text', - name: 'organizationName', - label: window.mph['nonprofit-organization-name'], - placeholder: window.mph['nonprofit-organization-name-placeholder'], - required: true, - }); - - const registryTag = nonprofitSelect({ - createTag, - name: 'registry', - label: window.mph['nonprofit-registry'], - placeholder: window.mph['nonprofit-registry-placeholder'], - labelKey: 'name', - valueKey: 'name', - disabled: true, - }); - - registriesStore.subscribe((registries, loading) => { - if (!countryTag.getValue()) return; - if (loading) { - registryTag.clear(false); - return; - } - registryTag.enable(); - registryTag.updateOptions(registries); - }); - - const organizationRegistrationIdTag = getNonprofitInput({ - type: 'text', - name: 'organizationRegistrationId', - label: window.mph['nonprofit-organization-registration-id'], - placeholder: window.mph['nonprofit-organization-registration-id-placeholder'], - required: true, - }); - - const evidenceNonProfitStatusTag = getNonprofitInput({ - type: 'file', - name: 'evidenceNonProfitStatus', - label: window.mph['nonprofit-evidence-non-profit-status'], - placeholder: window.mph['nonprofit-evidence-non-profit-status-placeholder'], - required: true, - }); - - const websiteTag = getNonprofitInput({ - type: 'text', - name: 'website', - label: window.mph['nonprofit-website'], - placeholder: window.mph['nonprofit-website-placeholder'], - required: true, - }); - - const submitTag = getSubmitTag(); - - formTag.append( - countryTag, - organizationNameTag, - registryTag, - organizationRegistrationIdTag, - evidenceNonProfitStatusTag, - websiteTag, - submitTag, - ); - - trackSubmitCondition(formTag); - - formTag.addEventListener('submit', (ev) => { - ev.preventDefault(); - - const formData = new FormData(formTag); - nonprofitFormData.countryCode = formData.get('country'); - nonprofitFormData.organizationName = formData.get('organizationName'); - nonprofitFormData.registryName = formData.get('registry'); - nonprofitFormData.organizationRegistrationId = formData.get('organizationRegistrationId'); - nonprofitFormData.evidenceNonProfitStatus = formData.get('evidenceNonProfitStatus'); - nonprofitFormData.website = formData.get('website'); - - stepperStore.update((prev) => ({ ...prev, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH, step: 3 })); - }); - - containerTag.replaceChildren(descriptionTag, formTag); -} - -// Organization address -function renderOrganizationAddress(containerTag) { - // Description - const descriptionTag = getDescriptionTag(window.mph['nonprofit-title-organization-address']); - - // Form - const formTag = createTag('form', { class: 'np-form' }); - - const streetAddressTag = getNonprofitInput({ - type: 'text', - name: 'streetAddress', - label: window.mph['nonprofit-street-address'], - placeholder: window.mph['nonprofit-street-address-placeholder'], - required: true, - }); - - const addressDetailsTag = getNonprofitInput({ - type: 'text', - name: 'addressDetails', - label: window.mph['nonprofit-address-details'], - placeholder: window.mph['nonprofit-address-details-placeholder'], - }); - - const stateTag = getNonprofitInput({ - type: 'text', - name: 'state', - label: window.mph['nonprofit-state'], - placeholder: window.mph['nonprofit-state-placeholder'], - }); - - const cityTag = getNonprofitInput({ - type: 'text', - name: 'city', - label: window.mph['nonprofit-city'], - placeholder: window.mph['nonprofit-city-placeholder'], - required: true, - }); - - const zipCodeTag = getNonprofitInput({ - type: 'text', - name: 'zipCode', - label: window.mph['nonprofit-zip-code'], - placeholder: window.mph['nonprofit-zip-code-placeholder'], - required: true, - }); - - const submitTag = getSubmitTag(); - - formTag.append(streetAddressTag, addressDetailsTag, stateTag, cityTag, zipCodeTag, submitTag); - - trackSubmitCondition(formTag); - - formTag.addEventListener('submit', (ev) => { - ev.preventDefault(); - - const formData = new FormData(formTag); - nonprofitFormData.streetAddress = formData.get('streetAddress'); - nonprofitFormData.addressDetails = formData.get('addressDetails'); - nonprofitFormData.state = formData.get('state'); - nonprofitFormData.city = formData.get('city'); - nonprofitFormData.zipCode = formData.get('zipCode'); - - stepperStore.update((prev) => ({ ...prev, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH, step: 4 })); - }); - - containerTag.replaceChildren(descriptionTag, formTag); -} - -// Personal data -function renderPersonalData(containerTag) { - // Description - const descriptionTag = getDescriptionTag( - window.mph['nonprofit-title-personal-details'], - window.mph['nonprofit-subtitle-personal-details'], - ); - - // Form - const formTag = createTag('form', { class: 'np-form' }); - - const firstNameTag = getNonprofitInput({ - type: 'text', - name: 'firstName', - label: window.mph['nonprofit-first-name'], - placeholder: window.mph['nonprofit-first-name-placeholder'], - required: true, - }); - - const lastNameTag = getNonprofitInput({ - type: 'text', - name: 'lastName', - label: window.mph['nonprofit-last-name'], - placeholder: window.mph['nonprofit-last-name-placeholder'], - required: true, - }); - - const emailTag = getNonprofitInput({ - type: 'text', - name: 'email', - label: window.mph['nonprofit-email'], - placeholder: window.mph['nonprofit-email-placeholder'], - required: true, - }); - - const disclaimerTag = createTag( - 'span', - { class: 'np-personal-data-disclaimer' }, - window.mph['nonprofit-personal-data-disclaimer'], - ); - - const submitTag = getSubmitTag(); - - formTag.append(firstNameTag, lastNameTag, emailTag, disclaimerTag, submitTag); - - trackSubmitCondition(formTag); - - formTag.addEventListener('submit', async (ev) => { - ev.preventDefault(); - - const formData = new FormData(formTag); - nonprofitFormData.firstName = formData.get('firstName'); - nonprofitFormData.lastName = formData.get('lastName'); - nonprofitFormData.email = formData.get('email'); - - const inputs = formTag.querySelectorAll('input'); - inputs.forEach((input) => { - input.setAttribute('disabled', 'disabled'); - }); - - stepperStore.update((prev) => ({ ...prev, pending: true })); - - const ok = await sendOrganizationData(); - - if (!ok) { - inputs.forEach((input) => { - input.removeAttribute('disabled'); - }); - - stepperStore.update((prev) => ({ ...prev, pending: false })); - } else { - stepperStore.update((prev) => ({ ...prev, step: prev.step + 1 })); - } - }); - - containerTag.replaceChildren(descriptionTag, formTag); -} - -function renderApplicationReview(containerTag) { - const applicationReviewTag = createTag('div', { class: 'np-application-review-container' }); - - const titleTag = createTag( - 'span', - { class: 'np-title' }, - window.mph['nonprofit-title-application-review'], - ); - const detail1Tag = createTag( - 'span', - { class: 'np-application-review-detail' }, - window.mph['nonprofit-detail-1-application-review'], - ); - const detail2Tag = createTag( - 'span', - { class: 'np-application-review-detail' }, - window.mph['nonprofit-detail-2-application-review']?.replace( - '__EMAIL__', - nonprofitFormData.email, - ), - ); - - applicationReviewTag.append(titleTag, detail1Tag, detail2Tag); - - const returnToAcrobatForNonprofitsTag = createTag( - 'button', - { class: 'np-button' }, - window.mph['nonprofit-return-to-acrobat-for-nonprofits'], - ); - - containerTag.replaceChildren(applicationReviewTag, returnToAcrobatForNonprofitsTag); -} - -function renderStepContent(containerTag) { - const contentContainerTag = createTag('div', { class: 'np-content-container' }); - - let currentStep; - let currentScenario; - stepperStore.subscribe(({ step, scenario }) => { - if (step === currentStep && scenario === currentScenario) return; - currentStep = step; - currentScenario = scenario; - - if (step === 1) renderSelectNonprofit(contentContainerTag); - if (step === 2 && scenario === SCENARIOS.FOUND_IN_SEARCH) renderPersonalData(contentContainerTag); - if (step === 2 && scenario === SCENARIOS.NOT_FOUND_IN_SEARCH) renderOrganizationDetails(contentContainerTag); - if (step === 3 && scenario === SCENARIOS.FOUND_IN_SEARCH) renderApplicationReview(contentContainerTag); - if (step === 3 && scenario === SCENARIOS.NOT_FOUND_IN_SEARCH) renderOrganizationAddress(contentContainerTag); - if (step === 4 && scenario === SCENARIOS.NOT_FOUND_IN_SEARCH) renderPersonalData(contentContainerTag); - if (step === 5 && scenario === SCENARIOS.NOT_FOUND_IN_SEARCH) renderApplicationReview(contentContainerTag); - }); - - containerTag.append(contentContainerTag); -} - -// #endregion - -// #region Stepper Controller (TODO - remove) - -function initStepperController(tag) { - const containerTag = createTag('div', { class: 'np-controller-container' }); - - const titleTag = createTag( - 'span', - { class: 'np-controller-title' }, - 'Stepper controller (for testing)', - ); - - const scenariosTag = createTag('div', { class: 'np-controller-section' }); - const stepsTag = createTag('div', { class: 'np-controller-section' }); - - stepperStore.subscribe(() => { - const { step, scenario } = stepperStore.data; - - const foundInSearchTag = createTag( - 'button', - { class: `np-controller-button${scenario === SCENARIOS.FOUND_IN_SEARCH ? ' selected' : ''}` }, - 'Found in search', - ); - foundInSearchTag.addEventListener('click', () => { - const newStep = step > 3 ? 1 : step; - stepperStore.update((prev) => ({ - ...prev, - step: newStep, - scenario: SCENARIOS.FOUND_IN_SEARCH, - })); - }); - const notFoundInSearchTag = createTag( - 'button', - { class: `np-controller-button${scenario === SCENARIOS.NOT_FOUND_IN_SEARCH ? ' selected' : ''}` }, - 'Not found in search', - ); - notFoundInSearchTag.addEventListener('click', () => stepperStore.update((prev) => ({ ...prev, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH }))); - - const maxSteps = scenario === SCENARIOS.FOUND_IN_SEARCH ? 3 : 5; - - stepsTag.replaceChildren(); - Array.from({ length: maxSteps }) - .map((_, index) => index + 1) - .forEach((value) => { - const stepTag = createTag( - 'button', - { class: `np-controller-button is-step ${step === value ? ' selected' : ''}` }, - value, - ); - stepTag.addEventListener('click', () => stepperStore.update((prev) => ({ ...prev, step: value }))); - stepsTag.append(stepTag); - }); - - scenariosTag.replaceChildren(foundInSearchTag, notFoundInSearchTag); - }); - - containerTag.append(titleTag, scenariosTag, stepsTag); - - const bufferTag = createTag('div', { class: 'np-controller-buffer ' }); - tag.append(bufferTag, containerTag); -} - -// #endregion - -function initNonprofit(element) { - const containerTag = createTag('div', { class: 'np-container' }); - - renderStepper(containerTag); - renderStepContent(containerTag); - - element.append(containerTag); - - // TODO - remove - initStepperController(element); -} - -export default function init(element) { - // Get metadata - removeOptionElements(element); - initNonprofit(element); -} diff --git a/acrobat/blocks/verb-widget/icons.js b/acrobat/blocks/verb-widget/icons.js new file mode 100644 index 00000000..67a45713 --- /dev/null +++ b/acrobat/blocks/verb-widget/icons.js @@ -0,0 +1,27 @@ +// icons.js + +// Object mapping icon names to SVG strings +const ICONS = { + WIDGET_ICON: '', + fillsign: '', + UPLOAD_ICON: '', + SECURITY_ICON: '', + INFO_ICON: '', + CLOSE_ICON: '', +}; + +export default function createSvgElement(iconName) { + const svgString = ICONS[iconName]; + + if (!svgString) { + // eslint-disable-next-line no-console + console.warn(`Icon not found: ${iconName}`); + return null; + } + + const parser = new DOMParser(); + const svgDoc = parser.parseFromString(svgString, 'image/svg+xml'); + const svgElement = svgDoc.documentElement; + + return svgElement; +} diff --git a/acrobat/blocks/verb-widget/limits.js b/acrobat/blocks/verb-widget/limits.js index 298a1073..4457b30b 100644 --- a/acrobat/blocks/verb-widget/limits.js +++ b/acrobat/blocks/verb-widget/limits.js @@ -2,23 +2,23 @@ const LIMITS = { fillsign: { maxFileSize: 100000000, // 100 MB maxFileSizeFriendly: '100 MB', // 100 MB - acceptedFiles: ['application/pdf'], + acceptedFiles: '.pdf', maxNumFiles: 1, mobileApp: true, }, 'delete-pages': { maxFileSize: 100000000, // 1 MB - acceptedFiles: ['application/pdf'], + acceptedFiles: '.pdf', maxNumFiles: 1, }, 'number-pages': { maxFileSize: 100000000, // 1 MB - acceptedFiles: ['application/pdf'], + acceptedFiles: '.pdf', maxNumFiles: 1, }, 'compress-pdf': { maxFileSize: 100000000, - acceptedFiles: ['application/pdf'], + acceptedFiles: '.pdf', maxNumFiles: 1, }, }; diff --git a/acrobat/blocks/verb-widget/verb-widget.css b/acrobat/blocks/verb-widget/verb-widget.css index 74458b00..38778564 100644 --- a/acrobat/blocks/verb-widget/verb-widget.css +++ b/acrobat/blocks/verb-widget/verb-widget.css @@ -10,8 +10,15 @@ color: initial; } +.verb-legal-wrapper { + align-items: flex-start; + display: flex; + flex-flow: column; +} + .verb-legal { margin-top: 30px; + margin-bottom: 0; font-size: 14px; line-height: 20px; max-width: 527px; @@ -20,39 +27,49 @@ color: #2c2c2c; } +.verb-legal-two { + margin-top: 0; +} + .verb-icon { - height: 56px; - min-width: 56px; - background-image: url('../../img/icons/widget-icon.png'); - background-size: cover; display: inline-flex; + width: 56px; + height: 56px; } -.verb-big-icon { - width: 500px; - height: 500px; - background-image: url('../../img/icons/chat-pdf.svg'); - background-size: contain; - margin: auto; +.verb-image { + width: 100%; + height: 100%; + background-size: cover; background-repeat: no-repeat; - display: inline; - float: right; - position: absolute; - right: 0; - top: 40px; + background-position: center; + max-width: 345px; +} + +.verb-image svg { + display: block; + width: 100%; + } .security-icon { + display: flex; width: 42px; height: 42px; - background-image: url('../../img/icons/widget-sec.png'); - background-size: contain; - background-repeat: no-repeat; - display: inline-block; + margin: 15px 3px 0 0; +} + +.info-icon { + display: flex; + margin: 15px 0 0 20px; } .verb-footer { text-align: center; + align-items: center; + display: flex; + flex-flow: row; + justify-content: center; } .verb-footer a { @@ -122,6 +139,7 @@ } .verb-cta { + cursor: pointer; background: #1473e6; border-radius: 8px; padding: 11px 27px; @@ -137,11 +155,7 @@ border: none; } -.verb-cta::before { - content: ''; - background-image: url('/acrobat/img/icons/upload-icon.svg'); - background-repeat: no-repeat; - background-size: 32px 28px; +.verb-cta .upload-icon { display: flex; align-items: center; justify-content: center; @@ -158,8 +172,8 @@ .verb-error { background: #D31510; - min-width: 506px; - padding: 11px 27px; + max-width: 506px; + padding: 11px 37px 11px 20px; color: white; white-space: nowrap; display: flex; @@ -178,22 +192,10 @@ } .verb-errorBtn { - position: absolute; - right: 0; - top: 0; -} - -.verb-errorBtn::after { - content: ''; - background-image: url('/acrobat/img/icons/ui/close.svg'); - background-repeat: no-repeat; - background-size: 45px 50px; - display: flex; - align-items: center; - justify-content: center; - width: 50px; - height: 50px; cursor: pointer; + position: absolute; + right: 10px; + bottom: 0; } .verb-errorIcon { @@ -251,6 +253,7 @@ flex: 1 1 55%; flex-direction: row; padding: 48px 0 48px 48px; + /* cursor: pointer; */ } .verb-mobile-cta { @@ -283,8 +286,8 @@ } .verb-icon { + width: 40px; height: 40px; - min-width: 40px; } .verb-title { @@ -300,7 +303,7 @@ } .verb-cta { - /* display: none; */ + cursor: pointer; } .verb-mobile-cta { @@ -318,4 +321,3 @@ width: 30%; } } - diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index d56cae1b..cd92fb28 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -1,9 +1,10 @@ import LIMITS from './limits.js'; import { setLibs, getEnv, isOldBrowser } from '../../scripts/utils.js'; import verbAnalytics from '../../scripts/alloy/verb-widget.js'; +import createSvgElement from './icons.js'; const miloLibs = setLibs('/libs'); -const { createTag } = await import(`${miloLibs}/utils/utils.js`); +const { createTag, getConfig } = await import(`${miloLibs}/utils/utils.js`); const fallBack = 'https://www.adobe.com/go/acrobat-overview'; const EOLBrowserPage = 'https://acrobat.adobe.com/home/index-browser-eol.html'; @@ -79,8 +80,10 @@ export default async function init(element) { return; } - const ppURL = 'https://www.adobe.com/privacy/policy.html'; - const touURL = 'https://www.adobe.com/legal/terms.html'; + const { locale } = getConfig(); + + const ppURL = window.mph['verb-widget-privacy-policy-url'] || `https://www.adobe.com${locale.prefix}/privacy/policy.html`; + const touURL = window.mph['verb-widget-terms-of-use-url'] || `https://www.adobe.com${locale.prefix}/legal/terms.html`; const children = element.querySelectorAll(':scope > div'); const VERB = element.classList[1]; @@ -103,22 +106,56 @@ export default async function init(element) { const widgetRight = createTag('div', { class: 'verb-col right' }); const widgetHeader = createTag('div', { class: 'verb-header' }); const widgetIcon = createTag('div', { class: 'verb-icon' }); + const widgetIconSvg = createSvgElement('WIDGET_ICON'); + if (widgetIconSvg) { + widgetIconSvg.classList.add('icon-verb'); + widgetIcon.appendChild(widgetIconSvg); + } const widgetTitle = createTag('div', { class: 'verb-title' }, 'Adobe Acrobat'); const widgetCopy = createTag('p', { class: 'verb-copy' }, window.mph[`verb-widget-${VERB}-description`]); const widgetMobCopy = createTag('p', { class: 'verb-copy' }, window.mph[`verb-widget-${VERB}-mobile-description`]); - const widgetButton = createTag('button', { for: 'file-upload', class: 'verb-cta', tabindex: 0 }, window.mph['verb-widget-cta']); + const widgetButton = createTag('button', { for: 'file-upload', class: 'verb-cta', tabindex: 0 }); + const widgetButtonLabel = createTag('span', { class: 'verb-cta-label' }, window.mph['verb-widget-cta']); + widgetButton.append(widgetButtonLabel); + const uploadIconSvg = createSvgElement('UPLOAD_ICON'); + if (uploadIconSvg) { + uploadIconSvg.classList.add('upload-icon'); + widgetButton.prepend(uploadIconSvg); + } + const widgetMobileButton = createTag('a', { class: 'verb-mobile-cta', href: mobileLink }, window.mph['verb-widget-cta-mobile']); - const button = createTag('input', { type: 'file', id: 'file-upload', class: 'hide', 'aria-hidden': true }); - const widgetImage = createTag('img', { class: 'verb-image', src: `/acrobat/img/verb-widget/${VERB}.png`, alt: '' }); + const button = createTag('input', { type: 'file', accept: LIMITS[VERB].acceptedFiles, id: 'file-upload', class: 'hide', 'aria-hidden': true }); + const widgetImage = createTag('div', { class: 'verb-image' }); + const verbIconName = `${VERB}`; + const verbImageSvg = createSvgElement(verbIconName); + if (verbImageSvg) { + verbImageSvg.classList.add('icon-verb-image'); + widgetImage.appendChild(verbImageSvg); + } + // Since we're using placeholders we need a solution for the hyperlinks + const legalWrapper = createTag('div', { class: 'verb-legal-wrapper' }); const legal = createTag('p', { class: 'verb-legal' }, `${window.mph['verb-widget-legal']} `); + const legalTwo = createTag('p', { class: 'verb-legal verb-legal-two' }, `${window.mph['verb-widget-legal-2']} `); const iconSecurity = createTag('div', { class: 'security-icon' }); + const infoIcon = createTag('div', { class: 'info-icon milo-tooltip right', 'data-tooltip': `${window.mph['verb-widget-tool-tip']}` }); + const securityIconSvg = createSvgElement('SECURITY_ICON'); + const infoIconSvg = createSvgElement('INFO_ICON'); + if (securityIconSvg) { + iconSecurity.appendChild(securityIconSvg); + infoIcon.appendChild(infoIconSvg); + } const footer = createTag('div', { class: 'verb-footer' }); const errorState = createTag('div', { class: 'hide' }); const errorStateText = createTag('p', { class: 'verb-errorText' }); const errorIcon = createTag('div', { class: 'verb-errorIcon' }); const errorCloseBtn = createTag('div', { class: 'verb-errorBtn' }); + const closeIconSvg = createSvgElement('CLOSE_ICON'); + if (closeIconSvg) { + closeIconSvg.classList.add('close-icon'); + errorCloseBtn.prepend(closeIconSvg); + } widget.append(widgetContainer); widgetContainer.append(widgetRow); @@ -131,18 +168,17 @@ export default async function init(element) { element.append(widget); } else { widgetLeft.append(widgetHeader, widgetHeading, widgetCopy, errorState, widgetButton, button); - // Make ticket to localize links - legal.innerHTML = legal.outerHTML.replace(window.mph['verb-widget-terms-of-use'], ` ${window.mph['verb-widget-terms-of-use']}`); - legal.innerHTML = legal.outerHTML.replace(window.mph['verb-widget-privacy-policy'], ` ${window.mph['verb-widget-privacy-policy']}`); + legalTwo.innerHTML = legalTwo.outerHTML.replace(window.mph['verb-widget-terms-of-use'], ` ${window.mph['verb-widget-terms-of-use']}`); + legalTwo.innerHTML = legalTwo.outerHTML.replace(window.mph['verb-widget-privacy-policy'], ` ${window.mph['verb-widget-privacy-policy']}`); - footer.append(iconSecurity, legal); + legalWrapper.append(legal, legalTwo); + footer.append(iconSecurity, legalWrapper, infoIcon); element.append(widget, footer); } // Redirect after IMS:Ready window.addEventListener('IMS:Ready', () => { - console.log('IMS:Ready 😎'); if (window.adobeIMS.isSignedInUser() && window.adobeIMS.getAccountType() !== 'type1') { redDir(VERB); @@ -151,7 +187,6 @@ export default async function init(element) { // Race Condition if (window.adobeIMS?.isSignedInUser() && window.adobeIMS?.getAccountType() !== 'type1') { - console.log('Race Con ⏩'); redDir(VERB); } @@ -164,6 +199,10 @@ export default async function init(element) { verbAnalytics('goto-app:clicked', VERB); }); + // widget.addEventListener('click', () => { + // if (!mobileLink) { button.click(); } + // }); + widgetButton.addEventListener('click', () => { button.click(); }); @@ -188,11 +227,6 @@ export default async function init(element) { setDraggingClass(widget, false); }); - widget.addEventListener('drop', (e) => { - e.preventDefault(); - initiatePrefetch(VERB); - }); - errorCloseBtn.addEventListener('click', () => { errorState.classList.remove('verb-error'); errorState.classList.add('hide'); @@ -205,6 +239,7 @@ export default async function init(element) { } // maybe new event name files-dropped? if (e.detail?.event === 'drop') { + initiatePrefetch(VERB); verbAnalytics('files-dropped', VERB, e.detail?.data); setDraggingClass(widget, false); setUser(); @@ -222,11 +257,16 @@ export default async function init(element) { }); // Errors, Analytics & Logging + const lanaOptions = { + sampleRate: 1, + tags: 'DC_Milo,Project Unity (DC)', + }; + const handleError = (str) => { + setDraggingClass(widget, false); errorState.classList.add('verb-error'); errorState.classList.remove('hide'); errorStateText.textContent = str; - setTimeout(() => { errorState.classList.remove('verb-error'); errorState.classList.add('hide'); @@ -235,7 +275,6 @@ export default async function init(element) { element.addEventListener('unity:show-error-toast', (e) => { // eslint-disable-next-line no-console - console.log(`⛔️ Error Code - ${e.detail?.code}`); if (e.detail?.code.includes('error_only_accept_one_file')) { handleError(e.detail?.message); verbAnalytics('error', VERB); @@ -267,6 +306,7 @@ export default async function init(element) { || e.detail?.code.includes('error_duplicate_asset')) { handleError(e.detail?.message); verbAnalytics('error', VERB); + window.lana?.log(`Error Status: ${e.detail?.message}, Error Message: ${e.detail?.status}`, lanaOptions); } // acrobat:verb-fillsign:error:page_count_missing_from_metadata_api diff --git a/acrobat/img/icons/widget-icon.svg b/acrobat/img/icons/widget-icon.svg new file mode 100644 index 00000000..90b40ee1 --- /dev/null +++ b/acrobat/img/icons/widget-icon.svg @@ -0,0 +1 @@ + diff --git a/acrobat/scripts/alloy/verb-widget.js b/acrobat/scripts/alloy/verb-widget.js index f04636c9..31aca1cf 100644 --- a/acrobat/scripts/alloy/verb-widget.js +++ b/acrobat/scripts/alloy/verb-widget.js @@ -32,7 +32,6 @@ export default function init(eventName, verb, metaData) { // eslint-disable-next-line consistent-return return tokenPayload.sub || tokenPayload.user_id; } - console.log(`📡 Event Name - acrobat:verb-${verb}:${eventName} - metaData: ${metaData?.type} / ${metaData?.size} `); const event = { documentUnloading: true, data: { diff --git a/acrobat/scripts/utils.js b/acrobat/scripts/utils.js index 849bb556..df9e5852 100644 --- a/acrobat/scripts/utils.js +++ b/acrobat/scripts/utils.js @@ -25,11 +25,12 @@ export const [setLibs, getLibs] = (() => { (prodLibs, location) => { libs = (() => { const { hostname, search } = location || window.location; + // eslint-disable-next-line compat/compat const branch = new URLSearchParams(search).get('milolibs') || 'main'; - if (branch === 'main' && hostname === 'www.stage.adobe.com') return 'https://www.adobe.com/libs'; + if (branch === 'main' && hostname === 'www.stage.adobe.com') return '/libs'; if (!(hostname.includes('.hlx.') || hostname.includes('local') || hostname.includes('stage'))) return prodLibs; if (branch === 'local') return 'http://localhost:6456/libs'; - return branch.includes('--') ? `https://${branch}.hlx.page/libs` : `https://${branch}--milo--adobecom.hlx.page/libs`; + return branch.includes('--') ? `https://${branch}.hlx.live/libs` : `https://${branch}--milo--adobecom.hlx.live/libs`; })(); return libs; }, () => libs, diff --git a/test/blocks/nonprofit/mocks/body.html b/test/blocks/nonprofit/mocks/body.html deleted file mode 100644 index 1857cd32..00000000 --- a/test/blocks/nonprofit/mocks/body.html +++ /dev/null @@ -1,5 +0,0 @@ -
-
-
-
-
diff --git a/test/blocks/nonprofit/mocks/data.js b/test/blocks/nonprofit/mocks/data.js deleted file mode 100644 index 3abfeae1..00000000 --- a/test/blocks/nonprofit/mocks/data.js +++ /dev/null @@ -1,13 +0,0 @@ -export const mockedOrganizations = [ - { id: 'atestid_1', name: 'A Test Organization 1' }, - { id: 'atestid_2', name: 'A Test Organization 2' }, - { id: 'atestid_3', name: 'A Test Organization 3' }, - { id: 'atestid_4', name: 'A Test Organization 4' }, -]; - -export const mockedRegistries = [ - { name: 'A Test Registry 1' }, - { name: 'A Test Registry 2' }, - { name: 'A Test Registry 3' }, - { name: 'A Test Registry 4' }, -]; diff --git a/test/blocks/nonprofit/nonprofit.test.js b/test/blocks/nonprofit/nonprofit.test.js deleted file mode 100644 index 5aaabc11..00000000 --- a/test/blocks/nonprofit/nonprofit.test.js +++ /dev/null @@ -1,583 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable func-names */ -/* eslint-disable compat/compat */ -import sinon from 'sinon'; -import { readFile, sendKeys } from '@web/test-runner-commands'; -import { expect } from '@esm-bundle/chai'; -// import userEvent from '@testing-library/user-event'; -import { delay, waitForElement } from '../../helpers/waitfor.js'; -import { - organizationsStore, - registriesStore, - SCENARIOS, - stepperStore, -} from '../../../acrobat/blocks/nonprofit/nonprofit.js'; -import { mockedOrganizations, mockedRegistries } from './mocks/data.js'; - -const { default: init } = await import('../../../acrobat/blocks/nonprofit/nonprofit.js'); - -const body = await readFile({ path: './mocks/body.html' }); - -describe('nonprofit - General', () => { - before(() => { - window.mph = { - 'nonprofit-title-select-non-profit': "What's your nonprofit organization?", - 'nonprofit-title-organization-details': 'Verify your organization details', - 'nonprofit-title-organization-address': "What's the physical address of your organization?", - 'nonprofit-title-personal-details': 'Confirm your details?', - 'nonprofit-subtitle-personal-details': - 'We need to confirm your name and email in order to finish checking if your organisation is eligible.', - 'nonprofit-title-application-review': 'Your application is being reviewed', - }; - }); - - beforeEach(() => { - document.body.innerHTML = body; - const np = document.querySelector('.nonprofit'); - init(np); - }); - - afterEach(() => { - stepperStore.update((prev) => ({ ...prev, step: 1, scenario: SCENARIOS.FOUND_IN_SEARCH })); - }); - - function validateStepTitle(value) { - const title = document.querySelector('.np-title'); - expect(title.textContent).to.equal(value); - } - - it('should display nonprofit', async () => { - const container = await waitForElement('.np-container'); - const form = container.querySelector('.np-form'); - const countryInput = form.querySelector('input[name="country"]'); - const organizationInput = form.querySelector('input[name="organizationId"]'); - expect(container).to.exist; - expect(countryInput).to.exist; - expect(organizationInput).to.exist; - }); - - it('should enable organizations select on country selection', async () => { - const countrySearch = document.querySelector('input[data-for="country"]'); - const organizationSearch = document.querySelector('input[data-for="organizationId"]'); - - expect(organizationSearch.getAttribute('disabled')).to.equal('disabled'); - - countrySearch.focus(); - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'Enter' }); - - expect(organizationSearch.hasAttribute('disabled')).to.be.false; - }); - - it('should change step on stepperStore update', async () => { - validateStepTitle("What's your nonprofit organization?"); - - stepperStore.update({ step: 2, scenario: SCENARIOS.FOUND_IN_SEARCH }); - validateStepTitle('Confirm your details?'); - - stepperStore.update({ step: 3, scenario: SCENARIOS.FOUND_IN_SEARCH }); - validateStepTitle('Your application is being reviewed'); - - stepperStore.update({ step: 1, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH }); - validateStepTitle("What's your nonprofit organization?"); - - stepperStore.update({ step: 2, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH }); - validateStepTitle('Verify your organization details'); - - stepperStore.update({ step: 3, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH }); - validateStepTitle("What's the physical address of your organization?"); - - stepperStore.update({ step: 4, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH }); - validateStepTitle('Confirm your details?'); - - stepperStore.update({ step: 5, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH }); - validateStepTitle('Your application is being reviewed'); - }); - - it('should go to previous step on back button click', async () => { - stepperStore.update({ step: 2, scenario: SCENARIOS.FOUND_IN_SEARCH }); - validateStepTitle('Confirm your details?'); - - const backButton = document.querySelector('.np-stepper-back'); - - expect(backButton.style.display).to.equal('flex'); - - backButton.click(); - - expect(stepperStore.data.step).to.equal(1); - }); - - it('should go to previous step on back button Enter keypress', async () => { - stepperStore.update({ step: 2, scenario: SCENARIOS.FOUND_IN_SEARCH }); - validateStepTitle('Confirm your details?'); - - const backButton = document.querySelector('.np-stepper-back'); - - expect(backButton.style.display).to.equal('flex'); - - backButton.focus(); - await sendKeys({ press: 'Enter' }); - - expect(stepperStore.data.step).to.equal(1); - }); -}); - -describe('nonprofit - Organization search', () => { - const mockedFetchReturnData = { - default: { data: mockedOrganizations, _links: { next: '' } }, - 'should search next organizations': { - data: mockedOrganizations, - _links: { next: 'a.test?link=' }, - }, - 'should navigate to alternate flow': { - data: [], - _links: { next: '' }, - }, - }; - - before(() => { - window.mph = {}; - window.lana = { log: () => {} }; - }); - - beforeEach(async function () { - document.body.innerHTML = body; - const np = document.querySelector('.nonprofit'); - init(np); - - const data = mockedFetchReturnData[this.currentTest.title] || mockedFetchReturnData.default; - - sinon.stub(window, 'fetch'); - window.fetch.returns( - Promise.resolve({ - json: () => Promise.resolve(data), - ok: !data.error, - }), - ); - - const countrySearch = document.querySelector('input[data-for="country"]'); - const organizationSearch = document.querySelector('input[data-for="organizationId"]'); - - countrySearch.focus(); - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'Enter' }); - - expect(document.activeElement).to.equal(organizationSearch); - }); - - afterEach(() => { - if (window.fetch.restore) window.fetch.restore(); - if (window.lana.log.restore) window.lana.log.restore(); - organizationsStore.update([]); - stepperStore.update((prev) => ({ ...prev, step: 1, scenario: SCENARIOS.FOUND_IN_SEARCH })); - }); - - it('should search organizations', async () => { - const selectedOrganizationContainer = document.querySelector( - '.np-selected-organization-container', - ); - - expect(selectedOrganizationContainer.style.display).to.equal(''); - - await sendKeys({ press: 'a' }); - - await waitForElement('.np-select-list[data-for="organizationId"] .np-select-item'); - - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'Enter' }); - - expect(selectedOrganizationContainer.style.display).to.equal('flex'); - }); - - it('should search next organizations', async () => { - const organizationSearch = document.querySelector('input[data-for="organizationId"]'); - expect(document.activeElement).to.equal(organizationSearch); - - await sendKeys({ press: 'a' }); - - await waitForElement('.np-select-list[data-for="organizationId"] .np-select-item'); - const organizationsList = document.querySelector('.np-select-list[data-for="organizationId"]'); - organizationsList.dispatchEvent(new Event('scroll')); - - expect(window.fetch.getCall(1).args[0]).to.equal('a.test?link='); - }); - - it('should navigate options with arrows', async () => { - await sendKeys({ press: 'a' }); - - const firstOption = await waitForElement( - '.np-select-list[data-for="organizationId"] .np-select-item', - ); - const lastOption = document.querySelector( - '.np-select-list[data-for="organizationId"] .np-select-item:last-child', - ); - - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'ArrowDown' }); - - expect(document.activeElement).to.equal(lastOption); - - await sendKeys({ press: 'ArrowUp' }); - await sendKeys({ press: 'ArrowUp' }); - await sendKeys({ press: 'ArrowUp' }); - - expect(document.activeElement).to.equal(firstOption); - }); - - it('should focus the organization search from new keypresses (except arrows and Enter) on select options', async () => { - await sendKeys({ press: 'a' }); - - const firstOption = await waitForElement( - '.np-select-list[data-for="organizationId"] .np-select-item', - ); - - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'ArrowUp' }); - - expect(document.activeElement).to.equal(firstOption); - - await sendKeys({ press: 'a' }); - - const organizationSearch = document.querySelector('input[data-for="organizationId"]'); - - expect(document.activeElement).to.equal(organizationSearch); - expect(organizationSearch.value).to.equal('aa'); - }); - - it('should reset select search value on blur without selection', async () => { - await sendKeys({ press: 'a' }); - - const organizationSearch = document.querySelector( - '.np-select-search[data-for="organizationId"]', - ); - - expect(organizationSearch.value).to.equal('a'); - - await waitForElement('.np-select-list[data-for="organizationId"] .np-select-no-options'); - organizationSearch.blur(); - - expect(organizationSearch.value).to.equal(''); - }); - - it('should navigate to personal data', async () => { - const selectedOrganizationContainer = document.querySelector( - '.np-selected-organization-container', - ); - - expect(selectedOrganizationContainer.style.display).to.equal(''); - - await sendKeys({ press: 'a' }); - - await waitForElement('.np-select-list[data-for="organizationId"] .np-select-item'); - - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'Enter' }); - - expect(selectedOrganizationContainer.style.display).to.equal('flex'); - - const form = document.querySelector('.np-form'); - form.dispatchEvent(new Event('submit')); - - expect(stepperStore.data.step).to.equal(2); - expect(stepperStore.data.scenario).to.equal(SCENARIOS.FOUND_IN_SEARCH); - }); - - it('should navigate to alternate flow', async () => { - await sendKeys({ press: 'a' }); - - await waitForElement('.np-select-list[data-for="organizationId"] .np-select-no-options'); - - await sendKeys({ press: 'Tab' }); - - const cannotFind = document.querySelector('.np-organization-cannot-find a'); - expect(document.activeElement).to.equal(cannotFind); - - await sendKeys({ press: 'Enter' }); - - expect(stepperStore.data.step).to.equal(2); - expect(stepperStore.data.scenario).to.equal(SCENARIOS.NOT_FOUND_IN_SEARCH); - }); -}); - -describe('nonprofit - Organization details', () => { - const mockedFetchReturnData = { - default: { data: mockedRegistries }, - 'should search organizations': { data: mockedRegistries }, - }; - - before(() => { - window.mph = {}; - }); - - beforeEach(async function () { - document.body.innerHTML = body; - const np = document.querySelector('.nonprofit'); - init(np); - - const data = mockedFetchReturnData[this.currentTest.title] || mockedFetchReturnData.default; - - sinon.stub(window, 'fetch'); - window.fetch.returns( - Promise.resolve({ - json: () => Promise.resolve(data), - ok: true, - }), - ); - - stepperStore.update({ step: 2, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH }); - }); - - afterEach(() => { - if (window.fetch.restore) window.fetch.restore(); - registriesStore.update([]); - }); - - it('should fetch registries on country select', async () => { - const countrySearch = document.querySelector('.np-select-search[data-for="country"]'); - - countrySearch.focus(); - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'Enter' }); - - await waitForElement('.np-select-list[data-for="registry"] .np-select-item'); - - expect(window.fetch.getCall(0)).to.not.be.null; - }); - - it('should not navigate without completed fields', async () => { - const submit = document.querySelector('input[type="submit"]'); - expect(submit).to.exist; - submit.click(); - - expect(stepperStore.data.step).to.equal(2); - }); - - // userEvent crashes on import - this test is skipped - it.skip('should navigate to address details on submit', async () => { - const countrySearch = document.querySelector('.np-select-search[data-for="country"]'); - - countrySearch.focus(); - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'Enter' }); - - await waitForElement('.np-select-list[data-for="registry"] .np-select-item'); - - const registrySearch = document.querySelector('.np-select-search[data-for="registry"]'); - - registrySearch.focus(); - await sendKeys({ press: 'ArrowDown' }); - await sendKeys({ press: 'Enter' }); - - const organizationName = document.querySelector('.np-input[name="organizationName"]'); - organizationName.value = 'A Test Name'; - organizationName.dispatchEvent(new Event('input')); - - const organizationRegistrationId = document.querySelector( - '.np-input[name="organizationRegistrationId"]', - ); - organizationRegistrationId.value = 'atestid'; - organizationRegistrationId.dispatchEvent(new Event('input')); - - // const evidenceNonProfitStatus = document.querySelector( - // '.np-input[name="evidenceNonProfitStatus"]', - // ); - // const file = new File(['evidenceofnonprofit'], 'evidenceofnonprofit.png', { type: 'image/png' }); - // await userEvent.upload(evidenceNonProfitStatus, file); - - const website = document.querySelector('.np-input[name="website"]'); - website.value = 'www.atestwebsite.com'; - website.dispatchEvent(new Event('input')); - - const submit = document.querySelector('input[type="submit"]'); - expect(submit).to.exist; - submit.click(); - - expect(stepperStore.data.step).to.equal(3); - }); -}); - -describe('nonprofit - Address details', () => { - before(() => { - window.mph = {}; - }); - - beforeEach(async () => { - document.body.innerHTML = body; - const np = document.querySelector('.nonprofit'); - init(np); - - stepperStore.update({ step: 3, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH }); - }); - - it('should not navigate without completed fields', async () => { - const submit = document.querySelector('input[type="submit"]'); - expect(submit).to.exist; - submit.click(); - - expect(stepperStore.data.step).to.equal(3); - }); - - it('should navigate to personal details on submit with all fields completed', async () => { - const streetAddress = document.querySelector('.np-input[name="streetAddress"]'); - streetAddress.value = 'A Test Street, 40'; - streetAddress.dispatchEvent(new Event('input')); - - const addressDetails = document.querySelector('.np-input[name="addressDetails"]'); - addressDetails.value = 'Block C, Floor 2'; - addressDetails.dispatchEvent(new Event('input')); - - const state = document.querySelector('.np-input[name="state"]'); - state.value = 'A Test State'; - state.dispatchEvent(new Event('input')); - - const city = document.querySelector('.np-input[name="city"]'); - city.value = 'A Test City'; - city.dispatchEvent(new Event('input')); - - const zipCode = document.querySelector('.np-input[name="zipCode"]'); - zipCode.value = 'TEST-0123'; - zipCode.dispatchEvent(new Event('input')); - - const submit = document.querySelector('input[type="submit"]'); - expect(submit).to.exist; - submit.click(); - - expect(stepperStore.data.step).to.equal(4); - }); - - it('should navigate to personal details on submit with just the required fields completed', async () => { - const streetAddress = document.querySelector('.np-input[name="streetAddress"]'); - streetAddress.value = 'A Test Street, 40'; - streetAddress.dispatchEvent(new Event('input')); - - const city = document.querySelector('.np-input[name="city"]'); - city.value = 'A Test City'; - city.dispatchEvent(new Event('input')); - - const zipCode = document.querySelector('.np-input[name="zipCode"]'); - zipCode.value = 'TEST-0123'; - zipCode.dispatchEvent(new Event('input')); - - const submit = document.querySelector('input[type="submit"]'); - expect(submit).to.exist; - submit.click(); - - expect(stepperStore.data.step).to.equal(4); - }); -}); - -describe('nonprofit - Personal details', () => { - const fillFields = () => { - const firstName = document.querySelector('.np-input[name="firstName"]'); - firstName.value = 'Atest'; - firstName.dispatchEvent(new Event('input')); - - const lastName = document.querySelector('.np-input[name="lastName"]'); - lastName.value = 'Name'; - lastName.dispatchEvent(new Event('input')); - - const email = document.querySelector('.np-input[name="email"]'); - email.value = 'atest@email.com'; - email.dispatchEvent(new Event('input')); - }; - - before(() => { - window.mph = {}; - window.lana = { log: () => {} }; - }); - - beforeEach(async () => { - document.body.innerHTML = body; - const np = document.querySelector('.nonprofit'); - init(np); - }); - - afterEach(() => { - if (window.fetch.restore) window.fetch.restore(); - if (window.lana.log.restore) window.lana.log.restore(); - }); - - it('should not submit without completed fields', async () => { - stepperStore.update({ step: 2, scenario: SCENARIOS.FOUND_IN_SEARCH }); - - const submit = document.querySelector('input[type="submit"]'); - expect(submit).to.exist; - submit.click(); - - expect(stepperStore.data.step).to.equal(2); - }); - - it('should submit completed form in found in search scenario', async () => { - sinon.stub(window, 'fetch'); - window.fetch.returns( - Promise.resolve({ - json: () => Promise.resolve({ data: { validationInviteId: 'avalidationinviteid_0123' } }), - ok: true, - }), - ); - - stepperStore.update({ step: 2, scenario: SCENARIOS.FOUND_IN_SEARCH }); - - fillFields(); - - const submit = document.querySelector('input[type="submit"]'); - expect(submit).to.exist; - submit.click(); - - await waitForElement('.np-application-review-container'); - - expect(stepperStore.data.step).to.equal(3); - }); - - it('should submit completed form in not found in search scenario', async () => { - sinon.stub(window, 'fetch'); - window.fetch.returns( - Promise.resolve({ - json: () => Promise.resolve({ data: { validationInviteId: 'avalidationinviteid_0123' } }), - ok: true, - }), - ); - - stepperStore.update({ step: 4, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH }); - - fillFields(); - - const submit = document.querySelector('input[type="submit"]'); - expect(submit).to.exist; - submit.click(); - - await waitForElement('.np-application-review-container'); - - expect(stepperStore.data.step).to.equal(5); - }); - - it('should not navigate on submission failure', async () => { - sinon.stub(window, 'fetch'); - sinon.stub(window.lana, 'log'); - - window.fetch.returns( - Promise.resolve({ - json: () => Promise.resolve({ error: { title: 'An error title', message: 'A submission failure' } }), - ok: false, - }), - ); - - stepperStore.update({ step: 2, scenario: SCENARIOS.FOUND_IN_SEARCH }); - - fillFields(); - - const submit = document.querySelector('input[type="submit"]'); - expect(submit).to.exist; - submit.click(); - - await delay(500); - - expect(stepperStore.data.step).to.equal(2); - expect(window.lana.log.getCall(0).args[0]).to.equal( - 'Could not send organization data: Error: An error title: A submission failure', - ); - }); -}); diff --git a/test/scripts/utils.test.js b/test/scripts/utils.test.js index 21a7c623..7979dc28 100644 --- a/test/scripts/utils.test.js +++ b/test/scripts/utils.test.js @@ -3,8 +3,8 @@ import * as sinon from 'sinon'; import { expect } from '@esm-bundle/chai'; describe('Test utils.js', async () => { - it('tests setLibs', async () => { + it('tests setLibs', async () => { const libs = setLibs('/lib'); - expect(libs).to.equal('https://main--milo--adobecom.hlx.page/libs'); + expect(libs).to.equal('https://main--milo--adobecom.hlx.live/libs'); }); });