Skip to content

Commit

Permalink
reset active element/focus order state on ui close (#158)
Browse files Browse the repository at this point in the history
* reset active element/focus order state on ui close
  • Loading branch information
csmccarthy authored Feb 26, 2024
1 parent 03918d9 commit 6c7bc40
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ export function App({
initialViewState,
dismissedViewState,
eventTarget,
savedActiveElement:
document.activeElement instanceof HTMLElement &&
document.activeElement !== document.body
? document.activeElement
: null,
});

// Language setup
Expand Down
21 changes: 20 additions & 1 deletion src/hooks/useViewState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,16 @@ export function useViewState({
initialViewState,
dismissedViewState,
eventTarget,
savedActiveElement,
}: {
/** Which state this consent manager should go to when opened */
initialViewState: InitialViewState;
/** Which state this consent manager should go to when closed */
dismissedViewState: DismissedViewState;
/** The event target on the `transcend` API, where we will dispatch view state change events */
eventTarget: EventTarget;
/** Element previously focused before our ui modal was opened */
savedActiveElement: HTMLElement | null;
}): {
/** The current view state */
viewState: ViewState;
Expand Down Expand Up @@ -118,14 +121,30 @@ export function useViewState({
break;

// Request to close the modal, to the closed view state (which depends on whether customer wants to display the collapse view or hide it)
case 'close':
case 'close': {
setState({
current: dismissedViewState,
previous: state.current,
auth,
firstSelectedViewState: null,
});
/* If the user previously had something focused before the modal popped up, then focus it.
* Otherwise, we have to do this really janky "reset" behavior with the temporary element
* because Chrome specifically has some weird internal state around focus order that is
* very difficult to interact with. We create an element with maximum focus priority and
* focus it so that when we delete it the user will be at the start of the focus order
* just like if they had freshly loaded the page. */
if (savedActiveElement !== null) {
savedActiveElement.focus();
} else {
const tempInteractiveEl = document.createElement('span');
tempInteractiveEl.setAttribute('tabindex', '1');
document.body.prepend(tempInteractiveEl);
tempInteractiveEl.focus();
tempInteractiveEl.remove();
}
break;
}

// Request to go to a specific view state, e.g. the language options page
default:
Expand Down

0 comments on commit 6c7bc40

Please sign in to comment.