1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-14 01:16:17 +02:00

feat: project features - new columns visibility handling (#5605)

In `ExperimentalProjectTable.tsx`, changes have been made to the columns
configuration in order to handle column visibiilty. This includes adding
id property to every column. Logic responsible for dynamically adjusts
column visibility based on screen size was moved to new hook,
`useDefaultColumnVisibility`
This commit is contained in:
Tymoteusz Czech 2023-12-13 10:08:16 +01:00 committed by GitHub
parent c0c1dba9b0
commit 17b747ea8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 167 additions and 39 deletions

View File

@ -45,7 +45,6 @@ const PaginatedProjectOverview = () => {
<StyledContentContainer> <StyledContentContainer>
<StyledProjectToggles> <StyledProjectToggles>
<ExperimentalProjectFeatureToggles <ExperimentalProjectFeatureToggles
style={{ width: '100%', margin: 0 }}
environments={environments} environments={environments}
storageKey='project-features' storageKey='project-features'
/> />

View File

@ -96,14 +96,17 @@ export const ExperimentalColumnsMenu: VFC<IColumnsMenuProps> = ({
<MenuList> <MenuList>
{columns.map((column) => {columns.map((column) =>
column.id === 'divider' ? ( column.id === 'divider' ? (
<StyledDivider /> <StyledDivider key='divider' />
) : ( ) : (
<StyledMenuItem <StyledMenuItem
key={column.id} key={column.id}
onClick={() => { onClick={() => {
onToggle?.(column.id); onToggle?.(column.id);
}} }}
disabled={column.isStatic === true} disabled={
column.isStatic === true &&
column.isVisible === true
}
> >
<ListItemIcon> <ListItemIcon>
<StyledCheckbox <StyledCheckbox

View File

@ -91,25 +91,26 @@ import { type FeatureSearchResponseSchema } from 'openapi';
import { FeatureNameCell } from 'component/common/Table/cells/FeatureNameCell/FeatureNameCell'; import { FeatureNameCell } from 'component/common/Table/cells/FeatureNameCell/FeatureNameCell';
import { FeatureToggleCell } from './FeatureToggleCell/FeatureToggleCell'; import { FeatureToggleCell } from './FeatureToggleCell/FeatureToggleCell';
import { ProjectOverviewFilters } from './ProjectOverviewFilters'; import { ProjectOverviewFilters } from './ProjectOverviewFilters';
import { useDefaultColumnVisibility } from './hooks/useDefaultColumnVisibility';
interface IExperimentalProjectFeatureTogglesProps { interface IExperimentalProjectFeatureTogglesProps {
environments: IProject['environments']; environments: IProject['environments'];
style?: CSSProperties;
refreshInterval?: number; refreshInterval?: number;
storageKey?: string; storageKey?: string;
} }
const staticColumns = ['Select', 'Actions', 'name', 'favorite']; const formatEnvironmentColumnId = (environment: string) =>
`environment:${environment}`;
const columnHelper = createColumnHelper<FeatureSearchResponseSchema>(); const columnHelper = createColumnHelper<FeatureSearchResponseSchema>();
export const ExperimentalProjectFeatureToggles = ({ export const ExperimentalProjectFeatureToggles = ({
environments, environments,
style,
refreshInterval = 15 * 1000, refreshInterval = 15 * 1000,
storageKey = 'project-feature-toggles', storageKey = 'project-feature-toggles',
}: IExperimentalProjectFeatureTogglesProps) => { }: IExperimentalProjectFeatureTogglesProps) => {
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const stateConfig = { const stateConfig = {
offset: withDefault(NumberParam, 0), offset: withDefault(NumberParam, 0),
limit: withDefault(NumberParam, DEFAULT_PAGE_LIMIT), limit: withDefault(NumberParam, DEFAULT_PAGE_LIMIT),
@ -158,7 +159,7 @@ export const ExperimentalProjectFeatureToggles = ({
const columns = useMemo( const columns = useMemo(
() => [ () => [
columnHelper.display({ columnHelper.display({
id: 'Select', id: 'select',
header: ({ table }) => ( header: ({ table }) => (
<MemoizedRowSelectCell <MemoizedRowSelectCell
noPadding noPadding
@ -177,6 +178,7 @@ export const ExperimentalProjectFeatureToggles = ({
), ),
}), }),
columnHelper.accessor('favorite', { columnHelper.accessor('favorite', {
id: 'favorite',
header: () => ( header: () => (
<FavoriteIconHeader <FavoriteIconHeader
isActive={tableState.favoritesFirst} isActive={tableState.favoritesFirst}
@ -196,10 +198,10 @@ export const ExperimentalProjectFeatureToggles = ({
enableSorting: false, enableSorting: false,
meta: { meta: {
align: 'center', align: 'center',
// hideInMenu: true,
}, },
}), }),
columnHelper.accessor('lastSeenAt', { columnHelper.accessor('lastSeenAt', {
id: 'lastSeenAt',
header: 'Last seen', header: 'Last seen',
cell: ({ row: { original } }) => ( cell: ({ row: { original } }) => (
<MemoizedFeatureEnvironmentSeenCell <MemoizedFeatureEnvironmentSeenCell
@ -213,6 +215,7 @@ export const ExperimentalProjectFeatureToggles = ({
}, },
}), }),
columnHelper.accessor('type', { columnHelper.accessor('type', {
id: 'type',
header: 'Type', header: 'Type',
cell: FeatureTypeCell, cell: FeatureTypeCell,
meta: { meta: {
@ -220,6 +223,7 @@ export const ExperimentalProjectFeatureToggles = ({
}, },
}), }),
columnHelper.accessor('name', { columnHelper.accessor('name', {
id: 'name',
header: 'Name', header: 'Name',
cell: FeatureNameCell, cell: FeatureNameCell,
meta: { meta: {
@ -227,6 +231,7 @@ export const ExperimentalProjectFeatureToggles = ({
}, },
}), }),
columnHelper.accessor('createdAt', { columnHelper.accessor('createdAt', {
id: 'createdAt',
header: 'Created', header: 'Created',
cell: DateCell, cell: DateCell,
}), }),
@ -252,8 +257,8 @@ export const ExperimentalProjectFeatureToggles = ({
) || false, ) || false,
}), }),
{ {
id: `environment:${name}`, id: formatEnvironmentColumnId(name),
header: loading ? '' : name, header: name,
meta: { meta: {
align: 'center', align: 'center',
}, },
@ -287,7 +292,7 @@ export const ExperimentalProjectFeatureToggles = ({
}, },
), ),
], ],
[projectId, environments, loading, tableState.favoritesFirst, refetch], [projectId, environments, tableState.favoritesFirst, refetch],
); );
const placeholderData = useMemo( const placeholderData = useMemo(
@ -319,15 +324,41 @@ export const ExperimentalProjectFeatureToggles = ({
} }
return features; return features;
}, [loading, features]); }, [loading, features]);
const allColumnIds = useMemo(
() => columns.map((column) => column.id).filter(Boolean) as string[],
[columns],
);
const defaultColumnVisibility = useDefaultColumnVisibility(allColumnIds);
const table = useReactTable( const table = useReactTable(
withTableState(tableState, setTableState, { withTableState(tableState, setTableState, {
columns, columns,
data, data,
enableRowSelection: true, enableRowSelection: true,
state: {
columnVisibility: defaultColumnVisibility,
},
}), }),
); );
const { columnVisibility } = table.getState();
const onToggleColumnVisibility = useCallback(
(columnId) => {
const isVisible = columnVisibility[columnId];
const newColumnVisibility: Record<string, boolean> = {
...columnVisibility,
[columnId]: !isVisible,
};
setTableState({
columns: Object.keys(newColumnVisibility).filter(
(columnId) => newColumnVisibility[columnId],
),
});
},
[columnVisibility, setTableState],
);
return ( return (
<> <>
<PageContent <PageContent
@ -351,23 +382,41 @@ export const ExperimentalProjectFeatureToggles = ({
{ {
header: 'Last seen', header: 'Last seen',
id: 'lastSeenAt', id: 'lastSeenAt',
isVisible: columnVisibility.lastSeenAt,
}, },
{ {
header: 'Type', header: 'Type',
id: 'type', id: 'type',
isVisible: columnVisibility.type,
}, },
{ {
header: 'Name', header: 'Name',
id: 'name', id: 'name',
isVisible: columnVisibility.name,
isStatic: true,
}, },
{ {
header: 'Created', header: 'Created',
id: 'createdAt', id: 'createdAt',
isVisible: columnVisibility.createdAt,
}, },
{ {
id: 'divider', id: 'divider',
}, },
...environments.map(({ environment }) => ({
header: environment,
id: formatEnvironmentColumnId(
environment,
),
isVisible:
columnVisibility[
formatEnvironmentColumnId(
environment,
)
],
})),
]} ]}
onToggle={onToggleColumnVisibility}
/> />
} }
/> />

View File

@ -0,0 +1,62 @@
import { useCallback } from 'react';
import { useMediaQuery, useTheme } from '@mui/material';
import { type VisibilityState } from '@tanstack/react-table';
const staticColumns = ['select', 'actions', 'name', 'favorite'];
const formatAsColumnVisibility = (
allColumns: string[],
visibleColumns: string[],
): VisibilityState =>
allColumns.reduce(
(acc, columnId) => ({
...acc,
[columnId]: visibleColumns.includes(columnId),
}),
{},
);
export const useDefaultColumnVisibility = (allColumnIds: string[]) => {
const theme = useTheme();
const isTinyScreen = useMediaQuery(theme.breakpoints.down('sm'));
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg'));
const showEnvironments = useCallback(
(environmentsToShow: number = 0) =>
allColumnIds
.filter((id) => id.startsWith('environment:') !== false)
.slice(0, environmentsToShow),
[allColumnIds],
);
if (isTinyScreen) {
return formatAsColumnVisibility(allColumnIds, [
...staticColumns,
'createdAt',
]);
}
if (isSmallScreen) {
return formatAsColumnVisibility(allColumnIds, [
...staticColumns,
'createdAt',
...showEnvironments(1),
]);
}
if (isMediumScreen) {
return formatAsColumnVisibility(allColumnIds, [
...staticColumns,
'createdAt',
'type',
...showEnvironments(1),
]);
}
return formatAsColumnVisibility(allColumnIds, [
...staticColumns,
'lastSeenAt',
'createdAt',
'type',
...showEnvironments(3),
]);
};

View File

@ -7,7 +7,7 @@ import {
getCoreRowModel, getCoreRowModel,
} from '@tanstack/react-table'; } from '@tanstack/react-table';
type TableStateColumnsType = (string | null)[] | null; type TableStateColumns = (string | null)[] | null | undefined;
const createOnSortingChange = const createOnSortingChange =
( (
@ -79,10 +79,10 @@ const createOnPaginationChange =
const createOnColumnVisibilityChange = const createOnColumnVisibilityChange =
( (
tableState: { tableState: {
columns?: TableStateColumnsType; columns?: TableStateColumns;
}, },
setTableState: (newState: { setTableState: (newState: {
columns?: TableStateColumnsType; columns?: TableStateColumns;
}) => void, }) => void,
): OnChangeFn<VisibilityState> => ): OnChangeFn<VisibilityState> =>
(newVisibility) => { (newVisibility) => {
@ -132,7 +132,7 @@ const createPaginationState = (tableState: {
}); });
const createColumnVisibilityState = (tableState: { const createColumnVisibilityState = (tableState: {
columns?: TableStateColumnsType; columns?: TableStateColumns;
}) => }) =>
tableState.columns tableState.columns
? { ? {
@ -152,35 +152,50 @@ export const withTableState = <T extends Object>(
sortOrder: string; sortOrder: string;
limit: number; limit: number;
offset: number; offset: number;
columns?: TableStateColumnsType; columns?: TableStateColumns;
}, },
setTableState: (newState: { setTableState: (newState: {
sortBy?: string; sortBy?: string;
sortOrder?: string; sortOrder?: string;
limit?: number; limit?: number;
offset?: number; offset?: number;
columns?: TableStateColumnsType; columns?: TableStateColumns;
}) => void, }) => void,
options: Omit<TableOptions<T>, 'getCoreRowModel'>, options: Omit<TableOptions<T>, 'getCoreRowModel'>,
) => ({ ) => {
getCoreRowModel: getCoreRowModel(), const hideAllColumns = Object.fromEntries(
enableSorting: true, Object.keys(options.state?.columnVisibility || {}).map((column) => [
enableMultiSort: false, column,
manualPagination: true, false,
manualSorting: true, ]),
enableSortingRemoval: false, );
enableHiding: true, const columnVisibility = tableState.columns
onPaginationChange: createOnPaginationChange(tableState, setTableState), ? {
onSortingChange: createOnSortingChange(tableState, setTableState), ...hideAllColumns,
onColumnVisibilityChange: createOnColumnVisibilityChange( ...createColumnVisibilityState(tableState).columnVisibility,
tableState, }
setTableState, : options.state?.columnVisibility;
),
...options, return {
state: { getCoreRowModel: getCoreRowModel(),
...createSortingState(tableState), enableSorting: true,
...createPaginationState(tableState), enableMultiSort: false,
...createColumnVisibilityState(tableState), manualPagination: true,
...(options.state || {}), manualSorting: true,
}, enableSortingRemoval: false,
}); enableHiding: true,
onPaginationChange: createOnPaginationChange(tableState, setTableState),
onSortingChange: createOnSortingChange(tableState, setTableState),
onColumnVisibilityChange: createOnColumnVisibilityChange(
tableState,
setTableState,
),
...options,
state: {
...createSortingState(tableState),
...createPaginationState(tableState),
...(options.state || {}),
columnVisibility,
},
};
};