From 7b3d310eabce5830155fe21159cedf2d16c1b980 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Wed, 8 Sep 2021 15:58:29 +0200 Subject: [PATCH] BC-12 feat: store accounts table columns resizing to local storage. --- client/src/components/DataTable.js | 13 +++++ client/src/components/Datatable/utils.js | 25 +++++++++ .../containers/Accounts/AccountsDataTable.js | 16 ++++-- client/src/hooks/index.js | 55 ++++++++++++++++--- client/src/hooks/useRequest.js | 6 +- 5 files changed, 99 insertions(+), 16 deletions(-) diff --git a/client/src/components/DataTable.js b/client/src/components/DataTable.js index af3486122..6512a9839 100644 --- a/client/src/components/DataTable.js +++ b/client/src/components/DataTable.js @@ -32,6 +32,8 @@ import TableWrapper from './Datatable/TableWrapper'; import TableIndeterminateCheckboxRow from './Datatable/TableIndeterminateCheckboxRow'; import TableIndeterminateCheckboxHeader from './Datatable/TableIndeterminateCheckboxHeader'; +import { useResizeObserver } from './Datatable/utils'; + /** * Datatable component. */ @@ -78,6 +80,9 @@ export default function DataTable(props) { TablePaginationRenderer, TableFooterRenderer, + onColumnResizing, + initialColumnsWidths, + ...restProps } = props; @@ -106,6 +111,9 @@ export default function DataTable(props) { pageIndex: initialPageIndex, pageSize: initialPageSize, expanded, + columnResizing: { + columnWidths: initialColumnsWidths || {}, + }, }, manualPagination, pageCount: controlledPageCount, @@ -164,6 +172,11 @@ export default function DataTable(props) { saveInvoke(onSelectedRowsChange, selectedFlatRows); }, [selectedRowIds, onSelectedRowsChange]); + // Column resizing observer. + useResizeObserver(table.state, (current, columnWidth, columnsResizing) => { + onColumnResizing && onColumnResizing(current, columnWidth, columnsResizing); + }); + return ( diff --git a/client/src/components/Datatable/utils.js b/client/src/components/Datatable/utils.js index 1e377511a..a219bf5bd 100644 --- a/client/src/components/Datatable/utils.js +++ b/client/src/components/Datatable/utils.js @@ -1,3 +1,5 @@ +import { React } from 'react'; + export const isCellLoading = (loading, cellsCoords, rowIndex, columnId) => { if (!loading) { return false; @@ -8,3 +10,26 @@ export const isCellLoading = (loading, cellsCoords, rowIndex, columnId) => { (cellCoord) => cellCoord[0] === rowIndex && cellCoord[1] === columnId, ); }; + +export const useResizeObserver = (state, callback) => { + // This Ref will contain the id of the column being resized or undefined + const columnResizeRef = React.useRef(); + + React.useEffect(() => { + // We are interested in calling the resize event only when "state.columnResizing?.isResizingColumn" changes from + // a string to undefined, because it indicates that it WAS resizing but it no longer is. + if ( + state.columnResizing && + !state.columnResizing?.isResizingColumn && + columnResizeRef.current + ) { + // Trigger resize event + callback( + columnResizeRef.current, + state.columnResizing.columnWidths[columnResizeRef.current], + state.columnResizing, + ); + } + columnResizeRef.current = state.columnResizing?.isResizingColumn; + }, [callback, state.columnResizing]); +}; diff --git a/client/src/containers/Accounts/AccountsDataTable.js b/client/src/containers/Accounts/AccountsDataTable.js index a07e70066..a51fa1d75 100644 --- a/client/src/containers/Accounts/AccountsDataTable.js +++ b/client/src/containers/Accounts/AccountsDataTable.js @@ -15,6 +15,8 @@ import withAlertsActions from 'containers/Alert/withAlertActions'; import withDialogActions from 'containers/Dialog/withDialogActions'; import withDrawerActions from 'containers/Drawer/withDrawerActions'; +import { useMemorizedColumnsWidths } from '../../hooks'; + /** * Accounts data-table. */ @@ -28,11 +30,8 @@ function AccountsDataTable({ // #withDrawerActions openDrawer, }) { - const { - isAccountsLoading, - isAccountsFetching, - accounts, - } = useAccountsChartContext(); + const { isAccountsLoading, isAccountsFetching, accounts } = + useAccountsChartContext(); // Retrieve accounts table columns. const columns = useAccountsTableColumns(); @@ -80,6 +79,9 @@ function AccountsDataTable({ const handleCellClick = (cell, event) => { openDrawer('account-drawer', { accountId: cell.row.original.id }); }; + // Local storage memorizing columns widths. + const [initialColumnsWidths, , handleColumnResizing] = + useMemorizedColumnsWidths('accounts'); return ( { }; export function useCellAutoFocus(ref, autoFocus, columnId, rowIndex) { - const focus = useMemo(() => isCurrentFocus(autoFocus, columnId, rowIndex), [ - autoFocus, - columnId, - rowIndex, - ]); + const focus = useMemo( + () => isCurrentFocus(autoFocus, columnId, rowIndex), + [autoFocus, columnId, rowIndex], + ); useEffect(() => { if (ref.current && focus) { ref.current.focus(); @@ -65,3 +63,46 @@ export function useCellAutoFocus(ref, autoFocus, columnId, rowIndex) { export * from './useRequestPdf'; export { useAsync, useAutofocus }; + +// Hook +export function useLocalStorage(key, initialValue) { + // State to store our value + // Pass initial state function to useState so logic is only executed once + const [storedValue, setStoredValue] = React.useState(() => { + try { + // Get from local storage by key + const item = window.localStorage.getItem(key); + // Parse stored json or if none return initialValue + return item ? JSON.parse(item) : initialValue; + } catch (error) { + return initialValue; + } + }); + // Return a wrapped version of useState's setter function that ... + // ... persists the new value to localStorage. + const setValue = (value) => { + try { + // Allow value to be a function so we have same API as useState + const valueToStore = + value instanceof Function ? value(storedValue) : value; + // Save state + setStoredValue(valueToStore); + // Save to local storage + window.localStorage.setItem(key, JSON.stringify(valueToStore)); + } catch (error) { + // A more advanced implementation would handle the error case + console.log(error); + } + }; + return [storedValue, setValue]; +} + + +export function useMemorizedColumnsWidths(tableName) { + const [get, save] = useLocalStorage(`${tableName}.columns_widths`, {}); + + const handleColumnResizing = (current, columnWidth, columnsResizing) => { + save(columnsResizing.columnWidths); + }; + return [get, save, handleColumnResizing]; +} \ No newline at end of file diff --git a/client/src/hooks/useRequest.js b/client/src/hooks/useRequest.js index 634a826a8..620e18728 100644 --- a/client/src/hooks/useRequest.js +++ b/client/src/hooks/useRequest.js @@ -6,13 +6,13 @@ import { useSetGlobalErrors, useAuthToken, } from './state'; -import { useAppIntlContext } from '../components/AppIntlProvider'; +import { getCookie } from '../utils'; export default function useApiRequest() { const setGlobalErrors = useSetGlobalErrors(); const { setLogout } = useAuthActions(); - const { currentLocale } = useAppIntlContext(); - + const currentLocale = getCookie('locale'); + // Authentication token. const token = useAuthToken();