From ed1b5d75a414aaad6fc56f5f1ef669d0e0a48d0c Mon Sep 17 00:00:00 2001 From: Danny Rorabaugh Date: Thu, 15 Aug 2024 10:18:29 -0400 Subject: [PATCH 1/7] Make column order/visibility controlled states --- src/goals/ReviewEntries/ReviewEntriesTable/index.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx index eda333755c..206a4ac41d 100644 --- a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx @@ -6,10 +6,12 @@ import { import { Typography } from "@mui/material"; import { MaterialReactTable, + type MRT_ColumnOrderState, type MRT_Localization, type MRT_PaginationState, type MRT_Row, type MRT_RowVirtualizer, + type MRT_VisibilityState, createMRTColumnHelper, useMaterialReactTable, } from "material-react-table"; @@ -87,6 +89,10 @@ export default function ReviewEntriesTable(props: { const autoResetPageIndexRef = useRef(true); const rowVirtualizerInstanceRef = useRef(null); + const [columnOrder, setColumnOrder] = useState([]); + const [columnVisibility, setColumnVisibility] = useState( + {} + ); const [data, setData] = useState([]); const [enablePagination, setEnablePagination] = useState(false); const [isLoading, setIsLoading] = useState(true); @@ -357,13 +363,15 @@ export default function ReviewEntriesTable(props: { sx: { maxHeight: `calc(100vh - ${enablePagination ? 180 : 130}px)` }, }, muiTablePaperProps: { sx: { height: `calc(100vh - ${topBarHeight}px)` } }, + onColumnOrderChange: setColumnOrder, + onColumnVisibilityChange: setColumnVisibility, onPaginationChange: (updater) => { setPagination(updater); scrollToTop(); }, rowVirtualizerInstanceRef, sortDescFirst: false, - state: { isLoading, pagination }, + state: { columnOrder, columnVisibility, isLoading, pagination }, }); return ; From 12cdcc48c5c0841cdacd8ed358e1111d48b99b21 Mon Sep 17 00:00:00 2001 From: Danny Rorabaugh Date: Thu, 15 Aug 2024 11:07:58 -0400 Subject: [PATCH 2/7] Move col state to redux proj state --- src/components/Project/ProjectActions.ts | 19 ++++++ src/components/Project/ProjectReducer.ts | 21 ++++++ src/components/Project/ProjectReduxTypes.ts | 10 +++ .../ReviewEntriesTable/index.tsx | 66 +++++++++++++------ 4 files changed, 97 insertions(+), 19 deletions(-) diff --git a/src/components/Project/ProjectActions.ts b/src/components/Project/ProjectActions.ts index 672b9bf341..1984d7acf3 100644 --- a/src/components/Project/ProjectActions.ts +++ b/src/components/Project/ProjectActions.ts @@ -1,4 +1,9 @@ import { type Action, type PayloadAction } from "@reduxjs/toolkit"; +import { + type MRT_ColumnOrderState, + type MRT_Updater, + type MRT_VisibilityState, +} from "material-react-table"; import { type Project, type Speaker, type User } from "api/models"; import { @@ -9,6 +14,8 @@ import { import { setProjectId } from "backend/localStorage"; import { resetAction, + setColumnOrderAction, + setColumnVisibilityAction, setProjectAction, setSemanticDomainsAction, setSpeakerAction, @@ -25,6 +32,18 @@ export function resetCurrentProject(): Action { return resetAction(); } +export function setReviewEntriesColumnOrder( + updater: MRT_Updater +): PayloadAction { + return setColumnOrderAction(updater); +} + +export function setReviewEntriesColumnVisibility( + updater: MRT_Updater +): PayloadAction { + return setColumnVisibilityAction(updater); +} + export function setCurrentProject(project?: Project): PayloadAction { return setProjectAction(project ?? newProject()); } diff --git a/src/components/Project/ProjectReducer.ts b/src/components/Project/ProjectReducer.ts index e17e067f6c..f781881c84 100644 --- a/src/components/Project/ProjectReducer.ts +++ b/src/components/Project/ProjectReducer.ts @@ -8,8 +8,27 @@ const projectSlice = createSlice({ initialState: defaultState, reducers: { resetAction: () => defaultState, + setColumnOrderAction: (state, action) => { + if (typeof action.payload === "function") { + state.reviewEntriesColumns.columnOrder = action.payload( + state.reviewEntriesColumns.columnOrder + ); + } else { + state.reviewEntriesColumns.columnOrder = action.payload; + } + }, + setColumnVisibilityAction: (state, action) => { + if (typeof action.payload === "function") { + state.reviewEntriesColumns.columnVisibility = action.payload( + state.reviewEntriesColumns.columnVisibility + ); + } else { + state.reviewEntriesColumns.columnVisibility = action.payload; + } + }, setProjectAction: (state, action) => { if (state.project.id !== action.payload.id) { + state.reviewEntriesColumns = defaultState.reviewEntriesColumns; state.speaker = undefined; state.users = []; } @@ -31,6 +50,8 @@ const projectSlice = createSlice({ export const { resetAction, + setColumnOrderAction, + setColumnVisibilityAction, setProjectAction, setSemanticDomainsAction, setSpeakerAction, diff --git a/src/components/Project/ProjectReduxTypes.ts b/src/components/Project/ProjectReduxTypes.ts index f1335688e4..b739f3c335 100644 --- a/src/components/Project/ProjectReduxTypes.ts +++ b/src/components/Project/ProjectReduxTypes.ts @@ -1,9 +1,18 @@ +import { + type MRT_ColumnOrderState, + type MRT_VisibilityState, +} from "material-react-table"; + import { type Project, type Speaker, type User } from "api/models"; import { type Hash } from "types/hash"; import { newProject } from "types/project"; export interface CurrentProjectState { project: Project; + reviewEntriesColumns: { + columnOrder: MRT_ColumnOrderState; + columnVisibility: MRT_VisibilityState; + }; semanticDomains?: Hash; speaker?: Speaker; users: User[]; @@ -11,5 +20,6 @@ export interface CurrentProjectState { export const defaultState: CurrentProjectState = { project: newProject(), + reviewEntriesColumns: { columnOrder: [], columnVisibility: {} }, users: [], }; diff --git a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx index 206a4ac41d..0ccc934366 100644 --- a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx @@ -11,20 +11,31 @@ import { type MRT_PaginationState, type MRT_Row, type MRT_RowVirtualizer, + type MRT_Updater, type MRT_VisibilityState, createMRTColumnHelper, useMaterialReactTable, } from "material-react-table"; -import { type ReactElement, useEffect, useRef, useState } from "react"; +import { + type ReactElement, + useCallback, + useEffect, + useRef, + useState, +} from "react"; import { useTranslation } from "react-i18next"; import { GramCatGroup, type GrammaticalInfo, type Word } from "api/models"; import { getAllSpeakers, getFrontierWords, getWord } from "backend"; import { topBarHeight } from "components/LandingPage/TopBar"; +import { + setReviewEntriesColumnOrder, + setReviewEntriesColumnVisibility, +} from "components/Project/ProjectActions"; import * as Cell from "goals/ReviewEntries/ReviewEntriesTable/Cells"; import * as ff from "goals/ReviewEntries/ReviewEntriesTable/filterFn"; import * as sf from "goals/ReviewEntries/ReviewEntriesTable/sortingFn"; -import { useAppSelector } from "rootRedux/hooks"; +import { useAppDispatch, useAppSelector } from "rootRedux/hooks"; import { type StoreState } from "rootRedux/types"; import { type Hash } from "types/hash"; @@ -78,21 +89,29 @@ interface RowsPerPageOption { export default function ReviewEntriesTable(props: { disableVirtualization?: boolean; }): ReactElement { - const showDefinitions = useAppSelector( - (state: StoreState) => state.currentProjectState.project.definitionsEnabled + const { columnOrder, columnVisibility } = useAppSelector( + (state: StoreState) => state.currentProjectState.reviewEntriesColumns ); - const showGrammaticalInfo = useAppSelector( - (state: StoreState) => - state.currentProjectState.project.grammaticalInfoEnabled + const { definitionsEnabled, grammaticalInfoEnabled } = useAppSelector( + (state: StoreState) => state.currentProjectState.project + ); + + const dispatch = useAppDispatch(); + const onColumnOrderChange = useCallback( + (updater: MRT_Updater): void => { + dispatch(setReviewEntriesColumnOrder(updater)); + }, + [dispatch] + ); + const onColumnVisibilityChange = useCallback( + (updater: MRT_Updater): void => { + dispatch(setReviewEntriesColumnVisibility(updater)); + }, + [dispatch] ); const autoResetPageIndexRef = useRef(true); const rowVirtualizerInstanceRef = useRef(null); - - const [columnOrder, setColumnOrder] = useState([]); - const [columnVisibility, setColumnVisibility] = useState( - {} - ); const [data, setData] = useState([]); const [enablePagination, setEnablePagination] = useState(false); const [isLoading, setIsLoading] = useState(true); @@ -216,7 +235,7 @@ export default function ReviewEntriesTable(props: { id: "definitions", size: BaselineColumnSize + 20, sortingFn: sf.sortingFnDefinitions, - visibleInShowHideMenu: showDefinitions, + visibleInShowHideMenu: definitionsEnabled, }), // Glosses column @@ -243,7 +262,7 @@ export default function ReviewEntriesTable(props: { header: t("reviewEntries.columns.partOfSpeech"), id: "partOfSpeech", sortingFn: sf.sortingFnPartOfSpeech, - visibleInShowHideMenu: showGrammaticalInfo, + visibleInShowHideMenu: grammaticalInfoEnabled, }), // Domains column @@ -349,8 +368,8 @@ export default function ReviewEntriesTable(props: { enableRowVirtualization: !props.disableVirtualization, initialState: { columnVisibility: { - definitions: showDefinitions, - partOfSpeech: showGrammaticalInfo, + definitions: definitionsEnabled, + partOfSpeech: grammaticalInfoEnabled, }, density: "compact", }, @@ -363,15 +382,24 @@ export default function ReviewEntriesTable(props: { sx: { maxHeight: `calc(100vh - ${enablePagination ? 180 : 130}px)` }, }, muiTablePaperProps: { sx: { height: `calc(100vh - ${topBarHeight}px)` } }, - onColumnOrderChange: setColumnOrder, - onColumnVisibilityChange: setColumnVisibility, + onColumnOrderChange, + onColumnVisibilityChange, onPaginationChange: (updater) => { setPagination(updater); scrollToTop(); }, rowVirtualizerInstanceRef, sortDescFirst: false, - state: { columnOrder, columnVisibility, isLoading, pagination }, + state: { + columnOrder: columnOrder.filter( + (col) => + (col !== "definitions" || definitionsEnabled) && + (col !== "partOfSpeech" || grammaticalInfoEnabled) + ), + columnVisibility, + isLoading, + pagination, + }, }); return ; From e3998af8e2de63d52a3826e87fdf46daef6ed57d Mon Sep 17 00:00:00 2001 From: Danny Rorabaugh Date: Thu, 15 Aug 2024 11:32:18 -0400 Subject: [PATCH 3/7] Restore removed empty line --- src/goals/ReviewEntries/ReviewEntriesTable/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx index 0ccc934366..cd0493d698 100644 --- a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx @@ -112,6 +112,7 @@ export default function ReviewEntriesTable(props: { const autoResetPageIndexRef = useRef(true); const rowVirtualizerInstanceRef = useRef(null); + const [data, setData] = useState([]); const [enablePagination, setEnablePagination] = useState(false); const [isLoading, setIsLoading] = useState(true); From 835a178f6508a6bed6a98af183e3ab82a5f20502 Mon Sep 17 00:00:00 2001 From: Danny Rorabaugh Date: Thu, 15 Aug 2024 15:02:10 -0400 Subject: [PATCH 4/7] Use enum for column ids --- .../ReviewEntriesTable/index.tsx | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx index cd0493d698..41bf476063 100644 --- a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx @@ -74,6 +74,20 @@ const IconHeaderPaddingTop = "2px"; // Vertical offset for a small icon as Heade const IconHeaderWidth = 20; // Width for a small icon as Header const SensesHeaderWidth = 15; // Width for # as Header +enum ColumnId { + Definitions = "definitions", + Delete = "delete", + Domains = "domains", + Edit = "edit", + Flag = "flag", + Glosses = "glosses", + Note = "note", + PartOfSpeech = "partOfSpeech", + Pronunciations = "pronunciations", + Senses = "senses", + Vernacular = "vernacular", +} + // Constants for pagination state. const rowsPerPage = [10, 100]; const initPaginationState: MRT_PaginationState = { @@ -197,6 +211,7 @@ export default function ReviewEntriesTable(props: { enableHiding: false, Header: "", header: t("reviewEntries.columns.edit"), + id: ColumnId.Edit, size: IconColumnSize, visibleInShowHideMenu: false, }), @@ -207,6 +222,7 @@ export default function ReviewEntriesTable(props: { enableColumnOrdering: false, enableHiding: false, header: t("reviewEntries.columns.vernacular"), + id: ColumnId.Vernacular, size: BaselineColumnSize - 40, }), @@ -216,7 +232,7 @@ export default function ReviewEntriesTable(props: { filterFn: "equals", Header: #, header: t("reviewEntries.columns.sensesCount"), - id: "senses", + id: ColumnId.Senses, muiTableHeadCellProps: { sx: { "& .Mui-TableHeadCell-Content-Wrapper": { @@ -233,7 +249,7 @@ export default function ReviewEntriesTable(props: { Cell: ({ row }: CellProps) => , filterFn: ff.filterFnDefinitions, header: t("reviewEntries.columns.definitions"), - id: "definitions", + id: ColumnId.Definitions, size: BaselineColumnSize + 20, sortingFn: sf.sortingFnDefinitions, visibleInShowHideMenu: definitionsEnabled, @@ -244,7 +260,7 @@ export default function ReviewEntriesTable(props: { Cell: ({ row }: CellProps) => , filterFn: ff.filterFnGlosses, header: t("reviewEntries.columns.glosses"), - id: "glosses", + id: ColumnId.Glosses, sortingFn: sf.sortingFnGlosses, }), @@ -261,7 +277,7 @@ export default function ReviewEntriesTable(props: { })), filterVariant: "select", header: t("reviewEntries.columns.partOfSpeech"), - id: "partOfSpeech", + id: ColumnId.PartOfSpeech, sortingFn: sf.sortingFnPartOfSpeech, visibleInShowHideMenu: grammaticalInfoEnabled, }), @@ -271,7 +287,7 @@ export default function ReviewEntriesTable(props: { Cell: ({ row }: CellProps) => , filterFn: ff.filterFnDomains, header: t("reviewEntries.columns.domains"), - id: "domains", + id: ColumnId.Domains, sortingFn: sf.sortingFnDomains, }), @@ -294,7 +310,7 @@ export default function ReviewEntriesTable(props: { ), header: t("reviewEntries.columns.pronunciations"), - id: "pronunciations", + id: ColumnId.Pronunciations, muiTableHeadCellProps: { sx: { "& .Mui-TableHeadCell-Content-Wrapper": { @@ -312,7 +328,7 @@ export default function ReviewEntriesTable(props: { columnHelper.accessor((w) => w.note.text || undefined, { Cell: ({ row }: CellProps) => , header: t("reviewEntries.columns.note"), - id: "note", + id: ColumnId.Note, size: BaselineColumnSize - 40, }), @@ -327,6 +343,7 @@ export default function ReviewEntriesTable(props: { /> ), header: t("reviewEntries.columns.flag"), + id: ColumnId.Flag, muiTableHeadCellProps: { sx: { "& .Mui-TableHeadCell-Content-Wrapper": { @@ -349,6 +366,7 @@ export default function ReviewEntriesTable(props: { enableHiding: false, Header: "", header: t("reviewEntries.columns.delete"), + id: ColumnId.Delete, size: IconColumnSize, visibleInShowHideMenu: false, }), @@ -394,8 +412,8 @@ export default function ReviewEntriesTable(props: { state: { columnOrder: columnOrder.filter( (col) => - (col !== "definitions" || definitionsEnabled) && - (col !== "partOfSpeech" || grammaticalInfoEnabled) + (col !== ColumnId.Definitions || definitionsEnabled) && + (col !== ColumnId.PartOfSpeech || grammaticalInfoEnabled) ), columnVisibility, isLoading, From 4f593a072d68f29ad1d16e5be0dc7a237382ac53 Mon Sep 17 00:00:00 2001 From: Danny Rorabaugh Date: Thu, 15 Aug 2024 17:10:16 -0400 Subject: [PATCH 5/7] Stabilize col order/visibility state --- src/components/Project/ProjectReducer.ts | 14 +++--- .../ReviewEntriesTable/index.tsx | 45 ++++++++++--------- .../ReviewEntriesTable/tests/index.test.tsx | 19 +++----- 3 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/components/Project/ProjectReducer.ts b/src/components/Project/ProjectReducer.ts index f781881c84..c1dc279291 100644 --- a/src/components/Project/ProjectReducer.ts +++ b/src/components/Project/ProjectReducer.ts @@ -9,21 +9,19 @@ const projectSlice = createSlice({ reducers: { resetAction: () => defaultState, setColumnOrderAction: (state, action) => { + const columns = state.reviewEntriesColumns; if (typeof action.payload === "function") { - state.reviewEntriesColumns.columnOrder = action.payload( - state.reviewEntriesColumns.columnOrder - ); + columns.columnOrder = action.payload(columns.columnOrder); } else { - state.reviewEntriesColumns.columnOrder = action.payload; + columns.columnOrder = action.payload; } }, setColumnVisibilityAction: (state, action) => { + const columns = state.reviewEntriesColumns; if (typeof action.payload === "function") { - state.reviewEntriesColumns.columnVisibility = action.payload( - state.reviewEntriesColumns.columnVisibility - ); + columns.columnVisibility = action.payload(columns.columnVisibility); } else { - state.reviewEntriesColumns.columnVisibility = action.payload; + columns.columnVisibility = action.payload; } }, setProjectAction: (state, action) => { diff --git a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx index 41bf476063..b86d2d6322 100644 --- a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx @@ -4,6 +4,7 @@ import { PlayArrow, } from "@mui/icons-material"; import { Typography } from "@mui/material"; +import { createSelector } from "@reduxjs/toolkit"; import { MaterialReactTable, type MRT_ColumnOrderState, @@ -74,7 +75,7 @@ const IconHeaderPaddingTop = "2px"; // Vertical offset for a small icon as Heade const IconHeaderWidth = 20; // Width for a small icon as Header const SensesHeaderWidth = 15; // Width for # as Header -enum ColumnId { +export enum ColumnId { Definitions = "definitions", Delete = "delete", Domains = "domains", @@ -103,8 +104,27 @@ interface RowsPerPageOption { export default function ReviewEntriesTable(props: { disableVirtualization?: boolean; }): ReactElement { - const { columnOrder, columnVisibility } = useAppSelector( - (state: StoreState) => state.currentProjectState.reviewEntriesColumns + const columnOrder = useAppSelector( + (state: StoreState) => + state.currentProjectState.reviewEntriesColumns.columnOrder + ); + const columnVisibility: MRT_VisibilityState = useAppSelector( + // Memoized selector that ensures correct column visibility. + createSelector( + [ + (state: StoreState) => + state.currentProjectState.reviewEntriesColumns.columnVisibility, + (state: StoreState) => + state.currentProjectState.project.definitionsEnabled, + (state: StoreState) => + state.currentProjectState.project.grammaticalInfoEnabled, + ], + (colVis, def, pos) => ({ + ...colVis, + [ColumnId.Definitions]: (colVis[ColumnId.Definitions] ?? def) && def, + [ColumnId.PartOfSpeech]: (colVis[ColumnId.PartOfSpeech] ?? pos) && pos, + }) + ) ); const { definitionsEnabled, grammaticalInfoEnabled } = useAppSelector( (state: StoreState) => state.currentProjectState.project @@ -385,13 +405,7 @@ export default function ReviewEntriesTable(props: { enableGlobalFilter: false, enablePagination, enableRowVirtualization: !props.disableVirtualization, - initialState: { - columnVisibility: { - definitions: definitionsEnabled, - partOfSpeech: grammaticalInfoEnabled, - }, - density: "compact", - }, + initialState: { density: "compact" }, localization, muiPaginationProps: { rowsPerPageOptions }, // Override whiteSpace: "nowrap" from having density: "compact" @@ -409,16 +423,7 @@ export default function ReviewEntriesTable(props: { }, rowVirtualizerInstanceRef, sortDescFirst: false, - state: { - columnOrder: columnOrder.filter( - (col) => - (col !== ColumnId.Definitions || definitionsEnabled) && - (col !== ColumnId.PartOfSpeech || grammaticalInfoEnabled) - ), - columnVisibility, - isLoading, - pagination, - }, + state: { columnOrder, columnVisibility, isLoading, pagination }, }); return ; diff --git a/src/goals/ReviewEntries/ReviewEntriesTable/tests/index.test.tsx b/src/goals/ReviewEntries/ReviewEntriesTable/tests/index.test.tsx index 179279f474..7c1c6239d3 100644 --- a/src/goals/ReviewEntries/ReviewEntriesTable/tests/index.test.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesTable/tests/index.test.tsx @@ -5,7 +5,9 @@ import { type ReactTestRenderer, act, create } from "react-test-renderer"; import configureMockStore from "redux-mock-store"; import { defaultState } from "components/Project/ProjectReduxTypes"; -import ReviewEntriesTable from "goals/ReviewEntries/ReviewEntriesTable"; +import ReviewEntriesTable, { + ColumnId, +} from "goals/ReviewEntries/ReviewEntriesTable"; import VernacularCell from "goals/ReviewEntries/ReviewEntriesTable/Cells/VernacularCell"; import { mockWords, @@ -38,10 +40,6 @@ jest.mock("backend", () => ({ })); jest.mock("components/Pronunciations/PronunciationsBackend"); jest.mock("i18n", () => ({})); -jest.mock("rootRedux/hooks", () => ({ - ...jest.requireActual("rootRedux/hooks"), - useAppDispatch: () => jest.fn(), -})); const mockClickEvent = { stopPropagation: jest.fn() }; const mockGetAllSpeakers = jest.fn(); @@ -153,16 +151,13 @@ describe("ReviewEntriesTable", () => { }); describe("definitionsEnabled & grammaticalInfoEnabled", () => { - const definitionsId = "definitions"; - const partOfSpeechId = "partOfSpeech"; - test("show definitions when definitionsEnabled is true", async () => { await renderReviewEntriesTable(true, false); const colIds = renderer.root .findAllByType(MRT_TableHeadCell) .map((col) => col.props.header.id); - expect(colIds).toContain(definitionsId); - expect(colIds).not.toContain(partOfSpeechId); + expect(colIds).toContain(ColumnId.Definitions); + expect(colIds).not.toContain(ColumnId.PartOfSpeech); }); test("show part of speech when grammaticalInfoEnabled is true", async () => { @@ -170,8 +165,8 @@ describe("ReviewEntriesTable", () => { const colIds = renderer.root .findAllByType(MRT_TableHeadCell) .map((col) => col.props.header.id); - expect(colIds).not.toContain(definitionsId); - expect(colIds).toContain(partOfSpeechId); + expect(colIds).not.toContain(ColumnId.Definitions); + expect(colIds).toContain(ColumnId.PartOfSpeech); }); }); From c5663c3620d7684e4d9edfcfd39f7bb718367a69 Mon Sep 17 00:00:00 2001 From: Danny Rorabaugh Date: Thu, 15 Aug 2024 17:16:02 -0400 Subject: [PATCH 6/7] Simplify dispatch --- .../ReviewEntriesTable/index.tsx | 32 ++++--------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx index b86d2d6322..5bfd9b55a5 100644 --- a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx @@ -7,23 +7,15 @@ import { Typography } from "@mui/material"; import { createSelector } from "@reduxjs/toolkit"; import { MaterialReactTable, - type MRT_ColumnOrderState, type MRT_Localization, type MRT_PaginationState, type MRT_Row, type MRT_RowVirtualizer, - type MRT_Updater, type MRT_VisibilityState, createMRTColumnHelper, useMaterialReactTable, } from "material-react-table"; -import { - type ReactElement, - useCallback, - useEffect, - useRef, - useState, -} from "react"; +import { type ReactElement, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { GramCatGroup, type GrammaticalInfo, type Word } from "api/models"; @@ -104,6 +96,8 @@ interface RowsPerPageOption { export default function ReviewEntriesTable(props: { disableVirtualization?: boolean; }): ReactElement { + const dispatch = useAppDispatch(); + const columnOrder = useAppSelector( (state: StoreState) => state.currentProjectState.reviewEntriesColumns.columnOrder @@ -130,20 +124,6 @@ export default function ReviewEntriesTable(props: { (state: StoreState) => state.currentProjectState.project ); - const dispatch = useAppDispatch(); - const onColumnOrderChange = useCallback( - (updater: MRT_Updater): void => { - dispatch(setReviewEntriesColumnOrder(updater)); - }, - [dispatch] - ); - const onColumnVisibilityChange = useCallback( - (updater: MRT_Updater): void => { - dispatch(setReviewEntriesColumnVisibility(updater)); - }, - [dispatch] - ); - const autoResetPageIndexRef = useRef(true); const rowVirtualizerInstanceRef = useRef(null); @@ -415,8 +395,10 @@ export default function ReviewEntriesTable(props: { sx: { maxHeight: `calc(100vh - ${enablePagination ? 180 : 130}px)` }, }, muiTablePaperProps: { sx: { height: `calc(100vh - ${topBarHeight}px)` } }, - onColumnOrderChange, - onColumnVisibilityChange, + onColumnOrderChange: (updater) => + dispatch(setReviewEntriesColumnOrder(updater)), + onColumnVisibilityChange: (updater) => + dispatch(setReviewEntriesColumnVisibility(updater)), onPaginationChange: (updater) => { setPagination(updater); scrollToTop(); From 2a12153a6f62e272e52a0bc8dab1f49f31afc922 Mon Sep 17 00:00:00 2001 From: Danny Rorabaugh Date: Fri, 27 Sep 2024 11:50:57 -0400 Subject: [PATCH 7/7] Add comments --- src/components/Project/ProjectReducer.ts | 4 ++++ src/components/Project/ProjectReduxTypes.ts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/components/Project/ProjectReducer.ts b/src/components/Project/ProjectReducer.ts index c1dc279291..2abfce0f3f 100644 --- a/src/components/Project/ProjectReducer.ts +++ b/src/components/Project/ProjectReducer.ts @@ -10,6 +10,8 @@ const projectSlice = createSlice({ resetAction: () => defaultState, setColumnOrderAction: (state, action) => { const columns = state.reviewEntriesColumns; + // Payload is a state updater, which can either be a new state + // or a function that takes the previous state and returns a new state. if (typeof action.payload === "function") { columns.columnOrder = action.payload(columns.columnOrder); } else { @@ -18,6 +20,8 @@ const projectSlice = createSlice({ }, setColumnVisibilityAction: (state, action) => { const columns = state.reviewEntriesColumns; + // Payload is a state updater, which can either be a new state + // or a function that takes the previous state and returns a new state. if (typeof action.payload === "function") { columns.columnVisibility = action.payload(columns.columnVisibility); } else { diff --git a/src/components/Project/ProjectReduxTypes.ts b/src/components/Project/ProjectReduxTypes.ts index b739f3c335..ee575f5a1a 100644 --- a/src/components/Project/ProjectReduxTypes.ts +++ b/src/components/Project/ProjectReduxTypes.ts @@ -9,6 +9,8 @@ import { newProject } from "types/project"; export interface CurrentProjectState { project: Project; + /** For project-level persistance of ReviewEntriesTable's managed states + * per https://www.material-react-table.com/docs/guides/state-management */ reviewEntriesColumns: { columnOrder: MRT_ColumnOrderState; columnVisibility: MRT_VisibilityState;