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

✨ integrate column management feature into apps table #1767

Merged
merged 4 commits into from
Mar 27, 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
2 changes: 2 additions & 0 deletions client/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
"import": "Import {{what}}",
"leavePage": "Leave page",
"leaveAssessment": "Leave assessment",
"manageColumns": "Manage columns",
"new": "New {{what}}",
"newArchetype": "Create new archetype",
"newAssessment": "New assessment",
Expand Down Expand Up @@ -213,6 +214,7 @@
"selectedBecauseArchetypeTags": "Selected because the archetype's tags include {{tags}}",
"selectedBecauseAppOrArchetypeTags": "Selected because the application or archetype's tags include {{tags}}",
"selectOwnerFromStakeholdersList": "Select owner from list of stakeholders",
"manageColumnsDescription": "Select the columns you want to display in the table view.",
"suggestedAdoptionPlanHelpText": "The suggested approach to migration based on effort, priority, and dependencies.",
"taskInProgressForTags": "A new analysis is in-progress. Tags may be updated upon completion.",
"toTagApplication": "Either no tags exist yet or you may not have permission to view any. If you have permission, try creating a new custom tag.",
Expand Down
1 change: 1 addition & 0 deletions client/src/app/components/answer-table/answer-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const AnswerTable: React.FC<IAnswerTableProps> = ({
const { t } = useTranslation();

const tableControls = useLocalTableControls({
tableName: "answer-table",
idProperty: "text",
items: hideAnswerKey
? answers.filter((answer) => answer.selected)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const QuestionsTable: React.FC<{
hideAnswerKey,
}) => {
const tableControls = useLocalTableControls({
tableName: "questions-table",
idProperty: "text",
items: questions || [],
columnNames: {
Expand Down
1 change: 1 addition & 0 deletions client/src/app/hooks/table-controls/DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const things: Thing[] = [
];

const tableControls = useLocalTableControls({
tableName: 'page-table-name' // A unique name of the table
idProperty: "id", // The name of a unique string or number property on the data items.
items: things, // The generic type `TItem` is inferred from the items passed here.
columnNames: {
Expand Down
28 changes: 28 additions & 0 deletions client/src/app/hooks/table-controls/column/useColumnState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useLocalStorage } from "@migtools/lib-ui";

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

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

interface IColumnStateArgs<TColumnKey extends string> {
initialColumns: ColumnState<TColumnKey>[];
columnsKey: string;
}

export const useColumnState = <TColumnKey extends string>(
args: IColumnStateArgs<TColumnKey>
): IColumnState<TColumnKey> => {
const [columns, setColumns] = useLocalStorage<ColumnState<TColumnKey>[]>({
key: args.columnsKey,
defaultValue: args.initialColumns,
});

return { columns, setColumns };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this is where you'd have to add column visibility persistence past the component state level that is used now. I predict there will be a QE ticket filed in less than 10 minutes of app testing say that the column visibility doesn't persist page page refreshes.

A chat before testing would help. Maybe even loop docs in to level set expectations as to how long the column management state lives.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added local storage support for the column names.

};
34 changes: 33 additions & 1 deletion client/src/app/hooks/table-controls/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
import { IFilterToolbarProps } from "@app/components/FilterToolbar";
import { IToolbarBulkSelectorProps } from "@app/components/ToolbarBulkSelector";
import { IExpansionPropHelpersExternalArgs } from "./expansion/useExpansionPropHelpers";
import { IColumnState } from "./column/useColumnState";

// Generic type params used here:
// TItem - The actual API objects represented by rows in the table. Can be any object.
Expand All @@ -56,7 +57,8 @@ export type TableFeature =
| "pagination"
| "selection"
| "expansion"
| "activeItem";
| "activeItem"
| "columns";

/**
* Identifier for where to persist state for a single table feature or for all table features.
Expand Down Expand Up @@ -136,12 +138,19 @@ export type IUseTableControlStateArgs<
TFilterCategoryKey extends string = string,
TPersistenceKeyPrefix extends string = string,
> = {
/**
* Unique table identifier. Used for state persistence and to distinguish between stored state between multiple tables.
*/
tableName: string;
/**
* An ordered mapping of unique keys to human-readable column name strings.
* - Keys of this object are used as unique identifiers for columns (`columnKey`).
* - Values of this object are rendered in the column headers by default (can be overridden by passing children to <Th>) and used as `dataLabel` for cells in the column.
*/
columnNames: Record<TColumnKey, string>;
/**
* Initial state for the columns feature. If omitted, all columns are enabled by default.
*/
} & IFilterStateArgs<TItem, TFilterCategoryKey> &
ISortStateArgs<TSortableColumnKey> &
IPaginationStateArgs & {
Expand Down Expand Up @@ -193,6 +202,10 @@ export type ITableControlState<
* State for the active item feature. Returned by useActiveItemState.
*/
activeItemState: IActiveItemState;
/**
* State for the columns feature. Returned by useColumnState.
*/
columnState: IColumnState<TColumnKey>;
};

/**
Expand Down Expand Up @@ -288,6 +301,10 @@ export type IUseTableControlPropsArgs<
* @todo this won't be included here when useSelectionState gets moved from lib-ui. It is separated from the other state temporarily and used only at render time.
*/
selectionState: ReturnType<typeof useSelectionState<TItem>>;
/**
* The state for the columns feature. Returned by useColumnState.
*/
columnState: IColumnState<TColumnKey>;
};

/**
Expand Down Expand Up @@ -325,9 +342,18 @@ export type ITableControls<
* Values derived at render time from the expansion feature state. Includes helper functions for convenience.
*/
expansionDerivedState: IExpansionDerivedState<TItem, TColumnKey>;
/**
* Values derived at render time from the column feature state. Includes helper functions for convenience.
*
*
*
*
*/
columnState: IColumnState<TColumnKey>;
/**
* Values derived at render time from the active-item feature state. Includes helper functions for convenience.
*/

activeItemDerivedState: IActiveItemDerivedState<TItem>;
/**
* Prop helpers: where it all comes together.
Expand Down Expand Up @@ -406,6 +432,12 @@ export type ITableControls<
* The two Trs for the expandable row and expanded content row should be contained in a Tbody with no other Tr components.
*/
getExpandedContentTdProps: (args: { item: TItem }) => Omit<TdProps, "ref">;

/**
* Returns the visibility of a column
*/

getColumnVisibility: (columnKey: TColumnKey) => boolean;
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const useLocalTableControls = <
> => {
const state = useTableControlState(args);
const derivedState = getLocalTableControlDerivedState({ ...args, ...state });
const { columnState } = state;
return useTableControlProps({
...args,
...state,
Expand All @@ -42,5 +43,7 @@ export const useLocalTableControls = <
...args,
isEqual: (a, b) => a[args.idProperty] === b[args.idProperty],
}),
idProperty: args.idProperty,
...columnState,
});
};
6 changes: 6 additions & 0 deletions client/src/app/hooks/table-controls/useTableControlProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const useTableControlProps = <
isSelectionEnabled,
isExpansionEnabled,
isActiveItemEnabled,
columnState: { columns },
} = args;

const columnKeys = objectKeys(columnNames);
Expand Down Expand Up @@ -171,6 +172,10 @@ export const useTableControlProps = <
},
});

const getColumnVisibility = (columnKey: TColumnKey) => {
return columns.find((column) => column.id === columnKey)?.isVisible ?? true;
};

return {
...args,
numColumnsBeforeData,
Expand All @@ -191,6 +196,7 @@ export const useTableControlProps = <
getSelectCheckboxTdProps,
getSingleExpandButtonTdProps,
getExpandedContentTdProps,
getColumnVisibility,
},
};
};
15 changes: 15 additions & 0 deletions client/src/app/hooks/table-controls/useTableControlState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useSortState } from "./sorting";
import { usePaginationState } from "./pagination";
import { useActiveItemState } from "./active-item";
import { useExpansionState } from "./expansion";
import { useColumnState } from "./column/useColumnState";

/**
* Provides the "source of truth" state for all table features.
Expand Down Expand Up @@ -66,12 +67,26 @@ export const useTableControlState = <
...args,
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 columnState = useColumnState<TColumnKey>({
columnsKey: tableName,
initialColumns,
});
return {
...args,
filterState,
sortState,
paginationState,
expansionState,
activeItemState,
columnState,
};
};
Loading
Loading