Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix v3-web-component va-segmented-progress-bar Safari focus bug #27784

Open
wants to merge 63 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
de37f5b
[WIP] Fix v3-web-component va-segmented-progress-bar Safari focus bug
tlei123 Feb 1, 2024
1be8b20
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 2, 2024
e5eb272
Temporarily disable dev-page-links in 21-0966 form-config
tlei123 Feb 2, 2024
ad6202d
Temporarily disable dev-page-links option in 40-0247 form-config
tlei123 Feb 2, 2024
1ab1e61
Fix shadow-host queries in FormNav & FormPage focus methods
tlei123 Feb 2, 2024
5a48c63
Remove remnant custom-focus references from 40-0127 form-config
tlei123 Feb 2, 2024
2d2bbf0
Add note to JSDoc for customScrollAndFocus method
tlei123 Feb 3, 2024
072ec3c
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 5, 2024
2835673
Update FormPage unit-tests
tlei123 Feb 5, 2024
c247518
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 5, 2024
96c567d
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 5, 2024
fe8a5d5
Refactor appeals shared unit-test
tlei123 Feb 5, 2024
6be21d4
Restore local-dev-page-links in 21-0966 form-config
tlei123 Feb 5, 2024
fddd64d
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 6, 2024
e60ab02
Revert "Fix shadow-host queries in FormNav & FormPage focus methods"
tlei123 Feb 6, 2024
dfd918b
Revert "Revert "Fix shadow-host queries in FormNav & FormPage focus m…
tlei123 Feb 7, 2024
80d1aab
Refactor focus methods and FormPage method-calls
tlei123 Feb 7, 2024
2d2d8cc
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 9, 2024
1e20664
Fix FormNav focus async-code
tlei123 Feb 10, 2024
dbd529a
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 12, 2024
036db33
Commit 20-10207 file-deletions left over from main-branch merge
tlei123 Feb 16, 2024
6b7e6cc
Update 21-0966 local-dev-links-config and local-mock-api responses
tlei123 Feb 16, 2024
64958e9
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 16, 2024
144251f
Update 21-0247 local-dev-links config and local-mock-api responses
tlei123 Feb 16, 2024
d1c7296
Commit 20-10207 file-deletions left-over from main-merge
tlei123 Feb 16, 2024
4bc551f
WIP Refactor FormNav scroll and focus
tlei123 Feb 17, 2024
959db71
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 19, 2024
c2a8616
Refactor FormPage focusForm method
tlei123 Feb 19, 2024
ce2213d
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 19, 2024
c9f7c73
Update 40-0247 SIP mock-api-responses
tlei123 Feb 20, 2024
be08c47
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 20, 2024
d9c1cee
Fix unwanted field-input-triggered focusing in FormNav effect-hook
tlei123 Feb 21, 2024
0f4abb8
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 21, 2024
53d2c2a
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 21, 2024
92d47b6
Remove console.logs
tlei123 Feb 21, 2024
afd59ee
Add new unit-tests for getFormNavFocusTargetRoot & handleFormNavFocus
tlei123 Feb 21, 2024
5210aca
Fix customScrollAndFocus for va-segmented-progress-bar
tlei123 Feb 21, 2024
d5dd13f
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 21, 2024
464a456
Fix anomalous PP10207 file-deletions
tlei123 Feb 22, 2024
22e90ee
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 22, 2024
7eeb750
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 22, 2024
f18466a
Update 21-10210 utils unit-tests
tlei123 Feb 22, 2024
b92eadf
Refactor FormNav & FormPage scroll/focus code.
tlei123 Feb 22, 2024
4c22c7c
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 22, 2024
128d0fb
Revert "Refactor FormNav & FormPage scroll/focus code."
tlei123 Feb 22, 2024
0f99cbc
Revert "Revert "Refactor FormNav & FormPage scroll/focus code.""
tlei123 Feb 22, 2024
55da566
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 23, 2024
164ac95
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 23, 2024
033cb4f
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 26, 2024
725fecd
CI-skip secure-messaging-compose e2e-spec due to flakiness
tlei123 Feb 26, 2024
3a99559
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 26, 2024
b059f87
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 27, 2024
5b99246
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 28, 2024
5d243d1
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 28, 2024
3830f26
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Feb 29, 2024
91d1473
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Mar 1, 2024
5450bf2
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Mar 1, 2024
14d40e7
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Mar 4, 2024
239ea49
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Mar 4, 2024
64d8b23
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Mar 5, 2024
d7c500e
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Mar 6, 2024
0834304
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Mar 6, 2024
d221816
Merge branch 'main' into simple-forms/978-safari-focus-2
tlei123 Mar 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/applications/simple-forms/21-0966/config/form.js
@@ -1,13 +1,13 @@
import footerContent from 'platform/forms/components/FormFooter';

