Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Provide initial state for column management #1964

Merged
merged 1 commit into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions client/src/app/hooks/table-controls/column/useColumnState.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,58 @@
import { useLocalStorage } from "@migtools/lib-ui";
import { ColumnSetting } from "../types";
import { useEffect } from "react";

export interface ColumnState<TColumnKey extends string> {
id: TColumnKey;
label: string;
isVisible: boolean;
isIdentity?: boolean;
}

export interface IColumnState<TColumnKey extends string> {
columns: ColumnState<TColumnKey>[];
defaultColumns: ColumnState<TColumnKey>[];
setColumns: (newColumns: ColumnState<TColumnKey>[]) => void;
}

interface IColumnStateArgs<TColumnKey extends string> {
initialColumns: ColumnState<TColumnKey>[];
initialColumns?: Partial<Record<TColumnKey, ColumnSetting>>;
columnsKey: string;
supportedColumns: Record<TColumnKey, string>;
}

export const useColumnState = <TColumnKey extends string>(
args: IColumnStateArgs<TColumnKey>
): IColumnState<TColumnKey> => {
export const useColumnState = <TColumnKey extends string>({
initialColumns,
supportedColumns,
columnsKey,
}: IColumnStateArgs<TColumnKey>): IColumnState<TColumnKey> => {
const defaultColumns = (
Object.entries(supportedColumns) as [TColumnKey, string][]
).map(([id, label]) => ({
id,
label,
isVisible: initialColumns?.[id]?.isVisible ?? true,
isIdentity: initialColumns?.[id]?.isIdentity,
}));
const [columns, setColumns] = useLocalStorage<ColumnState<TColumnKey>[]>({
key: args.columnsKey,
defaultValue: args.initialColumns,
key: columnsKey,
defaultValue: defaultColumns,
});

return { columns, setColumns };
useEffect(() => {
const valid = columns.filter(({ id }) =>
defaultColumns.find((it) => id === it.id)
);
if (valid.length !== defaultColumns.length) {
setColumns(
defaultColumns.map((it) => ({
...it,
isVisible:
valid.find(({ id }) => id === it.id)?.isVisible ?? it.isVisible,
}))
);
}
}, [defaultColumns, columns, setColumns]);

return { columns, setColumns, defaultColumns };
};
9 changes: 9 additions & 0 deletions client/src/app/hooks/table-controls/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ export type IFeaturePersistenceArgs<
*/
persistTo?: PersistTarget;
};

export interface ColumnSetting {
// visibility status, can change in time
isVisible?: boolean;
// column is always visible because it's needed to uniquely identify the row
isIdentity?: boolean;
}

/**
* Table-level persistence-specific args
* - Extra args needed for persisting state at the table level.
Expand Down Expand Up @@ -151,6 +159,7 @@ export type IUseTableControlStateArgs<
/**
* Initial state for the columns feature. If omitted, all columns are enabled by default.
*/
initialColumns?: Partial<Record<TColumnKey, ColumnSetting>>;
} & IFilterStateArgs<TItem, TFilterCategoryKey> &
ISortStateArgs<TSortableColumnKey> &
IPaginationStateArgs & {
Expand Down
10 changes: 2 additions & 8 deletions client/src/app/hooks/table-controls/useTableControlState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,11 @@ export const useTableControlState = <
persistTo: getPersistTo("activeItem"),
});

const { columnNames, tableName } = args;

const initialColumns = Object.entries(columnNames).map(([id, label]) => ({
id: id as TColumnKey,
label: label as string,
isVisible: true,
}));

const { columnNames, tableName, initialColumns } = args;
const columnState = useColumnState<TColumnKey>({
columnsKey: tableName,
initialColumns,
supportedColumns: columnNames,
});
return {
...args,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ export const ApplicationsTable: React.FC = () => {
isLoading: isFetchingApplications,
sortableColumns: ["name", "businessService", "tags", "effort"],
initialSort: { columnKey: "name", direction: "asc" },
initialColumns: {
name: { isIdentity: true },
},
initialFilterValues: deserializedFilterValues,
getSortValues: (app) => ({
name: app.name,
Expand Down Expand Up @@ -804,6 +807,7 @@ export const ApplicationsTable: React.FC = () => {
<ManageColumnsToolbar
columns={columnState.columns}
setColumns={columnState.setColumns}
defaultColumns={columnState.defaultColumns}
/>
</ToolbarGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export interface ManagedColumnsProps<TColumnKey extends string> {
saveLabel?: string;
cancelLabel?: string;
title?: string;
restoreLabel?: string;
defaultColumns: ColumnState<TColumnKey>[];
}

export const ManageColumnsModal = <TColumnKey extends string>({
Expand All @@ -35,6 +37,8 @@ export const ManageColumnsModal = <TColumnKey extends string>({
saveLabel = "Save",
cancelLabel = "Cancel",
title = "Manage Columns",
restoreLabel = "Restore defaults",
defaultColumns,
}: ManagedColumnsProps<TColumnKey>) => {
const [editedColumns, setEditedColumns] =
useState<ColumnState<TColumnKey>[]>(columns);
Expand All @@ -47,6 +51,7 @@ export const ManageColumnsModal = <TColumnKey extends string>({
}))
);
};
const restoreDefaults = () => setEditedColumns([...defaultColumns]);

const onSave = () => {
// If ordering is implemented, update accordingly
Expand All @@ -72,19 +77,20 @@ export const ManageColumnsModal = <TColumnKey extends string>({
<Button key="cancel" variant="secondary" onClick={onClose}>
{cancelLabel}
</Button>,
<Button key="restore" variant="link" onClick={restoreDefaults}>
{restoreLabel}
</Button>,
]}
>
<DataList aria-label={title} id="table-column-management" isCompact>
{editedColumns.map(({ id, label, isVisible }, index) => (
{editedColumns.map(({ id, label, isVisible, isIdentity }, index) => (
<DataListItem key={index}>
<DataListItemRow className="custom-data-list-item-row">
<DataListControl>
<DataListCheck
aria-labelledby={`check-${id}`}
checked={isVisible}
//TODO: Dynamic disable logic based on idProperty definition in useTableControlState.
// Currently, any column whose name is 'name' will not be allowed to be hidden
isDisabled={id === "name"}
checked={isVisible || isIdentity}
isDisabled={isIdentity}
onChange={(e, checked) => onSelect(id, checked)}
/>
</DataListControl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import { ColumnsIcon } from "@patternfly/react-icons";

interface ManageColumnsToolbarProps<TColumnKey extends string> {
columns: ColumnState<TColumnKey>[];
defaultColumns: ColumnState<TColumnKey>[];
setColumns: (newColumns: ColumnState<TColumnKey>[]) => void;
}

export const ManageColumnsToolbar = <TColumnKey extends string>({
columns,
setColumns,
defaultColumns,
}: ManageColumnsToolbarProps<TColumnKey>) => {
const { t } = useTranslation();
const [isOpen, setIsOpen] = useState(false);
Expand Down Expand Up @@ -46,6 +48,7 @@ export const ManageColumnsToolbar = <TColumnKey extends string>({
saveLabel={t("actions.save")}
cancelLabel={t("actions.cancel")}
title={t("dialog.title.manageColumns")}
defaultColumns={defaultColumns}
/>
)}
</>
Expand Down
Loading