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/09 12:39:04 UTC
(superset) 01/01: chore(Accessibility): Improve keyboard navigation and screen access
This is an automated email from the ASF dual-hosted git repository.
diegopucci pushed a commit to branch geido/chore/accessibility-phase-1
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 0f094e33e2020b6d32fffdf31575b9b7966299c9
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 dce962a6ce..ab6e7fed15 100644
--- a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
+++ b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
@@ -426,6 +426,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}
@@ -476,6 +477,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>