Check warning on line 1 in src/applications/simple-forms/21-0966/config/form.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/simple-forms/21-0966/config/form.js:1:1:Importing from platform via platform/forms is deprecated. Import from @department-of-veterans-affairs/platform-forms instead or check babel.config.json for an alias to the import.
import environment from '@department-of-veterans-affairs/platform-utilities/environment';
import { apiRequest } from 'platform/utilities/api';

Check warning on line 3 in src/applications/simple-forms/21-0966/config/form.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/simple-forms/21-0966/config/form.js:3:1:Importing from platform via platform/utilities is deprecated. Import from @department-of-veterans-affairs/platform-utilities instead or check babel.config.json for an alias to the import.
import manifest from '../manifest.json';

import ITFStatusLoadingIndicatorPage from '../components/ITFStatusLoadingIndicatorPage';

import transformForSubmit from '../../shared/config/submit-transformer';
import IntroductionPage from '../containers/IntroductionPage';
import ConfirmationPage from '../containers/ConfirmationPage';

Check warning on line 10 in src/applications/simple-forms/21-0966/config/form.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/simple-forms/21-0966/config/form.js:10:1:Dependency cycle via ../config/helpers:21
import getHelp from '../../shared/components/GetFormHelp';
import preparerIdentification from '../pages/preparerIdentification';
import veteranBenefitSelection from '../pages/veteranBenefitSelection';
Expand All @@ -18,7 +18,7 @@
import survivingDependentIdentificationInformation from '../pages/survivingDependentIdentificationInformation';
import survivingDependentMailingAddress from '../pages/survivingDependentMailingAddress';
import survivingDependentPhoneAndEmailAddress from '../pages/survivingDependentPhoneAndEmailAddress';
import {

Check warning on line 21 in src/applications/simple-forms/21-0966/config/form.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/simple-forms/21-0966/config/form.js:21:1:Dependency cycle detected.
preparerIsVeteran,
preparerIsSurvivingDependent,
preparerIsThirdPartyToTheVeteran,
Expand Down Expand Up @@ -71,9 +71,9 @@
},
},
formId: '21-0966',
dev: {
showNavLinks: true,
},
// dev: {
// showNavLinks: true,
// },
saveInProgress: {
// messages: {
// inProgress: 'Your benefits claims application (21-0966) is in progress.',
Expand Down
6 changes: 3 additions & 3 deletions src/applications/simple-forms/40-0247/config/form.js
@@ -1,6 +1,6 @@
// we're not using JSON schema for this form.
import environment from 'platform/utilities/environment';

Check warning on line 2 in src/applications/simple-forms/40-0247/config/form.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/simple-forms/40-0247/config/form.js:2:1:Importing from platform via platform/utilities is deprecated. Import from @department-of-veterans-affairs/platform-utilities instead or check babel.config.json for an alias to the import.
import footerContent from 'platform/forms/components/FormFooter';

Check warning on line 3 in src/applications/simple-forms/40-0247/config/form.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/simple-forms/40-0247/config/form.js:3:1:Importing from platform via platform/forms is deprecated. Import from @department-of-veterans-affairs/platform-forms instead or check babel.config.json for an alias to the import.
import getHelp from '../../shared/components/GetFormHelp';

import manifest from '../manifest.json';
Expand Down Expand Up @@ -33,9 +33,9 @@
urlPrefix: '/',
submitUrl: `${environment.API_URL}/simple_forms_api/v1/simple_forms`,
trackingPrefix: '0247-pmc',
dev: {
showNavLinks: !window.Cypress,
},
// dev: {
// showNavLinks: !window.Cypress,
// },
introduction: IntroductionPage,
confirmation: ConfirmationPage,
preSubmitInfo: {
Expand Down
20 changes: 10 additions & 10 deletions src/platform/forms-system/src/js/components/FormNav.jsx
Expand Up @@ -14,6 +14,7 @@
focusByOrder,
customScrollAndFocus,
defaultFocusSelector,
waitForRenderThenFocus,
} from '../../../../utilities/ui';

import { REVIEW_APP_DEFAULT_MESSAGE } from '../constants';
Expand Down Expand Up @@ -106,6 +107,11 @@
// https://github.com/department-of-veterans-affairs/va.gov-team/issues/12323
useEffect(
() => {
// Need to provide shadowRoot for focusing on elements inside shadow-DOM
const root = formConfig.v3SegmentedProgressBar
? document.querySelector('va-segmented-progress-bar').shadowRoot
tlei123 marked this conversation as resolved.
Show resolved Hide resolved
: document.querySelector('#react-root');

if (current > index + 1) {
setIndex(index + 1);
} else if (current === index) {
Expand All @@ -125,21 +131,15 @@
if (formConfig.useCustomScrollAndFocus && page.scrollAndFocusTarget) {
customScrollAndFocus(page.scrollAndFocusTarget, index);
} else {
focusByOrder([defaultFocusSelector, 'h2']);
waitForRenderThenFocus(defaultFocusSelector, root, 400);
}
} else {
// h2 fallback for confirmation page
focusByOrder([defaultFocusSelector, 'h2']);
// h2 fallback for review page
focusByOrder([defaultFocusSelector, 'h2'], root);
}
};
},
[
current,
formConfig.useCustomScrollAndFocus,
index,
page.chapterKey,
page.scrollAndFocusTarget,
],
[current, index],

Check warning on line 142 in src/platform/forms-system/src/js/components/FormNav.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/platform/forms-system/src/js/components/FormNav.jsx:142:5:React Hook useEffect has missing dependencies: 'formConfig.useCustomScrollAndFocus', 'formConfig.v3SegmentedProgressBar', 'page.chapterKey', and 'page.scrollAndFocusTarget'. Either include them or remove the dependency array.
);

