1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-24 01:18:01 +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>
<StyledProjectToggles>
<ExperimentalProjectFeatureToggles
style={{ width: '100%', margin: 0 }}
environments={environments}
storageKey='project-features'
/>

View File

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

View File

@ -91,25 +91,26 @@ import { type FeatureSearchResponseSchema } from 'openapi';
import { FeatureNameCell } from 'component/common/Table/cells/FeatureNameCell/FeatureNameCell';
import { FeatureToggleCell } from './FeatureToggleCell/FeatureToggleCell';
import { ProjectOverviewFilters } from './ProjectOverviewFilters';
import { useDefaultColumnVisibility } from './hooks/useDefaultColumnVisibility';
interface IExperimentalProjectFeatureTogglesProps {
environments: IProject['environments'];
style?: CSSProperties;
refreshInterval?: number;
storageKey?: string;
}
const staticColumns = ['Select', 'Actions', 'name', 'favorite'];
const formatEnvironmentColumnId = (environment: string) =>
`environment:${environment}`;
const columnHelper = createColumnHelper<FeatureSearchResponseSchema>();
export const ExperimentalProjectFeatureToggles = ({
environments,
style,
refreshInterval = 15 * 1000,
storageKey = 'project-feature-toggles',
}: IExperimentalProjectFeatureTogglesProps) => {
const projectId = useRequiredPathParam('projectId');
const stateConfig = {
offset: withDefault(NumberParam, 0),
limit: withDefault(NumberParam, DEFAULT_PAGE_LIMIT),
@ -158,7 +159,7 @@ export const ExperimentalProjectFeatureToggles = ({
const columns = useMemo(
() => [
columnHelper.display({
id: 'Select',
id: 'select',
header: ({ table }) => (
<MemoizedRowSelectCell
noPadding
@ -177,6 +178,7 @@ export const ExperimentalProjectFeatureToggles = ({
),
}),
columnHelper.accessor('favorite', {
id: 'favorite',
header: () => (
<FavoriteIconHeader
isActive={tableState.favoritesFirst}
@ -196,10 +198,10 @@ export const ExperimentalProjectFeatureToggles = ({
enableSorting: false,
meta: {
align: 'center',
// hideInMenu: true,
},
}),
columnHelper.accessor('lastSeenAt', {
id: 'lastSeenAt',
header: 'Last seen',
cell: ({ row: { original } }) => (
<MemoizedFeatureEnvironmentSeenCell
@ -213,6 +215,7 @@ export const ExperimentalProjectFeatureToggles = ({
},
}),
columnHelper.accessor('type', {
id: 'type',
header: 'Type',
cell: FeatureTypeCell,
meta: {
@ -220,6 +223,7 @@ export const ExperimentalProjectFeatureToggles = ({
},
}),
columnHelper.accessor('name', {
id: 'name',
header: 'Name',
cell: FeatureNameCell,
meta: {
@ -227,6 +231,7 @@ export const ExperimentalProjectFeatureToggles = ({
},
}),
columnHelper.accessor('createdAt', {
id: 'createdAt',
header: 'Created',
cell: DateCell,
}),
@ -252,8 +257,8 @@ export const ExperimentalProjectFeatureToggles = ({
) || false,
}),
{
id: `environment:${name}`,
header: loading ? '' : name,
id: formatEnvironmentColumnId(name),
header: name,
meta: {
align: 'center',
},
@ -287,7 +292,7 @@ export const ExperimentalProjectFeatureToggles = ({
},
),
],
[projectId, environments, loading, tableState.favoritesFirst, refetch],
[projectId, environments, tableState.favoritesFirst, refetch],
);
const placeholderData = useMemo(
@ -319,15 +324,41 @@ export const ExperimentalProjectFeatureToggles = ({
}
return features;
}, [loading, features]);
const allColumnIds = useMemo(
() => columns.map((column) => column.id).filter(Boolean) as string[],
[columns],
);
const defaultColumnVisibility = useDefaultColumnVisibility(allColumnIds);
const table = useReactTable(
withTableState(tableState, setTableState, {
columns,
data,
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 (
<>
<PageContent
@ -351,23 +382,41 @@ export const ExperimentalProjectFeatureToggles = ({
{
header: 'Last seen',
id: 'lastSeenAt',
isVisible: columnVisibility.lastSeenAt,
},
{
header: 'Type',
id: 'type',
isVisible: columnVisibility.type,
},
{
header: 'Name',
id: 'name',
isVisible: columnVisibility.name,
isStatic: true,
},
{
header: 'Created',
id: 'createdAt',
isVisible: columnVisibility.createdAt,
},
{
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,
} from '@tanstack/react-table';
type TableStateColumnsType = (string | null)[] | null;
type TableStateColumns = (string | null)[] | null | undefined;
const createOnSortingChange =
(
@ -79,10 +79,10 @@ const createOnPaginationChange =
const createOnColumnVisibilityChange =
(
tableState: {
columns?: TableStateColumnsType;
columns?: TableStateColumns;
},
setTableState: (newState: {
columns?: TableStateColumnsType;
columns?: TableStateColumns;
}) => void,
): OnChangeFn<VisibilityState> =>
(newVisibility) => {
@ -132,7 +132,7 @@ const createPaginationState = (tableState: {
});
const createColumnVisibilityState = (tableState: {
columns?: TableStateColumnsType;
columns?: TableStateColumns;
}) =>
tableState.columns
? {
@ -152,35 +152,50 @@ export const withTableState = <T extends Object>(
sortOrder: string;
limit: number;
offset: number;
columns?: TableStateColumnsType;
columns?: TableStateColumns;
},
setTableState: (newState: {
sortBy?: string;
sortOrder?: string;
limit?: number;
offset?: number;
columns?: TableStateColumnsType;
columns?: TableStateColumns;
}) => void,
options: Omit<TableOptions<T>, 'getCoreRowModel'>,
) => ({
getCoreRowModel: getCoreRowModel(),
enableSorting: true,
enableMultiSort: false,
manualPagination: true,
manualSorting: true,
enableSortingRemoval: false,
enableHiding: true,
onPaginationChange: createOnPaginationChange(tableState, setTableState),
onSortingChange: createOnSortingChange(tableState, setTableState),
onColumnVisibilityChange: createOnColumnVisibilityChange(
tableState,
setTableState,
),
...options,
state: {
...createSortingState(tableState),
...createPaginationState(tableState),
...createColumnVisibilityState(tableState),
...(options.state || {}),
},
});
) => {
const hideAllColumns = Object.fromEntries(
Object.keys(options.state?.columnVisibility || {}).map((column) => [
column,
false,
]),
);
const columnVisibility = tableState.columns
? {
...hideAllColumns,
...createColumnVisibilityState(tableState).columnVisibility,
}
: options.state?.columnVisibility;
return {
getCoreRowModel: getCoreRowModel(),
enableSorting: true,
enableMultiSort: false,
manualPagination: true,
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,
},
};
};