1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Persist column size changes in UserStore

Signed-off-by: Alex Culliere <alozhkin@mirantis.com>
This commit is contained in:
Alex Culliere 2021-02-17 16:43:50 +02:00
parent 74785e2fbc
commit 40a54a886c
7 changed files with 111 additions and 21 deletions

View File

@ -45,18 +45,6 @@ describe("user store tests", () => {
expect(us.seenContexts.has("bar")).toBe(true);
});
it("allows setting and getting preferences", () => {
const us = UserStore.getInstance<UserStore>();
us.preferences.httpsProxy = "abcd://defg";
expect(us.preferences.httpsProxy).toBe("abcd://defg");
expect(us.preferences.colorTheme).toBe(UserStore.defaultTheme);
us.preferences.colorTheme = "light";
expect(us.preferences.colorTheme).toBe("light");
});
it("correctly resets theme to default value", async () => {
const us = UserStore.getInstance<UserStore>();
@ -77,6 +65,32 @@ describe("user store tests", () => {
});
});
describe("preferences", () => {
it("allows setting and getting preferences", () => {
const us = UserStore.getInstance<UserStore>();
us.preferences.httpsProxy = "abcd://defg";
expect(us.preferences.httpsProxy).toBe("abcd://defg");
expect(us.preferences.colorTheme).toBe(UserStore.defaultTheme);
us.preferences.colorTheme = "light";
expect(us.preferences.colorTheme).toBe("light");
});
it("allows setting and getting table column sizes", () => {
const us = UserStore.getInstance<UserStore>();
const [clusterId, tableId] = ["foo", "bar"];
expect( us.getTableSizing(clusterId, tableId) ).toStrictEqual([]);
const sizes = [100, 150, 70, 400];
us.setTableSizing(clusterId, tableId, sizes);
expect( us.getTableSizing(clusterId, tableId) ).toStrictEqual(sizes);
});
});
describe("migrations", () => {
beforeEach(() => {
UserStore.resetInstance();

View File

@ -1,10 +1,13 @@
import { ClusterId } from "./cluster-store";
export type TableId = string;
export type TableSizeRecord = Record<TableId, number[]>;
/**
* Stores a configuration of table column sizes, set by the user,
* for each table by `TableId`, for each cluster by `ClusterId`
* for each talbe by `tableId`
*/
export type TableSizeRecord = Record<string, number[]>;
/**
* Stores a configuration of table column sizes, set by the user,
* for each table by `tableId`, for each cluster by `ClusterId`
*/
export type TableSizeConfig = Record<ClusterId, TableSizeRecord>;

View File

@ -92,6 +92,23 @@ export class UserStore extends BaseStore<UserStoreModel> {
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
}
@action
setTableSizing(clusterId: string, tableId: string, sizes: number[]) {
const { tableSizeConfig: config } = this.preferences;
if (!config[clusterId]) {
config[clusterId] = { [tableId]: sizes };
} else {
config[clusterId][tableId] = sizes;
}
}
getTableSizing(clusterId: string, tableId: string): number[] {
const { tableSizeConfig: config } = this.preferences;
return config[clusterId]?.[tableId] || [];
}
@action
setHiddenTableColumns(tableId: string, names: Set<string> | string[]) {
this.preferences.hiddenTableColumns[tableId] = Array.from(names);

View File

@ -51,6 +51,31 @@ import { CommandContainer } from "./command-palette/command-container";
import { KubeObjectStore } from "../kube-object.store";
import { clusterContext } from "./context";
/**
* Hacking into document event listeners to detect leaks
* TODO: remove this
*/
(function watchEvent() {
const addOne = document.addEventListener;
const removeOne = document.removeEventListener;
(document as any)["listeners"] = new Map<any, any>();
document.addEventListener = (type: string, listener: any) => {
const registry: Map<any, any> = (document as any)["listeners"];
registry.set(listener, type);
addOne(type, listener);
};
document.removeEventListener = (type: string, listener: any) => {
const registry: Map<any, any> = (document as any)["listeners"];
registry.delete(listener);
removeOne(type, listener);
};
})();
@observer
export class App extends React.Component {
static async init() {

View File

@ -37,6 +37,7 @@ export interface IHeaderPlaceholders {
export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
tableId?: string;
clusterId?: string;
className: IClassName;
items?: T[];
store: ItemStore<T>;
@ -112,6 +113,18 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
}
@observable cellSizes: number[];
get persistentCellSizes(): number[] {
const { clusterId, tableId, renderTableHeader: { length } } = this.props;
const savedSizes = userStore.getTableSizing(clusterId, tableId);
return savedSizes.length !== length ? new Array<number>(length) : savedSizes;
}
set persistentCellSizes(sizes: number[]) {
const { clusterId, tableId } = this.props;
userStore.setTableSizing(clusterId, tableId, sizes);
}
constructor(props: ItemListLayoutProps) {
super(props);
@ -121,12 +134,16 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
}
async componentDidMount() {
const { isClusterScoped, isConfigurable, tableId, preloadStores } = this.props;
const { isClusterScoped, isConfigurable, tableId, preloadStores, isResizable } = this.props;
if (isConfigurable && !tableId) {
throw new Error("[ItemListLayout]: configurable list require props.tableId to be specified");
}
if (isResizable) {
this.cellSizes = this.persistentCellSizes;
}
if (preloadStores) {
this.loadStores();
@ -218,6 +235,11 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
return this.applyFilters(filterItems.concat(this.props.filterItems), items);
}
persistCellSizes() {
console.log("Persisting sizes");
this.persistentCellSizes = this.cellSizes;
}
handleCellResize(index: number, width: number) {
this.cellSizes[index] = width;
}
@ -268,6 +290,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
if (resizable) {
cellProps._onResize = width => this.handleCellResize(index, width);
cellProps._onResizeComplete = () => this.persistCellSizes();
}
if (!headCell || !this.isHiddenColumn(headCell)) {
@ -404,7 +427,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
}
renderTableHeader() {
const { customizeTableRowProps, renderTableHeader, isSelectable, isConfigurable, store, isResizable: resizable } = this.props;
const { customizeTableRowProps, renderTableHeader, isSelectable, isConfigurable, store, isResizable } = this.props;
if (!renderTableHeader) {
return;
@ -423,9 +446,13 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
)}
{renderTableHeader.map((cellProps, index) => {
const cellSize = this.cellSizes[index];
const _cellProps = resizable ?
{ ...cellProps, _onResize: (width: number) => this.handleCellResize(index, width), resizable } :
cellProps;
const _cellProps = isResizable ?
{
...cellProps,
_onResize: (width: number) => this.handleCellResize(index, width),
_onResizeComplete: () => this.persistCellSizes(),
isResizable
} : cellProps;
if (cellSize !== undefined) {
_cellProps.size = cellSize;

View File

@ -49,6 +49,7 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
isResizable
{...layoutProps}
className={cssNames("KubeObjectListLayout", className)}
clusterId={store.context.cluster.id}
store={store}
items={items}
preloadStores={false} // loading handled in kubeWatchApi.subscribeStores()

View File

@ -25,6 +25,7 @@ export interface TableCellProps extends React.DOMAttributes<HTMLDivElement> {
_sort?(sortBy: TableSortBy): void; // <Table> sort function, don't use this prop outside (!)
_nowrap?: boolean; // indicator, might come from parent <TableHead>, don't use this prop outside (!)
_onResize?(width: number): void; // <Table> resize callbalck, don't use this prop outside (!)
_onResizeComplete?(): void; // triggers a callback when user stops resizing
}
type ResizeHandlerState = {
@ -104,6 +105,8 @@ export class TableCell extends React.Component<TableCellProps> {
mouseUpHandler() {
this.removeMouseEventListeners();
console.log((document as any)["listeners"]);
this.props?._onResizeComplete();
this.resizeHandlerState = undefined;
}