mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-26 01:17:00 +02:00
feat: new columns visibility menu (#5604)
New `<ColumnsMenu />` is using it's own list of elements instead of inferring it from table columns definition. This component doesn't need it's internal structure to depend on react-table and it's interface.
This commit is contained in:
parent
685489b9ad
commit
8a5a73ad7d
@ -0,0 +1,41 @@
|
||||
import {
|
||||
Box,
|
||||
Checkbox,
|
||||
Divider,
|
||||
IconButton,
|
||||
MenuItem,
|
||||
styled,
|
||||
} from '@mui/material';
|
||||
|
||||
import { flexRow } from 'themes/themeStyles';
|
||||
|
||||
export const StyledBoxContainer = styled(Box)(() => ({
|
||||
...flexRow,
|
||||
justifyContent: 'center',
|
||||
}));
|
||||
|
||||
export const StyledIconButton = styled(IconButton)(({ theme }) => ({
|
||||
margin: theme.spacing(-1, 0),
|
||||
}));
|
||||
|
||||
export const StyledBoxMenuHeader = styled(Box)(({ theme }) => ({
|
||||
...flexRow,
|
||||
justifyContent: 'space-between',
|
||||
padding: theme.spacing(1, 1, 0, 4),
|
||||
}));
|
||||
|
||||
export const StyledMenuItem = styled(MenuItem)(({ theme }) => ({
|
||||
padding: theme.spacing(0, 2),
|
||||
margin: theme.spacing(0, 2),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
}));
|
||||
|
||||
export const StyledDivider = styled(Divider)(({ theme }) => ({
|
||||
'&.MuiDivider-root.MuiDivider-fullWidth': {
|
||||
margin: theme.spacing(0.75, 0),
|
||||
},
|
||||
}));
|
||||
|
||||
export const StyledCheckbox = styled(Checkbox)(({ theme }) => ({
|
||||
padding: theme.spacing(0.75, 1),
|
||||
}));
|
@ -0,0 +1,134 @@
|
||||
import { useEffect, useState, VFC } from 'react';
|
||||
import {
|
||||
IconButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
MenuList,
|
||||
Popover,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import ColumnIcon from '@mui/icons-material/ViewWeek';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import {
|
||||
StyledBoxContainer,
|
||||
StyledBoxMenuHeader,
|
||||
StyledCheckbox,
|
||||
StyledDivider,
|
||||
StyledIconButton,
|
||||
StyledMenuItem,
|
||||
} from './ExperimentalColumnsMenu.styles';
|
||||
|
||||
interface IColumnsMenuProps {
|
||||
columns: {
|
||||
header?: string;
|
||||
id: string;
|
||||
isStatic?: boolean;
|
||||
isVisible?: boolean;
|
||||
}[];
|
||||
onToggle?: (id: string) => void;
|
||||
}
|
||||
|
||||
export const ExperimentalColumnsMenu: VFC<IColumnsMenuProps> = ({
|
||||
columns,
|
||||
onToggle,
|
||||
}) => {
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
|
||||
const onIconClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const isOpen = Boolean(anchorEl);
|
||||
const id = `columns-menu`;
|
||||
const menuId = `columns-menu-list-${id}`;
|
||||
|
||||
return (
|
||||
<StyledBoxContainer>
|
||||
<Tooltip title='Select columns' arrow describeChild>
|
||||
<StyledIconButton
|
||||
id={id}
|
||||
aria-controls={isOpen ? menuId : undefined}
|
||||
aria-haspopup='true'
|
||||
aria-expanded={isOpen ? 'true' : undefined}
|
||||
onClick={onIconClick}
|
||||
type='button'
|
||||
size='large'
|
||||
data-loading
|
||||
>
|
||||
<ColumnIcon />
|
||||
</StyledIconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Popover
|
||||
id={menuId}
|
||||
open={isOpen}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
disableScrollLock={true}
|
||||
PaperProps={{
|
||||
sx: (theme) => ({
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
paddingBottom: theme.spacing(2),
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<StyledBoxMenuHeader>
|
||||
<Typography variant='body2'>
|
||||
<strong>Columns</strong>
|
||||
</Typography>
|
||||
<IconButton onClick={handleClose}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</StyledBoxMenuHeader>
|
||||
<MenuList>
|
||||
{columns.map((column) =>
|
||||
column.id === 'divider' ? (
|
||||
<StyledDivider />
|
||||
) : (
|
||||
<StyledMenuItem
|
||||
key={column.id}
|
||||
onClick={() => {
|
||||
onToggle?.(column.id);
|
||||
}}
|
||||
disabled={column.isStatic === true}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<StyledCheckbox
|
||||
edge='start'
|
||||
checked={column.isVisible}
|
||||
disableRipple
|
||||
inputProps={{
|
||||
'aria-labelledby': column.id,
|
||||
}}
|
||||
size='medium'
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
id={column.id}
|
||||
primary={
|
||||
<Typography variant='body2'>
|
||||
{column.header}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</StyledMenuItem>
|
||||
),
|
||||
)}
|
||||
</MenuList>
|
||||
</Popover>
|
||||
</StyledBoxContainer>
|
||||
);
|
||||
};
|
@ -50,7 +50,7 @@ import { FavoriteIconHeader } from 'component/common/Table/FavoriteIconHeader/Fa
|
||||
import { FavoriteIconCell } from 'component/common/Table/cells/FavoriteIconCell/FavoriteIconCell';
|
||||
import { ProjectEnvironmentType } from '../../ProjectFeatureToggles/hooks/useEnvironmentsRef';
|
||||
import { ActionsCell } from '../../ProjectFeatureToggles/ActionsCell/ActionsCell';
|
||||
import { ColumnsMenu } from '../../ProjectFeatureToggles/ColumnsMenu/ColumnsMenu';
|
||||
import { ExperimentalColumnsMenu as ColumnsMenu } from './ExperimentalColumnsMenu/ExperimentalColumnsMenu';
|
||||
import { useStyles } from '../../ProjectFeatureToggles/ProjectFeatureToggles.styles';
|
||||
import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi/useFavoriteFeaturesApi';
|
||||
import { FeatureTagCell } from 'component/common/Table/cells/FeatureTagCell/FeatureTagCell';
|
||||
@ -333,6 +333,31 @@ export const ExperimentalProjectFeatureToggles = ({
|
||||
environmentsToExport={environments.map(
|
||||
({ environment }) => environment,
|
||||
)}
|
||||
actions={
|
||||
<ColumnsMenu
|
||||
columns={[
|
||||
{
|
||||
header: 'Last seen',
|
||||
id: 'lastSeenAt',
|
||||
},
|
||||
{
|
||||
header: 'Type',
|
||||
id: 'type',
|
||||
},
|
||||
{
|
||||
header: 'Name',
|
||||
id: 'name',
|
||||
},
|
||||
{
|
||||
header: 'Created',
|
||||
id: 'createdAt',
|
||||
},
|
||||
{
|
||||
id: 'divider',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { VFC, useState } from 'react';
|
||||
import { ReactNode, VFC, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
@ -28,6 +28,7 @@ interface IProjectFeatureTogglesHeaderProps {
|
||||
onChangeSearchQuery?: (query: string) => void;
|
||||
dataToExport?: Pick<FeatureSchema, 'name'>[];
|
||||
environmentsToExport?: string[];
|
||||
actions?: ReactNode;
|
||||
}
|
||||
|
||||
const StyledResponsiveButton = styled(ResponsiveButton)(() => ({
|
||||
@ -43,6 +44,7 @@ export const ProjectFeatureTogglesHeader: VFC<
|
||||
onChangeSearchQuery,
|
||||
dataToExport,
|
||||
environmentsToExport,
|
||||
actions,
|
||||
}) => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const headerLoadingRef = useLoading(isLoading || false);
|
||||
@ -91,16 +93,7 @@ export const ProjectFeatureTogglesHeader: VFC<
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{/* FIXME: columns menu */}
|
||||
{/* <ColumnsMenu
|
||||
allColumns={allColumns}
|
||||
staticColumns={staticColumns}
|
||||
dividerAfter={['createdAt']}
|
||||
dividerBefore={['Actions']}
|
||||
isCustomized={isCustomColumns}
|
||||
setHiddenColumns={setHiddenColumns}
|
||||
onCustomize={() => setIsCustomColumns(true)}
|
||||
/> */}
|
||||
{actions}
|
||||
<PageHeader.Divider sx={{ marginLeft: 0 }} />
|
||||
<ConditionallyRender
|
||||
condition={featuresExportImportFlag}
|
||||
|
Loading…
Reference in New Issue
Block a user