From 866cd75e25d8cc9507464009c65fc24cddcac0f2 Mon Sep 17 00:00:00 2001 From: Andrea Franceschini Date: Mon, 21 Mar 2022 14:30:16 +0100 Subject: [PATCH] Restore reverted PR to branch --- src/app/state/modalFocusMiddleware.ts | 29 +++++++++++++++++++++++++++ src/app/state/store.tsx | 2 ++ 2 files changed, 31 insertions(+) create mode 100644 src/app/state/modalFocusMiddleware.ts diff --git a/src/app/state/modalFocusMiddleware.ts b/src/app/state/modalFocusMiddleware.ts new file mode 100644 index 0000000000..0c73f28788 --- /dev/null +++ b/src/app/state/modalFocusMiddleware.ts @@ -0,0 +1,29 @@ +import {Dispatch, Middleware, MiddlewareAPI} from "redux"; +import {ACTION_TYPE} from "../services/constants"; + +let lastFocusedStack: Element[] = []; + +// When a modal is closed, this tries to focus the last element that was focused before it opened. If no element exists, +// we try to focus the page title. This is for accessibility, mostly to help when navigating the site with a screen-reader. +export const modalFocusMiddleware: Middleware = (api: MiddlewareAPI) => (next: Dispatch) => action => { + const state = api.getState(); + + switch (action.type) { + case ACTION_TYPE.ACTIVE_MODAL_OPEN: + // Before modal open, record the element that is currently focused + if (document.activeElement) lastFocusedStack.push(document.activeElement); + break; + case ACTION_TYPE.ACTIVE_MODAL_CLOSE: { + const lastFocusedElement = lastFocusedStack.pop() as HTMLElement; + // Before modal close, try to focus the last focused element, otherwise focus the main heading (if it exists) + if (lastFocusedElement && lastFocusedElement.isConnected && typeof lastFocusedElement.focus === "function") { + lastFocusedElement.focus(); + } else { + lastFocusedStack = []; + document.getElementById(state?.mainContentId || "main")?.focus(); + } + } + } + + return next(action); +}; diff --git a/src/app/state/store.tsx b/src/app/state/store.tsx index 1874c0dc53..7ae6d30f87 100644 --- a/src/app/state/store.tsx +++ b/src/app/state/store.tsx @@ -4,6 +4,7 @@ import * as reduxLogger from "redux-logger"; import {AppState, rootReducer} from "./reducers"; import {userConsistencyCheckerMiddleware} from "./userConsistencyChecker"; import {notificationCheckerMiddleware} from "../services/notificationManager"; +import {modalFocusMiddleware} from "./modalFocusMiddleware"; // @ts-ignore const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; @@ -11,6 +12,7 @@ const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; export const middleware: Middleware[] = [ userConsistencyCheckerMiddleware, notificationCheckerMiddleware, + modalFocusMiddleware, thunk, ];