You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by di...@apache.org on 2025/05/16 09:09:58 UTC
(superset) branch geido/fix/native-filters-applied-value created (now b0bfc27e11)
This is an automated email from the ASF dual-hosted git repository.
diegopucci pushed a change to branch geido/fix/native-filters-applied-value
in repository https://gitbox.apache.org/repos/asf/superset.git
at b0bfc27e11 fix(Native Filters): Apply existing value if it exists
This branch includes the following new commits:
new deeacb0dfb chore(Accessibility): Improve keyboard navigation and screen access
new b0bfc27e11 fix(Native Filters): Apply existing value if it exists
The 2 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) 02/02: fix(Native Filters): Apply existing value if it exists
Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
diegopucci pushed a commit to branch geido/fix/native-filters-applied-value
in repository https://gitbox.apache.org/repos/asf/superset.git
commit b0bfc27e1162f3740272bff4c344637d714a0c24
Author: Diego Pucci <di...@gmail.com>
AuthorDate: Fri May 16 11:09:39 2025 +0200
fix(Native Filters): Apply existing value if it exists
---
.../src/filters/components/Select/SelectFilterPlugin.tsx | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
index 67911ca296..c0a0e4066e 100644
--- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
+++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
@@ -335,6 +335,12 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
return;
}
+ if (filterState.value !== undefined) {
+ // Set the filter state value if it is defined
+ updateDataMask(filterState.value);
+ return;
+ }
+
// Case 2: Handle the default to first Value case
if (defaultToFirstItem) {
// Set to first item if defaultToFirstItem is true
@@ -353,6 +359,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
enableEmptyFilter,
defaultToFirstItem,
formData?.defaultValue,
+ filterState.value,
data,
groupby,
col,
(superset) 01/02: chore(Accessibility): Improve keyboard navigation and screen access
Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
diegopucci pushed a commit to branch geido/fix/native-filters-applied-value
in repository https://gitbox.apache.org/repos/asf/superset.git
commit deeacb0dfb850758a962746253daa3e17809c05a
Author: Diego Pucci <di...@gmail.com>
AuthorDate: Fri May 9 14:37:35 2025 +0200
chore(Accessibility): Improve keyboard navigation and screen access
---
.../src/components/ControlHeader.tsx | 6 +--
.../components/RadioButtonControl.tsx | 23 ++++++++++-
.../src/components/Datasource/DatasourceEditor.jsx | 2 +
superset-frontend/src/components/Modal/Modal.tsx | 19 +++++++--
.../src/components/Pagination/Item.tsx | 3 +-
.../src/components/Select/AsyncSelect.tsx | 6 ++-
superset-frontend/src/components/Select/Select.tsx | 6 ++-
superset-frontend/src/components/Select/styles.tsx | 4 ++
superset-frontend/src/components/Select/utils.tsx | 21 +++++++++-
.../src/components/TimezoneSelector/index.tsx | 2 +
.../components/controls/TextAreaControl.jsx | 1 +
.../controls/VizTypeControl/VizTypeGallery.tsx | 6 ++-
.../src/features/alerts/AlertReportModal.tsx | 16 ++++++++
.../src/features/alerts/components/NumberInput.tsx | 2 +
superset-frontend/src/features/home/SubMenu.tsx | 17 +++++---
.../Pagination/Item.tsx => hooks/useOpenerRef.ts} | 32 +++++----------
.../src/pages/AlertReportList/index.tsx | 46 +++++++++++++---------
17 files changed, 154 insertions(+), 58 deletions(-)
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlHeader.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlHeader.tsx
index 44e47d55e9..e1968f91a9 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlHeader.tsx
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/components/ControlHeader.tsx
@@ -94,9 +94,9 @@ export function ControlHeader({
<label className="control-label" htmlFor={name}>
{leftNode && <span>{leftNode}</span>}
<span
- role="button"
- tabIndex={0}
- onClick={onClick}
+ role={onClick ? 'button' : undefined}
+ {...(onClick ? { onClick } : {})}
+ {...(onClick ? { tabIndex: 0 } : {})}
className={labelClass}
style={{ cursor: onClick ? 'pointer' : '' }}
>
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/RadioButtonControl.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/RadioButtonControl.tsx
index 3af514f171..13e19427c3 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/RadioButtonControl.tsx
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/RadioButtonControl.tsx
@@ -69,17 +69,24 @@ export default function RadioButtonControl({
boxShadow: 'none',
},
}}
+ role="tablist"
+ aria-label={typeof props.label === 'string' ? props.label : undefined}
>
<ControlHeader {...props} />
<div className="btn-group btn-group-sm">
{options.map(([val, label]) => (
<button
+ aria-label={typeof label === 'string' ? label : undefined}
+ id={`tab-${val}`}
key={JSON.stringify(val)}
type="button"
+ aria-selected={val === currentValue}
+ role="tab"
className={`btn btn-default ${
val === currentValue ? 'active' : ''
}`}
- onClick={() => {
+ onClick={e => {
+ e.currentTarget?.focus();
onChange(val);
}}
>
@@ -87,6 +94,20 @@ export default function RadioButtonControl({
</button>
))}
</div>
+ {/* accessibility begin */}
+ <div
+ aria-live="polite"
+ style={{
+ position: 'absolute',
+ left: '-9999px',
+ height: '1px',
+ width: '1px',
+ overflow: 'hidden',
+ }}
+ >
+ {options.find(([val]) => val === currentValue)?.[1]} tab selected
+ </div>
+ {/* accessibility end */}
</div>
);
}
diff --git a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
index 4f21e7ae60..9fba99d8e2 100644
--- a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
+++ b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
@@ -432,6 +432,7 @@ function ColumnCollectionTable({
).is_dttm;
return (
<Radio
+ aria-label={t('Set %s as default datetime column', record.column_name)}
data-test={`radio-default-dttm-${record.column_name}`}
checked={checked}
disabled={disabled}
@@ -482,6 +483,7 @@ function ColumnCollectionTable({
).is_dttm;
return (
<Radio
+ aria-label={t('Set %s as default datetime column', record.column_name)}
data-test={`radio-default-dttm-${record.column_name}`}
checked={checked}
disabled={disabled}
diff --git a/superset-frontend/src/components/Modal/Modal.tsx b/superset-frontend/src/components/Modal/Modal.tsx
index a521586086..0be298cf5c 100644
--- a/superset-frontend/src/components/Modal/Modal.tsx
+++ b/superset-frontend/src/components/Modal/Modal.tsx
@@ -71,6 +71,7 @@ export interface ModalProps {
maskClosable?: boolean;
zIndex?: number;
bodyStyle?: CSSProperties;
+ openerRef?: React.RefObject<HTMLElement>;
}
interface StyledModalProps {
@@ -276,22 +277,34 @@ const CustomModal = ({
resizableConfig = defaultResizableConfig(hideFooter),
draggableConfig,
destroyOnClose,
+ openerRef = null,
...rest
}: ModalProps) => {
const draggableRef = useRef<HTMLDivElement>(null);
const [bounds, setBounds] = useState<DraggableBounds>();
const [dragDisabled, setDragDisabled] = useState<boolean>(true);
+
+ const handleOnHide = () => {
+ openerRef.current?.focus();
+ onHide();
+ };
+
let FooterComponent;
if (isValidElement(footer)) {
// If a footer component is provided inject a closeModal function
// so the footer can provide a "close" button if desired
FooterComponent = cloneElement(footer, {
- closeModal: onHide,
+ closeModal: handleOnHide,
} as Partial<unknown>);
}
const modalFooter = isNil(FooterComponent)
? [
- <Button key="back" onClick={onHide} cta data-test="modal-cancel-button">
+ <Button
+ key="back"
+ onClick={handleOnHide}
+ cta
+ data-test="modal-cancel-button"
+ >
{t('Cancel')}
</Button>,
<Button
@@ -350,7 +363,7 @@ const CustomModal = ({
<StyledModal
centered={!!centered}
onOk={onHandledPrimaryAction}
- onCancel={onHide}
+ onCancel={handleOnHide}
width={modalWidth}
maxWidth={maxWidth}
responsive={responsive}
diff --git a/superset-frontend/src/components/Pagination/Item.tsx b/superset-frontend/src/components/Pagination/Item.tsx
index d214499f9e..b71da3c939 100644
--- a/superset-frontend/src/components/Pagination/Item.tsx
+++ b/superset-frontend/src/components/Pagination/Item.tsx
@@ -31,7 +31,8 @@ export function Item({ active, children, onClick }: PaginationItemButton) {
<li className={classNames({ active })}>
<span
role="button"
- tabIndex={active ? -1 : 0}
+ tabIndex={0}
+ aria-current={active ? 'page' : undefined}
onClick={e => {
e.preventDefault();
if (!active) onClick(e);
diff --git a/superset-frontend/src/components/Select/AsyncSelect.tsx b/superset-frontend/src/components/Select/AsyncSelect.tsx
index 4615f93418..5e832e14bf 100644
--- a/superset-frontend/src/components/Select/AsyncSelect.tsx
+++ b/superset-frontend/src/components/Select/AsyncSelect.tsx
@@ -600,7 +600,11 @@ const AsyncSelect = forwardRef(
)}
<StyledSelect
allowClear={!isLoading && allowClear}
- aria-label={ariaLabel || name}
+ aria-label={
+ isSingleMode && isLabeledValue(selectValue)
+ ? `${ariaLabel || name}: ${selectValue.label}`
+ : ariaLabel || name
+ }
autoClearSearchValue={autoClearSearchValue}
dropdownRender={dropdownRender}
filterOption={handleFilterOption}
diff --git a/superset-frontend/src/components/Select/Select.tsx b/superset-frontend/src/components/Select/Select.tsx
index 71ca926632..d91a225c2e 100644
--- a/superset-frontend/src/components/Select/Select.tsx
+++ b/superset-frontend/src/components/Select/Select.tsx
@@ -673,7 +673,11 @@ const Select = forwardRef(
<StyledSelect
id={name}
allowClear={!isLoading && allowClear}
- aria-label={ariaLabel}
+ aria-label={
+ isSingleMode && isLabeledValue(selectValue)
+ ? `${ariaLabel || name}: ${selectValue.label}`
+ : ariaLabel || name
+ }
autoClearSearchValue={autoClearSearchValue}
dropdownRender={dropdownRender}
filterOption={handleFilterOption}
diff --git a/superset-frontend/src/components/Select/styles.tsx b/superset-frontend/src/components/Select/styles.tsx
index 966d9ade42..a56f349386 100644
--- a/superset-frontend/src/components/Select/styles.tsx
+++ b/superset-frontend/src/components/Select/styles.tsx
@@ -46,6 +46,10 @@ export const StyledSelect = styled(AntdSelect, {
shouldForwardProp: prop => prop !== 'headerPosition' && prop !== 'oneLine',
})<{ headerPosition?: string; oneLine?: boolean }>`
${({ theme, headerPosition, oneLine }) => `
+ .ant-select-item-option-active:not(.ant-select-item-option-disabled) {
+ outline: 2px solid ${theme.colors.primary.base};
+ outline-offset: -2px;
+ }
flex: ${headerPosition === 'left' ? 1 : 0};
&& .ant-select-selector {
border-radius: ${theme.gridUnit}px;
diff --git a/superset-frontend/src/components/Select/utils.tsx b/superset-frontend/src/components/Select/utils.tsx
index 528d9249df..3db5cdb24f 100644
--- a/superset-frontend/src/components/Select/utils.tsx
+++ b/superset-frontend/src/components/Select/utils.tsx
@@ -163,12 +163,31 @@ export const dropDownRenderHelper = (
if (errorComponent) {
return errorComponent;
}
+
+ // remap for accessibility for proper item count
+ const accessibilityNode = {
+ ...originNode,
+ props: {
+ ...originNode.props,
+ flattenOptions: ensureIsArray(originNode.props.flattenOptions).map(
+ (opt: Record<string, any>, idx: number) => ({
+ ...opt,
+ data: {
+ ...opt.data,
+ 'aria-setsize': originNode.props.flattenOptions?.length || 0,
+ 'aria-posinset': idx + 1,
+ },
+ }),
+ ),
+ },
+ };
+
return (
<>
{helperText && (
<StyledHelperText role="note">{helperText}</StyledHelperText>
)}
- {originNode}
+ {accessibilityNode}
{bulkSelectComponents}
</>
);
diff --git a/superset-frontend/src/components/TimezoneSelector/index.tsx b/superset-frontend/src/components/TimezoneSelector/index.tsx
index 4a3c69ca08..d815b94b29 100644
--- a/superset-frontend/src/components/TimezoneSelector/index.tsx
+++ b/superset-frontend/src/components/TimezoneSelector/index.tsx
@@ -55,6 +55,7 @@ export default function TimezoneSelector({
onTimezoneChange,
timezone,
minWidth = MIN_SELECT_WIDTH, // smallest size for current values
+ ...rest
}: TimezoneSelectorProps) {
const { TIMEZONE_OPTIONS, TIMEZONE_OPTIONS_SORT_COMPARATOR, validTimezone } =
useMemo(() => {
@@ -156,6 +157,7 @@ export default function TimezoneSelector({
value={validTimezone}
options={TIMEZONE_OPTIONS}
sortComparator={TIMEZONE_OPTIONS_SORT_COMPARATOR}
+ {...rest}
/>
);
}
diff --git a/superset-frontend/src/explore/components/controls/TextAreaControl.jsx b/superset-frontend/src/explore/components/controls/TextAreaControl.jsx
index e8f165c8e1..171735c2ce 100644
--- a/superset-frontend/src/explore/components/controls/TextAreaControl.jsx
+++ b/superset-frontend/src/explore/components/controls/TextAreaControl.jsx
@@ -131,6 +131,7 @@ class TextAreaControl extends Component {
defaultValue={this.props.initialValue}
disabled={this.props.readOnly}
style={{ height: this.props.height }}
+ aria-required={this.props["aria-required"]}
/>
</div>
);
diff --git a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
index 13512e5d8c..66ba957789 100644
--- a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
+++ b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
@@ -418,11 +418,15 @@ const Selector: FC<{
return (
<SelectorLabel
+ aria-label={selector}
+ aria-selected={isSelected}
ref={btnRef}
key={selector}
name={selector}
className={cx(className, isSelected && 'selected')}
onClick={() => onClick(selector, sectionId)}
+ tabIndex={0}
+ role="tab"
>
{icon}
{selector}
@@ -656,7 +660,7 @@ export default function VizTypeGallery(props: VizTypeGalleryProps) {
className={className}
isSelectedVizMetadata={Boolean(selectedVizMetadata)}
>
- <LeftPane>
+ <LeftPane aria-label={t('Choose chart type')} role="tablist">
<Selector
css={({ gridUnit }) =>
// adjust style for not being inside a collapse
diff --git a/superset-frontend/src/features/alerts/AlertReportModal.tsx b/superset-frontend/src/features/alerts/AlertReportModal.tsx
index ba2e9db945..062f5602d7 100644
--- a/superset-frontend/src/features/alerts/AlertReportModal.tsx
+++ b/superset-frontend/src/features/alerts/AlertReportModal.tsx
@@ -72,6 +72,7 @@ import {
import { useSelector } from 'react-redux';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
import { Icons } from 'src/components/Icons';
+import { useOpenerRef } from 'src/hooks/useOpenerRef';
import NumberInput from './components/NumberInput';
import { AlertReportCronScheduler } from './components/AlertReportCronScheduler';
import { NotificationMethod } from './components/NotificationMethod';
@@ -431,6 +432,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
isReport = false,
addSuccessToast,
}) => {
+ const openerRef = useOpenerRef(show);
const currentUser = useSelector<any, UserWithPermissionsAndRoles>(
state => state.user,
);
@@ -1465,6 +1467,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
width="500px"
centered
title={<h4 data-test="alert-report-modal-title">{getTitleText()}</h4>}
+ openerRef={openerRef}
>
<Collapse
expandIconPosition="right"
@@ -1504,6 +1507,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
isReport ? t('Enter report name') : t('Enter alert name')
}
onChange={onInputChange}
+ aria-required="true"
/>
</div>
</StyledInputContainer>
@@ -1515,6 +1519,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
<div data-test="owners-select" className="input-container">
<AsyncSelect
ariaLabel={t('Owners')}
+ aria-required="true"
allowClear
name="owners"
mode="multiple"
@@ -1581,6 +1586,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
<div className="input-container">
<AsyncSelect
ariaLabel={t('Database')}
+ aria-required="true"
name="source"
placeholder={t('Select database')}
value={
@@ -1617,6 +1623,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
readOnly={false}
initialValue={resource?.sql}
key={currentAlert?.id}
+ aria-required="true"
/>
</StyledInputContainer>
<div className="inline-container wrap">
@@ -1628,6 +1635,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
<div className="input-container">
<Select
ariaLabel={t('Condition')}
+ aria-required="true"
onChange={onConditionChange}
placeholder={t('Condition')}
value={currentAlert?.validator_config_json?.op || undefined}
@@ -1654,6 +1662,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
}
placeholder={t('Value')}
onChange={onThresholdChange}
+ aria-required="true"
/>
</div>
</StyledInputContainer>
@@ -1688,6 +1697,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
value={contentType}
options={CONTENT_TYPE_OPTIONS}
placeholder={t('Select content type')}
+ aria-required="true"
/>
</StyledInputContainer>
<StyledInputContainer>
@@ -1699,6 +1709,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
</div>
<AsyncSelect
ariaLabel={t('Chart')}
+ aria-required="true"
name="chart"
value={
currentAlert?.chart?.label && currentAlert?.chart?.value
@@ -1721,6 +1732,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
</div>
<AsyncSelect
ariaLabel={t('Dashboard')}
+ aria-required="true"
name="dashboard"
value={
currentAlert?.dashboard?.label &&
@@ -1751,6 +1763,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
</div>
<Select
ariaLabel={t('Select format')}
+ aria-required="true"
onChange={onFormatChange}
value={reportFormat}
options={
@@ -1845,6 +1858,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
onTimezoneChange={onTimezoneChange}
timezone={currentAlert?.timezone}
minWidth="100%"
+ aria-required="true"
/>
</StyledInputContainer>
<StyledInputContainer>
@@ -1855,6 +1869,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
<div className="input-container">
<Select
ariaLabel={t('Log retention')}
+ aria-required="true"
placeholder={t('Log retention')}
onChange={onLogRetentionChange}
value={currentAlert?.log_retention}
@@ -1878,6 +1893,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
placeholder={t('Time in seconds')}
onChange={onTimeoutVerifyChange}
timeUnit={t('seconds')}
+ aria-required="true"
/>
</div>
</>
diff --git a/superset-frontend/src/features/alerts/components/NumberInput.tsx b/superset-frontend/src/features/alerts/components/NumberInput.tsx
index 2975640547..1254e576dc 100644
--- a/superset-frontend/src/features/alerts/components/NumberInput.tsx
+++ b/superset-frontend/src/features/alerts/components/NumberInput.tsx
@@ -34,6 +34,7 @@ export default function NumberInput({
value,
placeholder,
onChange,
+ ...rest
}: NumberInputProps) {
const [isFocused, setIsFocused] = useState<boolean>(false);
@@ -47,6 +48,7 @@ export default function NumberInput({
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onChange={onChange}
+ {...rest}
/>
);
}
diff --git a/superset-frontend/src/features/home/SubMenu.tsx b/superset-frontend/src/features/home/SubMenu.tsx
index dd03fba9d1..83296066da 100644
--- a/superset-frontend/src/features/home/SubMenu.tsx
+++ b/superset-frontend/src/features/home/SubMenu.tsx
@@ -120,6 +120,8 @@ type MenuChild = {
usesRouter?: boolean;
onClick?: () => void;
'data-test'?: string;
+ id?: string;
+ 'aria-controls'?: string;
};
export interface ButtonProps {
@@ -196,20 +198,22 @@ const SubMenuComponent: FunctionComponent<SubMenuProps> = props => {
<StyledHeader>
<Row className="menu" role="navigation">
{props.name && <div className="header">{props.name}</div>}
- <Menu mode={showMenu} disabledOverflow>
+ <Menu mode={showMenu} disabledOverflow role="tablist">
{props.tabs?.map(tab => {
if ((props.usesRouter || hasHistory) && !!tab.usesRouter) {
return (
<Menu.Item key={tab.label}>
- <div
+ <Link
+ to={tab.url || ''}
role="tab"
+ id={tab.id || tab.name}
data-test={tab['data-test']}
+ aria-selected={tab.name === props.activeChild}
+ aria-controls={tab['aria-controls'] || ''}
className={tab.name === props.activeChild ? 'active' : ''}
>
- <div>
- <Link to={tab.url || ''}>{tab.label}</Link>
- </div>
- </div>
+ {tab.label}
+ </Link>
</Menu.Item>
);
}
@@ -221,6 +225,7 @@ const SubMenuComponent: FunctionComponent<SubMenuProps> = props => {
active: tab.name === props.activeChild,
})}
role="tab"
+ aria-selected={tab.name === props.activeChild}
>
<a href={tab.url} onClick={tab.onClick}>
{tab.label}
diff --git a/superset-frontend/src/components/Pagination/Item.tsx b/superset-frontend/src/hooks/useOpenerRef.ts
similarity index 58%
copy from superset-frontend/src/components/Pagination/Item.tsx
copy to superset-frontend/src/hooks/useOpenerRef.ts
index d214499f9e..be4b115ede 100644
--- a/superset-frontend/src/components/Pagination/Item.tsx
+++ b/superset-frontend/src/hooks/useOpenerRef.ts
@@ -17,28 +17,16 @@
* under the License.
*/
-import { ReactNode } from 'react';
-import classNames from 'classnames';
-import { PaginationButtonProps } from './types';
+import { useEffect, useRef } from 'react';
-interface PaginationItemButton extends PaginationButtonProps {
- active?: boolean;
- children: ReactNode;
-}
+export function useOpenerRef(active: boolean) {
+ const openerRef = useRef<HTMLElement | null>(null);
+
+ useEffect(() => {
+ if (active) {
+ openerRef.current = document.activeElement as HTMLElement;
+ }
+ }, [active]);
-export function Item({ active, children, onClick }: PaginationItemButton) {
- return (
- <li className={classNames({ active })}>
- <span
- role="button"
- tabIndex={active ? -1 : 0}
- onClick={e => {
- e.preventDefault();
- if (!active) onClick(e);
- }}
- >
- {children}
- </span>
- </li>
- );
+ return openerRef;
}
diff --git a/superset-frontend/src/pages/AlertReportList/index.tsx b/superset-frontend/src/pages/AlertReportList/index.tsx
index 2d24444d24..8d53a410d9 100644
--- a/superset-frontend/src/pages/AlertReportList/index.tsx
+++ b/superset-frontend/src/pages/AlertReportList/index.tsx
@@ -559,6 +559,8 @@ function AlertList({
url: '/alert/list/',
usesRouter: true,
'data-test': 'alert-list',
+ id: 'alert-tab',
+ 'aria-controls': 'alert-list',
},
{
name: 'Reports',
@@ -566,6 +568,8 @@ function AlertList({
url: '/report/list/',
usesRouter: true,
'data-test': 'report-list',
+ id: 'report-tab',
+ 'aria-controls': 'report-list',
},
]}
buttons={subMenuButtons}
@@ -623,24 +627,30 @@ function AlertList({
]
: [];
return (
- <ListView<AlertObject>
- className="alerts-list-view"
- columns={columns}
- count={alertsCount}
- data={alerts}
- emptyState={emptyState}
- fetchData={fetchData}
- filters={filters}
- initialSort={initialSort}
- loading={loading}
- bulkActions={bulkActions}
- bulkSelectEnabled={bulkSelectEnabled}
- disableBulkSelect={toggleBulkSelect}
- refreshData={refreshData}
- addDangerToast={addDangerToast}
- addSuccessToast={addSuccessToast}
- pageSize={PAGE_SIZE}
- />
+ <div
+ id={isReportEnabled ? 'report-list' : 'alert-list'}
+ role="tabpanel"
+ aria-labelledby={isReportEnabled ? 'report-tab' : 'alert-tab'}
+ >
+ <ListView<AlertObject>
+ className="alerts-list-view"
+ columns={columns}
+ count={alertsCount}
+ data={alerts}
+ emptyState={emptyState}
+ fetchData={fetchData}
+ filters={filters}
+ initialSort={initialSort}
+ loading={loading}
+ bulkActions={bulkActions}
+ bulkSelectEnabled={bulkSelectEnabled}
+ disableBulkSelect={toggleBulkSelect}
+ refreshData={refreshData}
+ addDangerToast={addDangerToast}
+ addSuccessToast={addSuccessToast}
+ pageSize={PAGE_SIZE}
+ />
+ </div>
);
}}
</ConfirmStatusChange>