From b60e810c5b512ea23efdb662440cfe6e8dc8ec0c Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Tue, 5 Apr 2022 08:47:47 -0700 Subject: [PATCH 01/10] Upgrade `tabbable` to latest to get `focusable` API + update import/fix types --- package.json | 4 ++-- .../context_menu/context_menu_panel.tsx | 2 +- src/components/datagrid/body/data_grid_cell.tsx | 2 +- .../header/data_grid_header_cell_wrapper.tsx | 2 +- .../body/header/header_is_interactive.ts | 2 +- src/components/datagrid/utils/focus.ts | 2 +- src/components/popover/input_popover.tsx | 4 ++-- src/components/popover/popover.tsx | 2 +- yarn.lock | 16 ++++++++-------- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index a09b3949107..4ff1cea3afe 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "remark-emoji": "^2.1.0", "remark-parse": "^8.0.3", "remark-rehype": "^8.0.0", - "tabbable": "^3.0.0", + "tabbable": "^5.2.1", "text-diff": "^1.0.1", "unified": "^9.2.0", "unist-util-visit": "^2.0.3", @@ -133,7 +133,7 @@ "@types/react-dom": "^17.0.11", "@types/react-is": "^17.0.3", "@types/react-router-dom": "^5.1.5", - "@types/tabbable": "^3.1.0", + "@types/tabbable": "^3.1.2", "@types/url-parse": "^1.4.8", "@types/uuid": "^8.3.0", "@typescript-eslint/eslint-plugin": "^5.10.2", diff --git a/src/components/context_menu/context_menu_panel.tsx b/src/components/context_menu/context_menu_panel.tsx index 3201acc8808..ce529e7b452 100644 --- a/src/components/context_menu/context_menu_panel.tsx +++ b/src/components/context_menu/context_menu_panel.tsx @@ -14,7 +14,7 @@ import React, { ReactNode, } from 'react'; import classNames from 'classnames'; -import tabbable from 'tabbable'; +import { tabbable } from 'tabbable'; import { CommonProps, NoArgCallback, keysOf } from '../common'; import { EuiIcon } from '../icon'; diff --git a/src/components/datagrid/body/data_grid_cell.tsx b/src/components/datagrid/body/data_grid_cell.tsx index 78c6ced31c3..16f659c16ac 100644 --- a/src/components/datagrid/body/data_grid_cell.tsx +++ b/src/components/datagrid/body/data_grid_cell.tsx @@ -18,7 +18,7 @@ import React, { MutableRefObject, } from 'react'; import { createPortal } from 'react-dom'; -import tabbable from 'tabbable'; +import { tabbable } from 'tabbable'; import { keys } from '../../../services'; import { EuiScreenReaderOnly } from '../../accessibility'; import { EuiFocusTrap } from '../../focus_trap'; diff --git a/src/components/datagrid/body/header/data_grid_header_cell_wrapper.tsx b/src/components/datagrid/body/header/data_grid_header_cell_wrapper.tsx index e4d1cdeeb5b..8b331754608 100644 --- a/src/components/datagrid/body/header/data_grid_header_cell_wrapper.tsx +++ b/src/components/datagrid/body/header/data_grid_header_cell_wrapper.tsx @@ -14,7 +14,7 @@ import React, { useRef, useState, } from 'react'; -import tabbable from 'tabbable'; +import { tabbable } from 'tabbable'; import { keys } from '../../../../services'; import { DataGridFocusContext } from '../../utils/focus'; import { EuiDataGridHeaderCellWrapperProps } from '../../data_grid_types'; diff --git a/src/components/datagrid/body/header/header_is_interactive.ts b/src/components/datagrid/body/header/header_is_interactive.ts index 7822f08d473..0550aad25f5 100644 --- a/src/components/datagrid/body/header/header_is_interactive.ts +++ b/src/components/datagrid/body/header/header_is_interactive.ts @@ -7,7 +7,7 @@ */ import { useCallback, useEffect, useState } from 'react'; -import tabbable from 'tabbable'; +import { tabbable } from 'tabbable'; export const useHeaderIsInteractive = (gridElement: HTMLElement | null) => { const [headerIsInteractive, setHeaderIsInteractive] = useState(false); diff --git a/src/components/datagrid/utils/focus.ts b/src/components/datagrid/utils/focus.ts index 94b15bd319a..40d992b857d 100644 --- a/src/components/datagrid/utils/focus.ts +++ b/src/components/datagrid/utils/focus.ts @@ -19,7 +19,7 @@ import { MutableRefObject, } from 'react'; import { GridOnItemsRenderedProps } from 'react-window'; -import tabbable from 'tabbable'; +import { tabbable } from 'tabbable'; import { keys } from '../../../services'; import { DataGridFocusContextShape, diff --git a/src/components/popover/input_popover.tsx b/src/components/popover/input_popover.tsx index 19d243e4080..f77e6c30681 100644 --- a/src/components/popover/input_popover.tsx +++ b/src/components/popover/input_popover.tsx @@ -14,7 +14,7 @@ import React, { useCallback, } from 'react'; import classnames from 'classnames'; -import tabbable from 'tabbable'; +import { tabbable, FocusableElement } from 'tabbable'; import { CommonProps } from '../common'; import { EuiFocusTrap } from '../focus_trap'; @@ -81,7 +81,7 @@ export const EuiInputPopover: FunctionComponent = ({ const onKeyDown = (event: React.KeyboardEvent) => { if (panelEl && event.key === cascadingMenuKeys.TAB) { - const tabbableItems = tabbable(panelEl).filter((el: HTMLElement) => { + const tabbableItems = tabbable(panelEl).filter((el: FocusableElement) => { return ( Array.from(el.attributes) .map((el) => el.name) diff --git a/src/components/popover/popover.tsx b/src/components/popover/popover.tsx index 06223be8b0a..ec87e585468 100644 --- a/src/components/popover/popover.tsx +++ b/src/components/popover/popover.tsx @@ -16,7 +16,7 @@ import React, { RefCallback, } from 'react'; import classNames from 'classnames'; -import tabbable from 'tabbable'; +import { tabbable } from 'tabbable'; import { CommonProps, NoArgCallback } from '../common'; import { FocusTarget, EuiFocusTrap, EuiFocusTrapProps } from '../focus_trap'; diff --git a/yarn.lock b/yarn.lock index 611a444c698..9919fcbb6ee 100755 --- a/yarn.lock +++ b/yarn.lock @@ -3465,10 +3465,10 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== -"@types/tabbable@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/tabbable/-/tabbable-3.1.0.tgz#540d4c2729872560badcc220e73c9412c1d2bffe" - integrity sha512-LL0q/bTlzseaXQ8j91eZ+Z8FQUzo0nwkng00B8365qULvFyiSOWylxV8m31Gmee3QuidkDqR72a9NRfR8s4qTw== +"@types/tabbable@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/tabbable/-/tabbable-3.1.2.tgz#5046f043fef50961d7727920b0076b37737e31ad" + integrity sha512-Yp+M5IjNZxYjsflBbSalyjUAIqiJyEISg++gLAstGrZlp9lzVi5KAsZvJqThT2qeoqGYnFqdZXorPEYtaVBAkg== "@types/tapable@*", "@types/tapable@^1.0.5": version "1.0.6" @@ -18843,10 +18843,10 @@ syntax-error@^1.1.1: dependencies: acorn-node "^1.2.0" -tabbable@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-3.1.2.tgz#f2d16cccd01f400e38635c7181adfe0ad965a4a2" - integrity sha512-wjB6puVXTYO0BSFtCmWQubA/KIn7Xvajw0x0l6eJUudMG/EAiJvIUnyNX6xO4NpGrJ16lbD0eUseB9WxW0vlpQ== +tabbable@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.2.1.tgz#e3fda7367ddbb172dcda9f871c0fdb36d1c4cd9c" + integrity sha512-40pEZ2mhjaZzK0BnI+QGNjJO8UYx9pP5v7BGe17SORTO0OEuuaAwQTkAp8whcZvqon44wKFOikD+Al11K3JICQ== table@^3.7.8: version "3.8.3" From 8eb8105e380c6ff46076ad52ab4dcf1ac8ffb326 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Tue, 5 Apr 2022 11:20:38 -0700 Subject: [PATCH 02/10] Update popovers to manually attempt to always focus back onto the toggle button on trap deactivation --- src/components/popover/popover.test.tsx | 68 +++++++++++++++++++++++++ src/components/popover/popover.tsx | 15 +++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/components/popover/popover.test.tsx b/src/components/popover/popover.test.tsx index df083c0bbd2..36cd4f80245 100644 --- a/src/components/popover/popover.test.tsx +++ b/src/components/popover/popover.test.tsx @@ -439,6 +439,74 @@ describe('EuiPopover', () => { jest.advanceTimersByTime(10); }); }); + + describe('refocusButtonOnClose', () => { + let rafSpy: jest.SpyInstance; + beforeAll(() => { + rafSpy = jest + .spyOn(window, 'requestAnimationFrame') + .mockImplementation((cb: Function) => cb()); + }); + afterAll(() => { + rafSpy.mockRestore(); + }); + + it('refocuses the toggle button on focus trap deactivation', () => { + const toggleButtonEl = React.createRef(); + const toggleButton =