mirror of
https://github.com/apache/superset.git
synced 2026-06-13 11:39:16 +00:00
Compare commits
2 Commits
docs/testi
...
geido/fix/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0bfc27e11 | ||
|
|
deeacb0dfb |
@@ -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' : '' }}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
32
superset-frontend/src/hooks/useOpenerRef.ts
Normal file
32
superset-frontend/src/hooks/useOpenerRef.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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 { useEffect, useRef } from 'react';
|
||||
|
||||
export function useOpenerRef(active: boolean) {
|
||||
const openerRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
openerRef.current = document.activeElement as HTMLElement;
|
||||
}
|
||||
}, [active]);
|
||||
|
||||
return openerRef;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user