Skip to content

Commit 91d8852

Browse files
authored
Merge pull request #10869 from marmelab/fix-columns-button-keyboard-usage
Fix `<ColumnsButton>` cannot be used with keyboard
2 parents 42d7465 + 72e6f6d commit 91d8852

File tree

4 files changed

+58
-39
lines changed

4 files changed

+58
-39
lines changed

packages/ra-ui-materialui/src/list/datatable/ColumnsButton.spec.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('ColumnsButton', () => {
2929
screen
3030
.getByRole('menu')
3131
.querySelectorAll('li:not(.columns-selector-actions)')
32-
).toHaveLength(7);
32+
).toHaveLength(8); // 7 columns + the filter input li
3333
// Typing a filter
3434
fireEvent.change(
3535
screen.getByPlaceholderText('ra.action.search_columns'),
@@ -43,7 +43,7 @@ describe('ColumnsButton', () => {
4343
screen
4444
.getByRole('menu')
4545
.querySelectorAll('li:not(.columns-selector-actions)')
46-
).toHaveLength(1);
46+
).toHaveLength(2); // only the column with 'DiA' in its label should remain + the filter input li
4747
});
4848
screen.getByLabelText('Téstïng diàcritics');
4949
// Clear the filter
@@ -53,7 +53,7 @@ describe('ColumnsButton', () => {
5353
screen
5454
.getByRole('menu')
5555
.querySelectorAll('li:not(.columns-selector-actions)')
56-
).toHaveLength(7);
56+
).toHaveLength(8);
5757
});
5858
});
5959
});

packages/ra-ui-materialui/src/list/datatable/ColumnsButton.tsx

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import * as React from 'react';
22
import { useTranslate, useResourceContext } from 'ra-core';
33
import {
4-
Box,
54
Button,
65
type ButtonProps,
7-
Menu,
86
useMediaQuery,
97
Theme,
108
Tooltip,
119
IconButton,
10+
Popover,
11+
PopoverOrigin,
12+
Box,
1213
} from '@mui/material';
14+
import { useRtl } from '@mui/system/RtlProvider';
1315
import ViewWeekIcon from '@mui/icons-material/ViewWeek';
1416
import {
1517
type ComponentsOverrides,
@@ -49,7 +51,7 @@ export const ColumnsButton = (inProps: ColumnsButtonProps) => {
4951
const storeKey = props.storeKey || `${resource}.datatable`;
5052

5153
const [anchorEl, setAnchorEl] = React.useState(null);
52-
54+
const isRtl = useRtl();
5355
const translate = useTranslate();
5456
const isXSmall = useMediaQuery((theme: Theme) =>
5557
theme.breakpoints.down('sm')
@@ -89,23 +91,37 @@ export const ColumnsButton = (inProps: ColumnsButtonProps) => {
8991
{title}
9092
</Button>
9193
)}
92-
<Menu
94+
<Popover
9395
open={Boolean(anchorEl)}
9496
keepMounted
9597
anchorEl={anchorEl}
9698
onClose={handleClose}
99+
anchorOrigin={{
100+
vertical: 'bottom',
101+
horizontal: isRtl ? 'right' : 'left',
102+
}}
103+
transformOrigin={isRtl ? RTL_ORIGIN : LTR_ORIGIN}
97104
>
98105
{/* ColumnsSelector will be rendered here via Portal */}
99106
<Box
100-
component="ul"
101-
sx={{ px: 1, my: 0, minWidth: 200 }}
102107
id={`${storeKey}-columnsSelector`}
108+
sx={{ px: 1, my: 0, minWidth: 200 }}
103109
/>
104-
</Menu>
110+
</Popover>
105111
</Root>
106112
);
107113
};
108114

115+
const RTL_ORIGIN: PopoverOrigin = {
116+
vertical: 'top',
117+
horizontal: 'right',
118+
};
119+
120+
const LTR_ORIGIN: PopoverOrigin = {
121+
vertical: 'top',
122+
horizontal: 'left',
123+
};
124+
109125
const PREFIX = 'RaColumnsButton';
110126
const Root = styled('span', {
111127
name: PREFIX,

packages/ra-ui-materialui/src/list/datatable/ColumnsSelector.tsx

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
useTranslate,
99
DataTableColumnFilterContext,
1010
} from 'ra-core';
11-
import { Box, InputAdornment } from '@mui/material';
11+
import { Box, InputAdornment, MenuList } from '@mui/material';
1212
import SearchIcon from '@mui/icons-material/Search';
1313

1414
import { Button } from '../../button';
@@ -67,34 +67,36 @@ export const ColumnsSelector = ({ children }: ColumnsSelectorProps) => {
6767
const shouldDisplaySearchInput = childrenArray.length > 5;
6868

6969
return createPortal(
70-
<>
70+
<MenuList>
7171
{shouldDisplaySearchInput ? (
72-
<ResettableTextField
73-
hiddenLabel
74-
label=""
75-
value={columnFilter}
76-
onChange={e => {
77-
if (typeof e === 'string') {
78-
setColumnFilter(e);
79-
return;
80-
}
81-
setColumnFilter(e.target.value);
82-
}}
83-
placeholder={translate('ra.action.search_columns', {
84-
_: 'Search columns',
85-
})}
86-
InputProps={{
87-
endAdornment: (
88-
<InputAdornment position="end">
89-
<SearchIcon color="disabled" />
90-
</InputAdornment>
91-
),
92-
}}
93-
resettable
94-
autoFocus
95-
size="small"
96-
sx={{ mb: 1 }}
97-
/>
72+
<Box component="li" tabIndex={-1}>
73+
<ResettableTextField
74+
hiddenLabel
75+
label=""
76+
value={columnFilter}
77+
onChange={e => {
78+
if (typeof e === 'string') {
79+
setColumnFilter(e);
80+
return;
81+
}
82+
setColumnFilter(e.target.value);
83+
}}
84+
placeholder={translate('ra.action.search_columns', {
85+
_: 'Search columns',
86+
})}
87+
InputProps={{
88+
endAdornment: (
89+
<InputAdornment position="end">
90+
<SearchIcon color="disabled" />
91+
</InputAdornment>
92+
),
93+
}}
94+
resettable
95+
autoFocus
96+
size="small"
97+
sx={{ mb: 1 }}
98+
/>
99+
</Box>
98100
) : null}
99101
{paddedColumnRanks.map((position, index) => (
100102
<DataTableColumnRankContext.Provider
@@ -123,7 +125,7 @@ export const ColumnsSelector = ({ children }: ColumnsSelectorProps) => {
123125
Reset
124126
</Button>
125127
</Box>
126-
</>,
128+
</MenuList>,
127129
container
128130
);
129131
};

packages/ra-ui-materialui/src/preferences/FieldToggle.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export const FieldToggle = (props: FieldToggleProps) => {
102102
return (
103103
<Root
104104
key={source}
105+
role="option"
105106
draggable={onMove ? 'true' : undefined}
106107
onDrag={onMove ? handleDrag : undefined}
107108
onDragStart={onMove ? handleDragStart : undefined}

0 commit comments

Comments
 (0)