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:45 UTC
(superset) 01/01: chore(a11y): enable jsx-a11y/click-events-have-key-events as error
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>