You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/07/13 19:23:44 UTC
(superset) branch chore/enable-click-events-have-key-events created (now 926013ab412)
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a change to branch chore/enable-click-events-have-key-events
in repository https://gitbox.apache.org/repos/asf/superset.git
at 926013ab412 chore(a11y): enable jsx-a11y/click-events-have-key-events as error
This branch includes the following new commits:
new 926013ab412 chore(a11y): enable jsx-a11y/click-events-have-key-events as error
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(superset) 01/01: chore(a11y): enable jsx-a11y/click-events-have-key-events as error
Posted by ru...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch chore/enable-click-events-have-key-events
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 926013ab4121aea0f26954360093fd843c3dda6a
Author: Evan Rusackas <ev...@rusackas.com>
AuthorDate: Mon Jul 13 12:23:23 2026 -0700
chore(a11y): enable jsx-a11y/click-events-have-key-events as error
Adds a small, tested `handleKeyboardActivation` helper to @superset-ui/core
that maps Enter/Space to a click handler, then applies it across the 80
violations (41 files) so every clickable non-interactive element is operable
from the keyboard. Turns the rule on as "error".
- New util `handleKeyboardActivation(callback)` returns an onKeyDown handler
that fires the callback on Enter/Space (with preventDefault). Unit-tested,
plus an ActionButton test covering the click and keyboard paths.
- Elements that already had role="button"/tabIndex get a mirrored onKeyDown.
Bare clickable divs (e.g. AgGrid CustomHeader menu items) also get
role="button" + tabIndex so the handler is actually reachable.
- Pure event-boundary wrappers (onClick only stops propagation) get a
matching onKeyDown that stops propagation, rather than a no-op activation.
- ModalTrigger's div trigger gains tabIndex so the new handler is focusable.
This completes the keyboard-operability follow-up promised while enabling
interactive-supports-focus in #42006.
Co-Authored-By: Claude Opus 4.8 <no...@anthropic.com>
---
superset-frontend/oxlint.json | 2 +-
.../components/ActionButton/ActionButton.test.tsx | 14 +++++-
.../src/components/ActionButton/index.tsx | 2 +
.../src/components/ModalTrigger/index.tsx | 9 +++-
.../src/components/PopoverSection/index.tsx | 2 +
.../src/utils/handleKeyboardActivation.test.ts | 52 ++++++++++++++++++++++
.../src/utils/handleKeyboardActivation.ts | 43 ++++++++++++++++++
.../packages/superset-ui-core/src/utils/index.ts | 1 +
.../src/AgGridTable/components/CustomHeader.tsx | 33 ++++++++++++--
.../src/react-pivottable/TableRenderers.tsx | 6 ++-
superset-frontend/spec/helpers/shim.tsx | 2 +-
.../TableExploreTree/TreeNodeRenderer.tsx | 11 +++++
superset-frontend/src/components/Chart/Chart.tsx | 8 +++-
.../src/components/Chart/DrillBy/DrillByModal.tsx | 2 +
.../components/Chart/DrillBy/DrillBySubmenu.tsx | 1 +
.../DatasourceEditor/DatasourceEditor.tsx | 2 +
.../src/components/ErrorMessage/ErrorAlert.tsx | 9 +++-
.../src/components/ListView/ListView.tsx | 9 ++++
.../components/URLShortLinkButton/index.tsx | 1 +
.../components/gridComponents/Tab/Tab.tsx | 4 ++
.../FilterBar/FilterConfigurationLink/index.tsx | 2 +
.../FiltersConfigModal/FilterTitleContainer.tsx | 2 +
.../FiltersConfigModal/ItemTitleContainer.tsx | 2 +
.../src/explore/components/ControlHeader.tsx | 2 +
.../components/DataTablesPane/DataTablesPane.tsx | 12 ++++-
.../explore/components/DatasourcePanel/index.tsx | 10 ++++-
.../explore/components/ExploreChartPanel/index.tsx | 11 ++++-
.../components/ExploreViewContainer/index.tsx | 3 ++
.../ColumnConfigControl/ColumnConfigControl.tsx | 4 ++
.../DndColumnSelectControl/ColumnSelectPopover.tsx | 10 +++++
.../DndColumnSelectPopoverTitle.tsx | 2 +
.../controls/LayerConfigsControl/LayerTreeItem.tsx | 3 ++
.../MetricControl/AdhocMetricEditPopover/index.tsx | 11 ++++-
.../MetricControl/AdhocMetricEditPopoverTitle.tsx | 2 +
.../components/controls/VizTypeControl/index.tsx | 12 ++++-
.../alerts/components/NotificationMethod.tsx | 6 +++
.../src/features/charts/ChartCard.tsx | 11 ++++-
.../src/features/dashboards/DashboardCard.tsx | 13 +++++-
.../AddDataset/DatasetPanel/DatasetPanel.tsx | 6 +++
superset-frontend/src/pages/ChartList/index.tsx | 4 ++
.../src/pages/DashboardList/index.tsx | 4 ++
superset-frontend/src/pages/DatabaseList/index.tsx | 11 +++++
superset-frontend/src/pages/DatasetList/index.tsx | 15 +++++++
.../src/pages/RowLevelSecurityList/index.tsx | 4 +-
superset-frontend/src/pages/Tags/index.tsx | 8 +++-
superset-frontend/src/pages/TaskList/index.tsx | 4 ++
46 files changed, 366 insertions(+), 21 deletions(-)
diff --git a/superset-frontend/oxlint.json b/superset-frontend/oxlint.json
index 1516d96689c..876a0d9cab8 100644
--- a/superset-frontend/oxlint.json
+++ b/superset-frontend/oxlint.json
@@ -201,7 +201,7 @@
"jsx-a11y/aria-role": ["error", { "ignoreNonDOM": false }],
"jsx-a11y/aria-unsupported-elements": "error",
"jsx-a11y/autocomplete-valid": "error",
- "jsx-a11y/click-events-have-key-events": "off",
+ "jsx-a11y/click-events-have-key-events": "error",
"jsx-a11y/control-has-associated-label": "error",
"jsx-a11y/heading-has-content": "error",
"jsx-a11y/html-has-lang": "error",
diff --git a/superset-frontend/packages/superset-ui-core/src/components/ActionButton/ActionButton.test.tsx b/superset-frontend/packages/superset-ui-core/src/components/ActionButton/ActionButton.test.tsx
index 123d5349505..06c6584d592 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/ActionButton/ActionButton.test.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/ActionButton/ActionButton.test.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { render, screen, userEvent } from '@superset-ui/core/spec';
+import { render, screen, userEvent, fireEvent } from '@superset-ui/core/spec';
import { Icons } from '@superset-ui/core/components/Icons';
import { ActionButton } from '.';
@@ -45,6 +45,18 @@ test('calls onClick when clicked', async () => {
expect(onClick).toHaveBeenCalledTimes(1);
});
+test('calls onClick when activated with the keyboard', () => {
+ const onClick = jest.fn();
+ render(<ActionButton {...defaultProps} onClick={onClick} />);
+
+ const button = screen.getByRole('button');
+ fireEvent.keyDown(button, { key: 'Enter' });
+ expect(onClick).toHaveBeenCalledTimes(1);
+
+ fireEvent.keyDown(button, { key: ' ' });
+ expect(onClick).toHaveBeenCalledTimes(2);
+});
+
test('renders with tooltip when tooltip prop is provided', async () => {
const tooltipText = 'This is a tooltip';
render(<ActionButton {...defaultProps} tooltip={tooltipText} />);
diff --git a/superset-frontend/packages/superset-ui-core/src/components/ActionButton/index.tsx b/superset-frontend/packages/superset-ui-core/src/components/ActionButton/index.tsx
index 336b8ca3d48..3e56efbc265 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/ActionButton/index.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/ActionButton/index.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
+import { handleKeyboardActivation } from '../../utils';
import type { ReactElement, ReactNode } from 'react';
import { Tooltip, type TooltipPlacement } from '@superset-ui/core/components';
import { css, useTheme } from '@apache-superset/core/theme';
@@ -55,6 +56,7 @@ export const ActionButton = ({
className="action-button"
data-test={label}
onClick={onClick}
+ onKeyDown={onClick ? handleKeyboardActivation(onClick) : undefined}
>
{icon}
</span>
diff --git a/superset-frontend/packages/superset-ui-core/src/components/ModalTrigger/index.tsx b/superset-frontend/packages/superset-ui-core/src/components/ModalTrigger/index.tsx
index 4f477437027..e3762d1e5ad 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/ModalTrigger/index.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/ModalTrigger/index.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '../../utils';
import {
forwardRef,
ForwardedRef,
@@ -126,7 +127,13 @@ export const ModalTrigger = forwardRef(
</Button>
)}
{!isButton && (
- <div data-test="span-modal-trigger" onClick={open} role="button">
+ <div
+ data-test="span-modal-trigger"
+ onClick={open}
+ onKeyDown={handleKeyboardActivation(open)}
+ role="button"
+ tabIndex={0}
+ >
{triggerNode}
</div>
)}
diff --git a/superset-frontend/packages/superset-ui-core/src/components/PopoverSection/index.tsx b/superset-frontend/packages/superset-ui-core/src/components/PopoverSection/index.tsx
index 39009eba76a..902f7378b67 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/PopoverSection/index.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/PopoverSection/index.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '../../utils';
import { MouseEventHandler, ReactNode } from 'react';
import { css, useTheme } from '@apache-superset/core/theme';
import { Icons } from '@superset-ui/core/components/Icons';
@@ -48,6 +49,7 @@ export default function PopoverSection({
role="button"
tabIndex={0}
onClick={onSelect}
+ onKeyDown={onSelect ? handleKeyboardActivation(onSelect) : undefined}
css={css`
display: flex;
align-items: center;
diff --git a/superset-frontend/packages/superset-ui-core/src/utils/handleKeyboardActivation.test.ts b/superset-frontend/packages/superset-ui-core/src/utils/handleKeyboardActivation.test.ts
new file mode 100644
index 00000000000..9fb42d4b252
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/src/utils/handleKeyboardActivation.test.ts
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { KeyboardEvent } from 'react';
+import { handleKeyboardActivation } from './handleKeyboardActivation';
+
+const makeEvent = (key: string) => {
+ const preventDefault = jest.fn();
+ return {
+ event: { key, preventDefault } as unknown as KeyboardEvent,
+ preventDefault,
+ };
+};
+
+test('invokes the callback and prevents default on Enter', () => {
+ const callback = jest.fn();
+ const { event, preventDefault } = makeEvent('Enter');
+ handleKeyboardActivation(callback)(event);
+ expect(callback).toHaveBeenCalledWith(event);
+ expect(preventDefault).toHaveBeenCalled();
+});
+
+test('invokes the callback and prevents default on Space', () => {
+ const callback = jest.fn();
+ const { event, preventDefault } = makeEvent(' ');
+ handleKeyboardActivation(callback)(event);
+ expect(callback).toHaveBeenCalledWith(event);
+ expect(preventDefault).toHaveBeenCalled();
+});
+
+test('ignores other keys', () => {
+ const callback = jest.fn();
+ const { event, preventDefault } = makeEvent('a');
+ handleKeyboardActivation(callback)(event);
+ expect(callback).not.toHaveBeenCalled();
+ expect(preventDefault).not.toHaveBeenCalled();
+});
diff --git a/superset-frontend/packages/superset-ui-core/src/utils/handleKeyboardActivation.ts b/superset-frontend/packages/superset-ui-core/src/utils/handleKeyboardActivation.ts
new file mode 100644
index 00000000000..b5470bfeecb
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/src/utils/handleKeyboardActivation.ts
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { KeyboardEvent } from 'react';
+
+/**
+ * Builds an `onKeyDown` handler that invokes `callback` when the user presses
+ * Enter or Space, mirroring a click for keyboard users. Pair it with an
+ * element's `onClick` so `role="button"` (or similar) controls are operable
+ * from the keyboard, satisfying `jsx-a11y/click-events-have-key-events`.
+ *
+ * <div role="button" onClick={handleClick}
+ * onKeyDown={handleKeyboardActivation(handleClick)} />
+ */
+export function handleKeyboardActivation(
+ callback: (event: KeyboardEvent) => void,
+) {
+ return (event: KeyboardEvent) => {
+ if (event.key === 'Enter' || event.key === ' ') {
+ // Prevent the page from scrolling on Space and stop any duplicate
+ // default activation on Enter.
+ event.preventDefault();
+ callback(event);
+ }
+ };
+}
+
+export default handleKeyboardActivation;
diff --git a/superset-frontend/packages/superset-ui-core/src/utils/index.ts b/superset-frontend/packages/superset-ui-core/src/utils/index.ts
index f947bdfd57e..797c7ab561f 100644
--- a/superset-frontend/packages/superset-ui-core/src/utils/index.ts
+++ b/superset-frontend/packages/superset-ui-core/src/utils/index.ts
@@ -27,6 +27,7 @@ export { default as makeSingleton } from './makeSingleton';
export { default as promiseTimeout } from './promiseTimeout';
export { default as removeDuplicates } from './removeDuplicates';
export { default as withLabel } from './withLabel';
+export { handleKeyboardActivation } from './handleKeyboardActivation';
export { lruCache } from './lruCache';
export { getSelectedText } from './getSelectedText';
export * from './featureFlags';
diff --git a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/AgGridTable/components/CustomHeader.tsx b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/AgGridTable/components/CustomHeader.tsx
index cb6e58e5491..024129ddb1d 100644
--- a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/AgGridTable/components/CustomHeader.tsx
+++ b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/AgGridTable/components/CustomHeader.tsx
@@ -19,6 +19,7 @@
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { useRef, useState, useEffect } from 'react';
import { t } from '@apache-superset/core/translation';
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
@@ -201,17 +202,35 @@ const CustomHeader: React.FC<CustomHeaderParams> = ({
const menuContent = (
<MenuContainer>
{shouldShowAsc && (
- <div onClick={() => applySort('asc')} className="menu-item">
+ <div
+ role="button"
+ tabIndex={0}
+ onClick={() => applySort('asc')}
+ onKeyDown={handleKeyboardActivation(() => applySort('asc'))}
+ className="menu-item"
+ >
<ArrowUpOutlined /> {t('Sort Ascending')}
</div>
)}
{shouldShowDesc && (
- <div onClick={() => applySort('desc')} className="menu-item">
+ <div
+ role="button"
+ tabIndex={0}
+ onClick={() => applySort('desc')}
+ onKeyDown={handleKeyboardActivation(() => applySort('desc'))}
+ className="menu-item"
+ >
<ArrowDownOutlined /> {t('Sort Descending')}
</div>
)}
{currentSort && currentSort?.colId === colId && (
- <div onClick={clearSort} className="menu-item">
+ <div
+ role="button"
+ tabIndex={0}
+ onClick={clearSort}
+ onKeyDown={handleKeyboardActivation(clearSort)}
+ className="menu-item"
+ >
<span style={{ fontSize: 16 }}>↻</span> {t('Clear Sort')}
</div>
)}
@@ -247,7 +266,13 @@ const CustomHeader: React.FC<CustomHeaderParams> = ({
isOpen={isMenuVisible}
onClose={() => setMenuVisible(false)}
>
- <div className="three-dots-menu" onClick={handleMenuClick}>
+ <div
+ role="button"
+ tabIndex={0}
+ className="three-dots-menu"
+ onClick={handleMenuClick}
+ onKeyDown={handleKeyboardActivation(handleMenuClick)}
+ >
<KebabMenu />
</div>
</CustomPopover>
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.tsx
index d3e58763827..ccef0bc509a 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.tsx
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.tsx
@@ -26,7 +26,7 @@ import {
useMemo,
useEffect,
} from 'react';
-import { safeHtmlSpan } from '@superset-ui/core';
+import { safeHtmlSpan, handleKeyboardActivation } from '@superset-ui/core';
import { t } from '@apache-superset/core/translation';
import { supersetTheme } from '@apache-superset/core/theme';
import PropTypes from 'prop-types';
@@ -172,6 +172,9 @@ function displayHeaderCell(
tabIndex={0}
className="toggle"
onClick={onArrowClick || undefined}
+ onKeyDown={
+ onArrowClick ? handleKeyboardActivation(onArrowClick) : undefined
+ }
>
{ArrowIcon}
</span>
@@ -1046,6 +1049,7 @@ export function TableRenderer(props: TableRendererProps) {
onClick={e => {
e.stopPropagation();
}}
+ onKeyDown={e => e.stopPropagation()}
aria-label={
activeSortColumn === i
? `Sorted by ${columnName} ${sortingOrder[i] === 'asc' ? 'ascending' : 'descending'}`
diff --git a/superset-frontend/spec/helpers/shim.tsx b/superset-frontend/spec/helpers/shim.tsx
index 0bff723e4ca..b1c243357b9 100644
--- a/superset-frontend/spec/helpers/shim.tsx
+++ b/superset-frontend/spec/helpers/shim.tsx
@@ -119,7 +119,7 @@ jest.mock('@superset-ui/core/components/Icons/AsyncIcon', () => ({
const label =
ariaLabel || fileName?.replace(/_/g, '-').toLowerCase() || '';
return (
- // eslint-disable-next-line jsx-a11y/no-static-element-interactions
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
<span
ref={ref}
role={role || (onClick ? 'button' : 'img')}
diff --git a/superset-frontend/src/SqlLab/components/TableExploreTree/TreeNodeRenderer.tsx b/superset-frontend/src/SqlLab/components/TableExploreTree/TreeNodeRenderer.tsx
index dc3e83eaefe..af423f12b5f 100644
--- a/superset-frontend/src/SqlLab/components/TableExploreTree/TreeNodeRenderer.tsx
+++ b/superset-frontend/src/SqlLab/components/TableExploreTree/TreeNodeRenderer.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { css, styled, useTheme } from '@apache-superset/core/theme';
import { t } from '@apache-superset/core/translation';
import type { NodeRendererProps } from 'react-arborist';
@@ -186,6 +187,7 @@ const TreeNodeRenderer: React.FC<TreeNodeRendererProps> = ({
<span
className="col-copy-action"
onClick={e => e.stopPropagation()}
+ onKeyDown={e => e.stopPropagation()}
>
<ActionButton
label={`copy-col-${data.name}`}
@@ -215,6 +217,13 @@ const TreeNodeRenderer: React.FC<TreeNodeRendererProps> = ({
node.toggle();
}
}}
+ onKeyDown={handleKeyboardActivation(() => {
+ if (node.isLeaf) {
+ node.select();
+ } else {
+ node.toggle();
+ }
+ })}
>
<span
className="tree-node-icon"
@@ -239,6 +248,7 @@ const TreeNodeRenderer: React.FC<TreeNodeRendererProps> = ({
<div
className="side-action-container"
onClick={e => e.stopPropagation()}
+ onKeyDown={e => e.stopPropagation()}
>
{pinnedSchemas.has(schema) && (
<div className="action-static">
@@ -306,6 +316,7 @@ const TreeNodeRenderer: React.FC<TreeNodeRendererProps> = ({
<div
className="side-action-container"
onClick={e => e.stopPropagation()}
+ onKeyDown={e => e.stopPropagation()}
>
{isPinned && (
<div className="action-static">
diff --git a/superset-frontend/src/components/Chart/Chart.tsx b/superset-frontend/src/components/Chart/Chart.tsx
index 1519837c747..4cc5b08502b 100644
--- a/superset-frontend/src/components/Chart/Chart.tsx
+++ b/superset-frontend/src/components/Chart/Chart.tsx
@@ -30,6 +30,7 @@ import {
type FilterState,
type JsonObject,
type AgGridChartState,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { styled } from '@apache-superset/core/theme';
import type { ChartState, Datasource, ChartStatus } from 'src/explore/types';
@@ -465,7 +466,12 @@ function Chart({
{t(
'Click on "Create chart" button in the control panel on the left to preview a visualization or',
)}{' '}
- <span role="button" tabIndex={0} onClick={onQuery}>
+ <span
+ role="button"
+ tabIndex={0}
+ onClick={onQuery}
+ onKeyDown={handleKeyboardActivation(onQuery)}
+ >
{t('click here')}
</span>
.
diff --git a/superset-frontend/src/components/Chart/DrillBy/DrillByModal.tsx b/superset-frontend/src/components/Chart/DrillBy/DrillByModal.tsx
index 57149ee55e0..2e5cc764237 100644
--- a/superset-frontend/src/components/Chart/DrillBy/DrillByModal.tsx
+++ b/superset-frontend/src/components/Chart/DrillBy/DrillByModal.tsx
@@ -28,6 +28,7 @@ import {
isDefined,
ContextMenuFilters,
AdhocFilter,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { Alert } from '@apache-superset/core/components';
import { css, useTheme } from '@apache-superset/core/theme';
@@ -565,6 +566,7 @@ export default function DrillByModal({
role="button"
tabIndex={0}
onClick={route.onClick}
+ onKeyDown={handleKeyboardActivation(route.onClick)}
css={css`
cursor: pointer;
`}
diff --git a/superset-frontend/src/components/Chart/DrillBy/DrillBySubmenu.tsx b/superset-frontend/src/components/Chart/DrillBy/DrillBySubmenu.tsx
index d8053a8d600..d331cf528e0 100644
--- a/superset-frontend/src/components/Chart/DrillBy/DrillBySubmenu.tsx
+++ b/superset-frontend/src/components/Chart/DrillBy/DrillBySubmenu.tsx
@@ -217,6 +217,7 @@ export const DrillBySubmenu = ({
role="menu"
tabIndex={0}
data-test="drill-by-submenu"
+ onKeyDown={e => e.stopPropagation()}
css={css`
width: 220px;
max-width: 220px;
diff --git a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx
index d5a9034a023..ddb0f6d45dd 100644
--- a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx
+++ b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx
@@ -36,6 +36,7 @@ import {
SupersetClient,
getClientErrorObject,
getExtensionsRegistry,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { GenericDataType } from '@apache-superset/core/common';
import { t } from '@apache-superset/core/translation';
@@ -1760,6 +1761,7 @@ function DatasourceEditor({
role="button"
tabIndex={0}
onClick={onChangeEditMode}
+ onKeyDown={handleKeyboardActivation(onChangeEditMode)}
>
{isEditMode ? (
<Icons.UnlockOutlined
diff --git a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
index 4eae54c6ad2..2965137df1d 100644
--- a/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
+++ b/superset-frontend/src/components/ErrorMessage/ErrorAlert.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { useState } from 'react';
import { t } from '@apache-superset/core/translation';
import { Alert } from '@apache-superset/core/components';
@@ -103,6 +104,7 @@ export const ErrorAlert: React.FC<ErrorAlertProps> = ({
role="button"
tabIndex={0}
onClick={toggleDescription}
+ onKeyDown={handleKeyboardActivation(toggleDescription)}
style={{ textDecoration: 'underline', cursor: 'pointer' }}
>
{isDescriptionVisible ? t('See less') : t('See more')}
@@ -127,7 +129,12 @@ export const ErrorAlert: React.FC<ErrorAlertProps> = ({
return (
<>
<Tooltip title={`${errorType}: ${message}`}>
- <span role="button" onClick={() => setShowModal(true)} tabIndex={0}>
+ <span
+ role="button"
+ onClick={() => setShowModal(true)}
+ onKeyDown={handleKeyboardActivation(() => setShowModal(true))}
+ tabIndex={0}
+ >
{renderTrigger()}
</span>
</Tooltip>
diff --git a/superset-frontend/src/components/ListView/ListView.tsx b/superset-frontend/src/components/ListView/ListView.tsx
index a88b2ebb736..f8129b50009 100644
--- a/superset-frontend/src/components/ListView/ListView.tsx
+++ b/superset-frontend/src/components/ListView/ListView.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { t } from '@apache-superset/core/translation';
import { Alert } from '@apache-superset/core/components';
import { styled } from '@apache-superset/core/theme';
@@ -255,6 +256,7 @@ const ViewModeToggle = ({
e.currentTarget.blur();
setMode('card');
}}
+ onKeyDown={handleKeyboardActivation(() => setMode('card'))}
className={cx('toggle-button', { active: mode === 'card' })}
>
<Icons.AppstoreOutlined iconSize="xl" />
@@ -269,6 +271,7 @@ const ViewModeToggle = ({
e.currentTarget.blur();
setMode('table');
}}
+ onKeyDown={handleKeyboardActivation(() => setMode('table'))}
className={cx('toggle-button', { active: mode === 'table' })}
>
<Icons.UnorderedListOutlined iconSize="xl" />
@@ -509,6 +512,9 @@ export function ListView<T extends object = any>({
tabIndex={0}
className="deselect-all"
onClick={() => toggleAllRowsSelected(false)}
+ onKeyDown={handleKeyboardActivation(() =>
+ toggleAllRowsSelected(false),
+ )}
>
{t('Deselect all')}
</span>
@@ -544,6 +550,9 @@ export function ListView<T extends object = any>({
tabIndex={0}
className="tag-btn"
onClick={() => setShowBulkTagModal(true)}
+ onKeyDown={handleKeyboardActivation(() =>
+ setShowBulkTagModal(true),
+ )}
>
{t('Add Tag')}
</span>
diff --git a/superset-frontend/src/dashboard/components/URLShortLinkButton/index.tsx b/superset-frontend/src/dashboard/components/URLShortLinkButton/index.tsx
index 136c44425b9..6574bb69386 100644
--- a/superset-frontend/src/dashboard/components/URLShortLinkButton/index.tsx
+++ b/superset-frontend/src/dashboard/components/URLShortLinkButton/index.tsx
@@ -106,6 +106,7 @@ export default function URLShortLinkButton({
onClick={e => {
e.stopPropagation();
}}
+ onKeyDown={e => e.stopPropagation()}
>
<CopyToClipboard
text={shortUrl}
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.tsx b/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.tsx
index 9bdbf9221c2..6f6621c7282 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import {
Fragment,
useCallback,
@@ -351,6 +352,9 @@ const Tab = (props: TabProps): ReactElement => {
role="button"
tabIndex={0}
onClick={() => dispatch(setEditMode(true))}
+ onKeyDown={handleKeyboardActivation(() =>
+ dispatch(setEditMode(true)),
+ )}
>
{t('edit mode')}
</span>
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
index 367072fbab9..76573405681 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { ReactNode, FC, memo } from 'react';
import { getFilterBarTestId } from '../utils';
@@ -32,6 +33,7 @@ export const FilterConfigurationLink: FC<FCBProps> = ({
<div
{...getFilterBarTestId('create-filter')}
onClick={onClick}
+ onKeyDown={handleKeyboardActivation(onClick)}
role="button"
tabIndex={0}
>
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterTitleContainer.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterTitleContainer.tsx
index e5420ad1b63..c3026f18efe 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterTitleContainer.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterTitleContainer.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { forwardRef, useCallback, useState } from 'react';
import { t } from '@apache-superset/core/translation';
@@ -199,6 +200,7 @@ const FilterTitleContainer = forwardRef<HTMLDivElement, Props>(
e.preventDefault();
restoreFilter(id);
}}
+ onKeyDown={handleKeyboardActivation(() => restoreFilter(id))}
>
{t('Undo?')}
</span>
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/ItemTitleContainer.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/ItemTitleContainer.tsx
index 388421b5ac1..7904835fc4c 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/ItemTitleContainer.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/ItemTitleContainer.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { forwardRef, useState } from 'react';
import { t } from '@apache-superset/core/translation';
@@ -167,6 +168,7 @@ const ItemTitleContainer = forwardRef<HTMLDivElement, Props>(
e.preventDefault();
restoreItem(id);
}}
+ onKeyDown={handleKeyboardActivation(() => restoreItem(id))}
>
{t('Undo?')}
</span>
diff --git a/superset-frontend/src/explore/components/ControlHeader.tsx b/superset-frontend/src/explore/components/ControlHeader.tsx
index a2bbf047c31..fd62cbaf739 100644
--- a/superset-frontend/src/explore/components/ControlHeader.tsx
+++ b/superset-frontend/src/explore/components/ControlHeader.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { FC, ReactNode } from 'react';
import { t } from '@apache-superset/core/translation';
import { css, useTheme, SupersetTheme } from '@apache-superset/core/theme';
@@ -139,6 +140,7 @@ const ControlHeader: FC<ControlHeaderProps> = ({
role="button"
tabIndex={0}
onClick={onClick}
+ onKeyDown={onClick ? handleKeyboardActivation(onClick) : undefined}
style={{ cursor: onClick ? 'pointer' : '' }}
>
{label}
diff --git a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.tsx b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.tsx
index 55994e58d82..6c40bba70d9 100644
--- a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.tsx
+++ b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.tsx
@@ -18,7 +18,11 @@
*/
import { useCallback, useEffect, useMemo, useState, MouseEvent } from 'react';
import { t } from '@apache-superset/core/translation';
-import { isFeatureEnabled, FeatureFlag } from '@superset-ui/core';
+import {
+ isFeatureEnabled,
+ FeatureFlag,
+ handleKeyboardActivation,
+} from '@superset-ui/core';
import { styled } from '@apache-superset/core/theme';
import { Icons } from '@superset-ui/core/components/Icons';
import Tabs from '@superset-ui/core/components/Tabs';
@@ -187,6 +191,9 @@ export const DataTablesPane = ({
role="button"
tabIndex={0}
onClick={() => handleCollapseChange(false)}
+ onKeyDown={handleKeyboardActivation(() =>
+ handleCollapseChange(false),
+ )}
>
{caretIcon}
</span>
@@ -195,6 +202,9 @@ export const DataTablesPane = ({
role="button"
tabIndex={0}
onClick={() => handleCollapseChange(true)}
+ onKeyDown={handleKeyboardActivation(() =>
+ handleCollapseChange(true),
+ )}
>
{caretIcon}
</span>
diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx
index 7e9d5244130..c72efc8c5fd 100644
--- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx
+++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx
@@ -18,7 +18,12 @@
*/
import { useContext, useDeferredValue, useMemo, useState } from 'react';
import { t } from '@apache-superset/core/translation';
-import { DatasourceType, Metric, QueryFormData } from '@superset-ui/core';
+import {
+ DatasourceType,
+ Metric,
+ QueryFormData,
+ handleKeyboardActivation,
+} from '@superset-ui/core';
import { Alert } from '@apache-superset/core/components';
import { css, styled, useTheme } from '@apache-superset/core/theme';
@@ -290,6 +295,9 @@ export default function DataSourcePanel({
role="button"
tabIndex={0}
onClick={() => setShowSaveDatasetModal(true)}
+ onKeyDown={handleKeyboardActivation(() =>
+ setShowSaveDatasetModal(true),
+ )}
className="add-dataset-alert-description"
>
{t('Create a dataset')}
diff --git a/superset-frontend/src/explore/components/ExploreChartPanel/index.tsx b/superset-frontend/src/explore/components/ExploreChartPanel/index.tsx
index 3145bda73a2..e90f9e708ea 100644
--- a/superset-frontend/src/explore/components/ExploreChartPanel/index.tsx
+++ b/superset-frontend/src/explore/components/ExploreChartPanel/index.tsx
@@ -30,6 +30,7 @@ import {
QueryFormData,
JsonObject,
getExtensionsRegistry,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { Alert } from '@apache-superset/core/components';
import { css, styled, useTheme } from '@apache-superset/core/theme';
@@ -396,6 +397,9 @@ const ExploreChartPanel = ({
role="button"
tabIndex={0}
onClick={() => setShowDatasetModal(true)}
+ onKeyDown={handleKeyboardActivation(() =>
+ setShowDatasetModal(true),
+ )}
css={{ textDecoration: 'underline' }}
>
{t('Create a dataset')}
@@ -420,7 +424,12 @@ const ExploreChartPanel = ({
{t(
'You updated the values in the control panel, but the chart was not updated automatically. Run the query by clicking on the "Update chart" button or',
)}{' '}
- <span role="button" tabIndex={0} onClick={onQuery}>
+ <span
+ role="button"
+ tabIndex={0}
+ onClick={onQuery}
+ onKeyDown={handleKeyboardActivation(onQuery)}
+ >
{t('click here')}
</span>
.
diff --git a/superset-frontend/src/explore/components/ExploreViewContainer/index.tsx b/superset-frontend/src/explore/components/ExploreViewContainer/index.tsx
index aebaa64da17..b0ca4ac0868 100644
--- a/superset-frontend/src/explore/components/ExploreViewContainer/index.tsx
+++ b/superset-frontend/src/explore/components/ExploreViewContainer/index.tsx
@@ -37,6 +37,7 @@ import {
MatrixifyFormData,
DatasourceType,
ensureIsArray,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import {
ControlStateMapping,
@@ -998,6 +999,7 @@ function ExploreViewContainer(props: ExploreViewContainerProps) {
tabIndex={0}
className="action-button"
onClick={toggleCollapse}
+ onKeyDown={handleKeyboardActivation(toggleCollapse)}
>
<Icons.VerticalAlignTopOutlined
iconSize="xl"
@@ -1023,6 +1025,7 @@ function ExploreViewContainer(props: ExploreViewContainerProps) {
<div
className="sidebar"
onClick={toggleCollapse}
+ onKeyDown={handleKeyboardActivation(toggleCollapse)}
data-test="open-datasource-tab"
role="button"
tabIndex={0}
diff --git a/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigControl.tsx b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigControl.tsx
index 239deefaac7..201a90f3f23 100644
--- a/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigControl.tsx
+++ b/superset-frontend/src/explore/components/controls/ColumnConfigControl/ColumnConfigControl.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { useMemo, useState } from 'react';
import { t } from '@apache-superset/core/translation';
import { useTheme } from '@apache-superset/core/theme';
@@ -180,6 +181,9 @@ export default function ColumnConfigControl<T extends ColumnConfig>({
},
}}
onClick={() => setShowAllColumns(!showAllColumns)}
+ onKeyDown={handleKeyboardActivation(() =>
+ setShowAllColumns(!showAllColumns),
+ )}
>
{showAllColumns ? (
<>
diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
index 1e69be2c45e..16d0aa344cc 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
@@ -35,6 +35,7 @@ import {
DatasourceType,
Metric,
QueryFormMetric,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { styled, css } from '@apache-superset/core/theme';
import { ColumnMeta, isSavedExpression } from '@superset-ui/chart-controls';
@@ -476,6 +477,9 @@ const ColumnSelectPopover = ({
role="button"
tabIndex={0}
onClick={setDatasetAndClose}
+ onKeyDown={handleKeyboardActivation(
+ setDatasetAndClose,
+ )}
>
{t('Create a dataset')}
</span>{' '}
@@ -487,6 +491,9 @@ const ColumnSelectPopover = ({
role="button"
tabIndex={0}
onClick={setDatasetAndClose}
+ onKeyDown={handleKeyboardActivation(
+ setDatasetAndClose,
+ )}
>
{t('Create a dataset')}
</span>{' '}
@@ -521,6 +528,9 @@ const ColumnSelectPopover = ({
role="button"
tabIndex={0}
onClick={setDatasetAndClose}
+ onKeyDown={handleKeyboardActivation(
+ setDatasetAndClose,
+ )}
>
{t('Create a dataset')}
</span>{' '}
diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelectPopoverTitle.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelectPopoverTitle.tsx
index 05c3cd3744c..38b3a47a73d 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelectPopoverTitle.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelectPopoverTitle.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { ChangeEvent, useCallback, useState } from 'react';
import { t } from '@apache-superset/core/translation';
import { styled, useTheme } from '@apache-superset/core/theme';
@@ -96,6 +97,7 @@ export const DndColumnSelectPopoverTitle = ({
onMouseOut={onMouseOut}
onFocus={onMouseOver}
onClick={onClick}
+ onKeyDown={onClick ? handleKeyboardActivation(onClick) : undefined}
onBlur={onBlur}
role="button"
tabIndex={0}
diff --git a/superset-frontend/src/explore/components/controls/LayerConfigsControl/LayerTreeItem.tsx b/superset-frontend/src/explore/components/controls/LayerConfigsControl/LayerTreeItem.tsx
index 72f519ff7d8..ce99c88acd3 100644
--- a/superset-frontend/src/explore/components/controls/LayerConfigsControl/LayerTreeItem.tsx
+++ b/superset-frontend/src/explore/components/controls/LayerConfigsControl/LayerTreeItem.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { Icons } from '@superset-ui/core/components/Icons';
import { Button } from '@superset-ui/core/components';
import { Tag } from 'src/components';
@@ -47,6 +48,7 @@ export const LayerTreeItem: FC<LayerTreeItemProps> = ({
<span
className="layer-tree-item-type"
onClick={onEditTag}
+ onKeyDown={handleKeyboardActivation(onEditTag)}
role="button"
tabIndex={0}
>
@@ -55,6 +57,7 @@ export const LayerTreeItem: FC<LayerTreeItemProps> = ({
<span
className="layer-tree-item-title"
onClick={onEditTag}
+ onKeyDown={handleKeyboardActivation(onEditTag)}
role="button"
tabIndex={0}
>
diff --git a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.tsx b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.tsx
index ccbd3a2deab..ce0fea1968b 100644
--- a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.tsx
+++ b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.tsx
@@ -19,7 +19,12 @@
/* eslint-disable camelcase */
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
-import { isDefined, ensureIsArray, DatasourceType } from '@superset-ui/core';
+import {
+ isDefined,
+ ensureIsArray,
+ DatasourceType,
+ handleKeyboardActivation,
+} from '@superset-ui/core';
import { t } from '@apache-superset/core/translation';
import type { editors } from '@apache-superset/core';
import { styled } from '@apache-superset/core/theme';
@@ -498,6 +503,10 @@ function AdhocMetricEditPopover({
handleDatasetModal?.(true);
onClose();
}}
+ onKeyDown={handleKeyboardActivation(() => {
+ handleDatasetModal?.(true);
+ onClose();
+ })}
>
{t('Create a dataset')}
</span>
diff --git a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopoverTitle.tsx b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopoverTitle.tsx
index 57d5ed8e3c2..e9cc283f458 100644
--- a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopoverTitle.tsx
+++ b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopoverTitle.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import {
ChangeEventHandler,
FocusEvent,
@@ -120,6 +121,7 @@ const AdhocMetricEditPopoverTitle: FC<AdhocMetricEditPopoverTitleProps> = ({
onMouseOut={handleMouseOut}
onFocus={handleMouseOver}
onClick={handleClick}
+ onKeyDown={handleKeyboardActivation(handleClick)}
onBlur={handleBlur}
role="button"
tabIndex={0}
diff --git a/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx b/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx
index ad11bd73da9..10c9fceff3f 100644
--- a/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx
+++ b/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx
@@ -18,7 +18,10 @@
*/
import { useCallback, useState } from 'react';
import { t } from '@apache-superset/core/translation';
-import { getChartMetadataRegistry } from '@superset-ui/core';
+import {
+ getChartMetadataRegistry,
+ handleKeyboardActivation,
+} from '@superset-ui/core';
import { css, styled, SupersetTheme } from '@apache-superset/core/theme';
import { usePluginContext } from 'src/components';
import { Icons, Modal } from '@superset-ui/core/components';
@@ -112,7 +115,12 @@ const VizTypeControl = ({
color: ${theme.colorTextTertiary};
`}
>
- <span role="button" tabIndex={0} onClick={openModal}>
+ <span
+ role="button"
+ tabIndex={0}
+ onClick={openModal}
+ onKeyDown={handleKeyboardActivation(openModal)}
+ >
{t('View all charts')}
</span>
</div>
diff --git a/superset-frontend/src/features/alerts/components/NotificationMethod.tsx b/superset-frontend/src/features/alerts/components/NotificationMethod.tsx
index 1a5df776491..6699ed02b67 100644
--- a/superset-frontend/src/features/alerts/components/NotificationMethod.tsx
+++ b/superset-frontend/src/features/alerts/components/NotificationMethod.tsx
@@ -31,6 +31,7 @@ import {
JsonResponse,
SupersetClient,
isFeatureEnabled,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { styled, useTheme } from '@apache-superset/core/theme';
import { Icons } from '@superset-ui/core/components/Icons';
@@ -574,6 +575,7 @@ export const NotificationMethod: FunctionComponent<NotificationMethodProps> = ({
tabIndex={0}
className="delete-button"
onClick={() => onRemove(index)}
+ onKeyDown={handleKeyboardActivation(() => onRemove(index))}
aria-label={t('Remove notification method')}
>
<Icons.DeleteOutlined iconSize="l" />
@@ -773,6 +775,7 @@ export const NotificationMethod: FunctionComponent<NotificationMethodProps> = ({
role="button"
tabIndex={0}
onClick={() => setCcVisible(true)}
+ onKeyDown={handleKeyboardActivation(() => setCcVisible(true))}
style={{ display: ccVisible ? 'none' : 'inline-flex' }}
>
<Icons.MailOutlined iconSize="xs" className="icon" />
@@ -783,6 +786,9 @@ export const NotificationMethod: FunctionComponent<NotificationMethodProps> = ({
role="button"
tabIndex={0}
onClick={() => setBccVisible(true)}
+ onKeyDown={handleKeyboardActivation(() =>
+ setBccVisible(true),
+ )}
style={{ display: bccVisible ? 'none' : 'inline-flex' }}
>
<Icons.MailOutlined iconSize="xs" className="icon" />
diff --git a/superset-frontend/src/features/charts/ChartCard.tsx b/superset-frontend/src/features/charts/ChartCard.tsx
index 9a7c0569ed2..b55904f7144 100644
--- a/superset-frontend/src/features/charts/ChartCard.tsx
+++ b/superset-frontend/src/features/charts/ChartCard.tsx
@@ -17,7 +17,11 @@
* under the License.
*/
import { t } from '@apache-superset/core/translation';
-import { isFeatureEnabled, FeatureFlag } from '@superset-ui/core';
+import {
+ isFeatureEnabled,
+ FeatureFlag,
+ handleKeyboardActivation,
+} from '@superset-ui/core';
import { css } from '@apache-superset/core/theme';
import { Link, useHistory } from 'react-router-dom';
import {
@@ -86,6 +90,7 @@ export default function ChartCard({
role="button"
tabIndex={0}
onClick={() => openChartEditModal(chart)}
+ onKeyDown={handleKeyboardActivation(() => openChartEditModal(chart))}
>
<Icons.EditOutlined
iconSize="l"
@@ -107,6 +112,9 @@ export default function ChartCard({
role="button"
tabIndex={0}
onClick={() => handleBulkChartExport([chart])}
+ onKeyDown={handleKeyboardActivation(() =>
+ handleBulkChartExport([chart]),
+ )}
>
<Icons.UploadOutlined
iconSize="l"
@@ -150,6 +158,7 @@ export default function ChartCard({
tabIndex={0}
className="action-button"
onClick={confirmDelete}
+ onKeyDown={handleKeyboardActivation(confirmDelete)}
>
<Icons.DeleteOutlined
iconSize="l"
diff --git a/superset-frontend/src/features/dashboards/DashboardCard.tsx b/superset-frontend/src/features/dashboards/DashboardCard.tsx
index 6b45288641a..1b1096a24dc 100644
--- a/superset-frontend/src/features/dashboards/DashboardCard.tsx
+++ b/superset-frontend/src/features/dashboards/DashboardCard.tsx
@@ -18,7 +18,11 @@
*/
import { Link, useHistory } from 'react-router-dom';
import { t } from '@apache-superset/core/translation';
-import { isFeatureEnabled, FeatureFlag } from '@superset-ui/core';
+import {
+ isFeatureEnabled,
+ FeatureFlag,
+ handleKeyboardActivation,
+} from '@superset-ui/core';
import { CardStyles } from 'src/views/CRUD/utils';
import {
FaveStar,
@@ -80,6 +84,9 @@ function DashboardCard({
tabIndex={0}
className="action-button"
onClick={() => openDashboardEditModal(dashboard)}
+ onKeyDown={handleKeyboardActivation(() =>
+ openDashboardEditModal(dashboard),
+ )}
data-test="dashboard-card-option-edit-button"
>
<Icons.EditOutlined iconSize="l" data-test="edit-alt" /> {t('Edit')}
@@ -96,6 +103,9 @@ function DashboardCard({
role="button"
tabIndex={0}
onClick={() => handleBulkDashboardExport([dashboard])}
+ onKeyDown={handleKeyboardActivation(() =>
+ handleBulkDashboardExport([dashboard]),
+ )}
className="action-button"
data-test="dashboard-card-option-export-button"
>
@@ -114,6 +124,7 @@ function DashboardCard({
tabIndex={0}
className="action-button"
onClick={() => onDelete(dashboard)}
+ onKeyDown={handleKeyboardActivation(() => onDelete(dashboard))}
data-test="dashboard-card-option-delete-button"
>
<Icons.DeleteOutlined iconSize="l" /> {t('Delete')}
diff --git a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.tsx b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.tsx
index 6bf357a1f3b..ad29c510699 100644
--- a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.tsx
+++ b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { handleKeyboardActivation } from '@superset-ui/core';
import { t } from '@apache-superset/core/translation';
import { Alert } from '@apache-superset/core/components';
import { styled } from '@apache-superset/core/theme';
@@ -235,6 +236,11 @@ const renderExistingDatasetAlert = (dataset?: DatasetObject) => (
openInNewTab(stripAppRoot(dataset.explore_url));
}
}}
+ onKeyDown={handleKeyboardActivation(() => {
+ if (dataset?.explore_url) {
+ openInNewTab(stripAppRoot(dataset.explore_url));
+ }
+ })}
tabIndex={0}
className="view-dataset-button"
>
diff --git a/superset-frontend/src/pages/ChartList/index.tsx b/superset-frontend/src/pages/ChartList/index.tsx
index 45e65370170..00ea34af07e 100644
--- a/superset-frontend/src/pages/ChartList/index.tsx
+++ b/superset-frontend/src/pages/ChartList/index.tsx
@@ -25,6 +25,7 @@ import {
JsonResponse,
SupersetClient,
isMatrixifyEnabled,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { css, styled } from '@apache-superset/core/theme';
import { useState, useMemo, useCallback } from 'react';
@@ -544,6 +545,7 @@ function ChartList(props: ChartListProps) {
tabIndex={0}
className="action-button"
onClick={openEditModal}
+ onKeyDown={handleKeyboardActivation(openEditModal)}
>
<Icons.EditOutlined data-test="edit-alt" iconSize="l" />
</span>
@@ -561,6 +563,7 @@ function ChartList(props: ChartListProps) {
tabIndex={0}
className="action-button"
onClick={handleExport}
+ onKeyDown={handleKeyboardActivation(handleExport)}
>
<Icons.UploadOutlined iconSize="l" />
</span>
@@ -589,6 +592,7 @@ function ChartList(props: ChartListProps) {
tabIndex={0}
className="action-button"
onClick={confirmDelete}
+ onKeyDown={handleKeyboardActivation(confirmDelete)}
>
<Icons.DeleteOutlined iconSize="l" />
</span>
diff --git a/superset-frontend/src/pages/DashboardList/index.tsx b/superset-frontend/src/pages/DashboardList/index.tsx
index 100669050bb..91d3ef3bc80 100644
--- a/superset-frontend/src/pages/DashboardList/index.tsx
+++ b/superset-frontend/src/pages/DashboardList/index.tsx
@@ -21,6 +21,7 @@ import {
isFeatureEnabled,
FeatureFlag,
SupersetClient,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { styled } from '@apache-superset/core/theme';
import { useSelector } from 'react-redux';
@@ -503,6 +504,7 @@ function DashboardList(props: DashboardListProps) {
tabIndex={0}
className="action-button"
onClick={handleEdit}
+ onKeyDown={handleKeyboardActivation(handleEdit)}
>
<Icons.EditOutlined data-test="edit-alt" iconSize="l" />
</span>
@@ -520,6 +522,7 @@ function DashboardList(props: DashboardListProps) {
tabIndex={0}
className="action-button"
onClick={handleExport}
+ onKeyDown={handleKeyboardActivation(handleExport)}
>
<Icons.UploadOutlined iconSize="l" />
</span>
@@ -548,6 +551,7 @@ function DashboardList(props: DashboardListProps) {
tabIndex={0}
className="action-button"
onClick={confirmDelete}
+ onKeyDown={handleKeyboardActivation(confirmDelete)}
>
<Icons.DeleteOutlined
iconSize="l"
diff --git a/superset-frontend/src/pages/DatabaseList/index.tsx b/superset-frontend/src/pages/DatabaseList/index.tsx
index aa284e26095..e36c3f38819 100644
--- a/superset-frontend/src/pages/DatabaseList/index.tsx
+++ b/superset-frontend/src/pages/DatabaseList/index.tsx
@@ -22,6 +22,7 @@ import {
SupersetClient,
isFeatureEnabled,
FeatureFlag,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { css, styled, useTheme } from '@apache-superset/core/theme';
import { useState, useMemo, useEffect, useCallback } from 'react';
@@ -697,6 +698,9 @@ function DatabaseList({
tabIndex={0}
className="action-button"
onClick={() => setSlCurrentlyDeleting(original)}
+ onKeyDown={handleKeyboardActivation(() =>
+ setSlCurrentlyDeleting(original),
+ )}
>
<Icons.DeleteOutlined iconSize="l" />
</span>
@@ -715,6 +719,9 @@ function DatabaseList({
onClick={() =>
setSlCurrentlyEditing(original.uuid ?? null)
}
+ onKeyDown={handleKeyboardActivation(() =>
+ setSlCurrentlyEditing(original.uuid ?? null),
+ )}
>
<Icons.EditOutlined iconSize="l" />
</span>
@@ -746,6 +753,7 @@ function DatabaseList({
tabIndex={0}
className="action-button"
onClick={handleEdit}
+ onKeyDown={handleKeyboardActivation(handleEdit)}
>
<Icons.EditOutlined data-test="edit-alt" iconSize="l" />
</span>
@@ -762,6 +770,7 @@ function DatabaseList({
tabIndex={0}
className="action-button"
onClick={handleExport}
+ onKeyDown={handleKeyboardActivation(handleExport)}
>
<Icons.UploadOutlined iconSize="l" />
</span>
@@ -779,6 +788,7 @@ function DatabaseList({
tabIndex={0}
className="action-button"
onClick={handleSync}
+ onKeyDown={handleKeyboardActivation(handleSync)}
>
<Icons.SyncOutlined iconSize="l" />
</span>
@@ -791,6 +801,7 @@ function DatabaseList({
className="action-button"
data-test="database-delete"
onClick={handleDelete}
+ onKeyDown={handleKeyboardActivation(handleDelete)}
>
<Tooltip
id="delete-action-tooltip"
diff --git a/superset-frontend/src/pages/DatasetList/index.tsx b/superset-frontend/src/pages/DatasetList/index.tsx
index 4c89289077e..84c4d306d2d 100644
--- a/superset-frontend/src/pages/DatasetList/index.tsx
+++ b/superset-frontend/src/pages/DatasetList/index.tsx
@@ -22,6 +22,7 @@ import {
SupersetClient,
isFeatureEnabled,
FeatureFlag,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { styled, useTheme, css } from '@apache-superset/core/theme';
import {
@@ -869,6 +870,9 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
tabIndex={0}
className="action-button"
onClick={() => handleSemanticViewDelete(original)}
+ onKeyDown={handleKeyboardActivation(() =>
+ handleSemanticViewDelete(original),
+ )}
>
<Icons.DeleteOutlined iconSize="l" />
</span>
@@ -886,6 +890,9 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
tabIndex={0}
className="action-button"
onClick={() => setSvCurrentlyEditing(original)}
+ onKeyDown={handleKeyboardActivation(() =>
+ setSvCurrentlyEditing(original),
+ )}
>
<Icons.EditOutlined iconSize="l" />
</span>
@@ -932,6 +939,11 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
tabIndex={0}
className={`action-button ${allowEdit ? '' : 'disabled'}`}
onClick={allowEdit ? handleEdit : undefined}
+ onKeyDown={
+ allowEdit
+ ? handleKeyboardActivation(handleEdit)
+ : undefined
+ }
>
<Icons.EditOutlined iconSize="l" />
</span>
@@ -949,6 +961,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
tabIndex={0}
className="action-button"
onClick={handleExport}
+ onKeyDown={handleKeyboardActivation(handleExport)}
>
<Icons.UploadOutlined iconSize="l" />
</span>
@@ -966,6 +979,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
tabIndex={0}
className="action-button"
onClick={handleDuplicate}
+ onKeyDown={handleKeyboardActivation(handleDuplicate)}
>
<Icons.CopyOutlined iconSize="l" />
</span>
@@ -983,6 +997,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
tabIndex={0}
className="action-button"
onClick={handleDelete}
+ onKeyDown={handleKeyboardActivation(handleDelete)}
>
<Icons.DeleteOutlined iconSize="l" />
</span>
diff --git a/superset-frontend/src/pages/RowLevelSecurityList/index.tsx b/superset-frontend/src/pages/RowLevelSecurityList/index.tsx
index 4d77911ba1d..689c493e49e 100644
--- a/superset-frontend/src/pages/RowLevelSecurityList/index.tsx
+++ b/superset-frontend/src/pages/RowLevelSecurityList/index.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { t } from '@apache-superset/core/translation';
-import { SupersetClient } from '@superset-ui/core';
+import { SupersetClient, handleKeyboardActivation } from '@superset-ui/core';
import { useCallback, useMemo, useState } from 'react';
import { ConfirmStatusChange, Tooltip } from '@superset-ui/core/components';
import {
@@ -209,6 +209,7 @@ function RowLevelSecurityList(props: RLSProps) {
tabIndex={0}
className="action-button"
onClick={handleEdit}
+ onKeyDown={handleKeyboardActivation(handleEdit)}
>
<Icons.EditOutlined data-test="edit-alt" iconSize="l" />
</span>
@@ -236,6 +237,7 @@ function RowLevelSecurityList(props: RLSProps) {
tabIndex={0}
className="action-button"
onClick={confirmDelete}
+ onKeyDown={handleKeyboardActivation(confirmDelete)}
>
<Icons.DeleteOutlined
data-test="rls-list-trash-icon"
diff --git a/superset-frontend/src/pages/Tags/index.tsx b/superset-frontend/src/pages/Tags/index.tsx
index ffd7bd591cc..00cfbe6c400 100644
--- a/superset-frontend/src/pages/Tags/index.tsx
+++ b/superset-frontend/src/pages/Tags/index.tsx
@@ -18,7 +18,11 @@
*/
import { useMemo, useState } from 'react';
import { t } from '@apache-superset/core/translation';
-import { isFeatureEnabled, FeatureFlag } from '@superset-ui/core';
+import {
+ isFeatureEnabled,
+ FeatureFlag,
+ handleKeyboardActivation,
+} from '@superset-ui/core';
import {
Actions,
createErrorHandler,
@@ -219,6 +223,7 @@ function TagList(props: TagListProps) {
tabIndex={0}
className="action-button"
onClick={confirmDelete}
+ onKeyDown={handleKeyboardActivation(confirmDelete)}
>
<Icons.DeleteOutlined
data-test="dashboard-list-trash-icon"
@@ -240,6 +245,7 @@ function TagList(props: TagListProps) {
tabIndex={0}
className="action-button"
onClick={handleEdit}
+ onKeyDown={handleKeyboardActivation(handleEdit)}
>
<Icons.EditOutlined data-test="edit-alt" iconSize="l" />
</span>
diff --git a/superset-frontend/src/pages/TaskList/index.tsx b/superset-frontend/src/pages/TaskList/index.tsx
index 41b25d65602..3acac2d5177 100644
--- a/superset-frontend/src/pages/TaskList/index.tsx
+++ b/superset-frontend/src/pages/TaskList/index.tsx
@@ -21,6 +21,7 @@ import {
FeatureFlag,
isFeatureEnabled,
SupersetClient,
+ handleKeyboardActivation,
} from '@superset-ui/core';
import { useTheme } from '@apache-superset/core/theme';
import { t } from '@apache-superset/core/translation';
@@ -519,6 +520,9 @@ function TaskList({ addDangerToast, addSuccessToast, user }: TaskListProps) {
tabIndex={0}
className="action-button"
onClick={() => openCancelModal(original)}
+ onKeyDown={handleKeyboardActivation(() =>
+ openCancelModal(original),
+ )}
>
<Icons.StopOutlined iconSize="l" />
</span>