const v3SegmentedProgressBar = formConfig?.v3SegmentedProgressBar;
Expand Down
12 changes: 9 additions & 3 deletions src/platform/forms-system/src/js/containers/FormPage.jsx
Expand Up @@ -5,7 +5,7 @@
import classNames from 'classnames';
import environment from '@department-of-veterans-affairs/platform-utilities/environment';
import { getDefaultFormState } from '@department-of-veterans-affairs/react-jsonschema-form/lib/utils';
import {

Check warning on line 8 in src/platform/forms-system/src/js/containers/FormPage.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/platform/forms-system/src/js/containers/FormPage.jsx:8:1:Importing from platform via platform/utilities is deprecated. Import from @department-of-veterans-affairs/platform-utilities instead or check babel.config.json for an alias to the import.
isReactComponent,
focusElement,
customScrollAndFocus,
Expand All @@ -17,7 +17,7 @@
import FormNavButtons from '../components/FormNavButtons';
import SchemaForm from '../components/SchemaForm';
import { setData, uploadFile } from '../actions';
import {

Check warning on line 20 in src/platform/forms-system/src/js/containers/FormPage.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/platform/forms-system/src/js/containers/FormPage.jsx:20:1:Dependency cycle detected.
getNextPagePath,
getPreviousPagePath,
checkValidPagePath,
Expand All @@ -26,11 +26,17 @@
import { stringifyUrlParams } from '../helpers';

function focusForm(route, index) {
const { useCustomScrollAndFocus, v3SegmentedProgressBar } = route.formConfig;
const { scrollAndFocusTarget } = route.pageConfig;
// Check main toggle to enable custom focus
if (route.formConfig?.useCustomScrollAndFocus) {
customScrollAndFocus(route.pageConfig?.scrollAndFocusTarget, index);
if (useCustomScrollAndFocus && scrollAndFocusTarget) {
customScrollAndFocus(scrollAndFocusTarget, index);
Mottie marked this conversation as resolved.
Show resolved Hide resolved
} else {
focusElement(defaultFocusSelector);
// Need to provide shadowRoot to focus on elements inside shadow-DOM
const root = v3SegmentedProgressBar
? document.querySelector('va-segmented-progress-bar').shadowRoot
: document.querySelector('#react-root');
focusElement(defaultFocusSelector, {}, root);
}
}

Expand Down
30 changes: 24 additions & 6 deletions src/platform/utilities/ui/focus.js
@@ -1,9 +1,20 @@
/* eslint-disable no-console */
import { isWebComponent, querySelectorWithShadowRoot } from './webComponents';
import {
isInShadowDOM,
isWebComponent,
querySelectorWithShadowRoot,
} from './webComponents';

// .nav-header > h2 contains "Step {index} of {total}: {page title}"
/** defaultFocusSelector
* Selector string for both pre-v3 and v3 va-segmented-progress-bar's H2
* Both H2s include "Step {index} of {total}: {page title}"
* NOTE: For v3 bar, pass its shadowRoot as root param to the focus methods. [See FormNav.jsx.]
*/
export const defaultFocusSelector =
'.nav-header > h2, va-segmented-progress-bar[uswds][heading-text][header-level="2"]';
// #nav-form-header is pre-v3-bar's H2
// .usa-step-indicator__heading is v3-bar's H2
// TODO: Remove pre-v3 selector, after DST defaults all components to v3 [~2024-02-17].
'#nav-form-header, .usa-step-indicator__heading';

/**
* Focus on element
Expand All @@ -14,7 +25,7 @@ export const defaultFocusSelector =
* @param {Element} root - root element for querySelector; would allow focusing
* on elements inside of shadow dom
*/
export function focusElement(selectorOrElement, options, root) {
export async function focusElement(selectorOrElement, options, root) {
function applyFocus(el) {
if (el) {
// Use getAttribute to grab the "tabindex" attribute (returns string), not
Expand All @@ -39,13 +50,20 @@ export function focusElement(selectorOrElement, options, root) {
}

el.focus(options);
if (isInShadowDOM(el)) {
// Safari doesn't dispatch focus events on shadow-DOM elements,
// so we manually dispath it to ensure screen readers are aware.
el.dispatchEvent(new FocusEvent('focus'));
}
}
}

if (isWebComponent(root) || isWebComponent(selectorOrElement, root)) {
querySelectorWithShadowRoot(selectorOrElement, root).then(
elWithShadowRoot => applyFocus(elWithShadowRoot), // async code
const elWithShadowRoot = await querySelectorWithShadowRoot(
selectorOrElement,
root,
);
applyFocus(elWithShadowRoot); // synchronous code
} else {
const el =
typeof selectorOrElement === 'string'
Expand Down
8 changes: 8 additions & 0 deletions src/platform/utilities/ui/webComponents.js
Expand Up @@ -37,6 +37,14 @@ export function isWebComponentReady(el, root) {
return !!(element?.shadowRoot && element?.classList.contains('hydrated'));
}

/**
* Checks if an element is inside shadow-DOM
* @param {HTMLElement} el
*/
export function isInShadowDOM(el) {
return el.getRootNode() instanceof ShadowRoot;
}

/**
* Web components initially render as 0 width / 0 height with no
* shadow dom content, so this waits until it contains a shadowRoot
Expand Down