Refactor fast follow on column move feature (#1665)

* Refactor fast follow on column move feature

* Fix lint
This commit is contained in:
Charles Bochet
2023-09-19 16:42:11 -07:00
committed by GitHub
parent cb05b1fbc9
commit 8c21dc8bba
12 changed files with 66 additions and 75 deletions

View File

@ -5,11 +5,8 @@ export {
IconAlertTriangle, IconAlertTriangle,
IconArchive, IconArchive,
IconArrowBack, IconArrowBack,
IconArrowNarrowDown,
IconArrowNarrowLeft,
IconArrowNarrowRight,
IconArrowNarrowUp,
IconArrowDown, IconArrowDown,
IconArrowLeft,
IconArrowRight, IconArrowRight,
IconArrowUp, IconArrowUp,
IconArrowUpRight, IconArrowUpRight,

View File

@ -4,7 +4,7 @@ import styled from '@emotion/styled';
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton'; import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
import { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField'; import { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField';
import { ColumnHeaderDropdownId } from '../constants/ColumnHeaderDropdownId'; import { ColumnHeadDropdownId } from '../constants/ColumnHeadDropdownId';
import { ColumnDefinition } from '../types/ColumnDefinition'; import { ColumnDefinition } from '../types/ColumnDefinition';
import { EntityTableHeaderOptions } from './EntityTableHeaderOptions'; import { EntityTableHeaderOptions } from './EntityTableHeaderOptions';
@ -49,7 +49,7 @@ export const ColumnHead = ({
const theme = useTheme(); const theme = useTheme();
const { openDropdownButton } = useDropdownButton({ const { openDropdownButton } = useDropdownButton({
dropdownId: ColumnHeaderDropdownId, dropdownId: ColumnHeadDropdownId,
}); });
return ( return (

View File

@ -166,21 +166,22 @@ export const EntityTableHeader = () => {
</th> </th>
{visibleTableColumns.map((column, index) => ( {visibleTableColumns.map((column, index) => (
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <StyledColumnHeaderCell
<StyledColumnHeaderCell key={column.key}
key={column.key} isResizing={resizedFieldKey === column.key}
isResizing={resizedFieldKey === column.key} columnWidth={Math.max(
columnWidth={Math.max( tableColumnsByKey[column.key].size +
tableColumnsByKey[column.key].size + (resizedFieldKey === column.key ? resizeFieldOffset : 0),
(resizedFieldKey === column.key ? resizeFieldOffset : 0), COLUMN_MIN_WIDTH,
COLUMN_MIN_WIDTH, )}
)} >
> <RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
<ColumnHead <ColumnHead
column={column} column={column}
isFirstColumn={index === 0} isFirstColumn={index === 0}
isLastColumn={index === visibleTableColumns.length - 1} isLastColumn={index === visibleTableColumns.length - 1}
/> />
<StyledResizeHandler <StyledResizeHandler
className="cursor-col-resize" className="cursor-col-resize"
role="separator" role="separator"
@ -188,8 +189,8 @@ export const EntityTableHeader = () => {
setResizedFieldKey(column.key); setResizedFieldKey(column.key);
}} }}
/> />
</StyledColumnHeaderCell> </RecoilScope>
</RecoilScope> </StyledColumnHeaderCell>
))} ))}
<th> <th>
{hiddenTableColumns.length > 0 && ( {hiddenTableColumns.length > 0 && (

View File

@ -1,4 +1,3 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton'; import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
@ -6,14 +5,10 @@ import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton'; import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
import { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField'; import { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField';
import { import { IconArrowLeft, IconArrowRight, IconEyeOff } from '@/ui/icon';
IconArrowNarrowLeft,
IconArrowNarrowRight,
IconEyeOff,
} from '@/ui/icon';
import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { ColumnHeaderDropdownId } from '../constants/ColumnHeaderDropdownId'; import { ColumnHeadDropdownId } from '../constants/ColumnHeadDropdownId';
import { useTableColumns } from '../hooks/useTableColumns'; import { useTableColumns } from '../hooks/useTableColumns';
import { ColumnDefinition } from '../types/ColumnDefinition'; import { ColumnDefinition } from '../types/ColumnDefinition';
@ -33,8 +28,6 @@ export const EntityTableHeaderOptions = ({
isFirstColumn, isFirstColumn,
isLastColumn, isLastColumn,
}: EntityTableHeaderOptionsProps) => { }: EntityTableHeaderOptionsProps) => {
const theme = useTheme();
const { const {
handleColumnVisibilityChange, handleColumnVisibilityChange,
handleColumnLeftMove, handleColumnLeftMove,
@ -42,19 +35,23 @@ export const EntityTableHeaderOptions = ({
} = useTableColumns(); } = useTableColumns();
const { closeDropdownButton } = useDropdownButton({ const { closeDropdownButton } = useDropdownButton({
dropdownId: ColumnHeaderDropdownId, dropdownId: ColumnHeadDropdownId,
}); });
const handleColumnMoveLeft = () => { const handleColumnMoveLeft = () => {
closeDropdownButton(); closeDropdownButton();
if (isFirstColumn) return; if (isFirstColumn) {
else handleColumnLeftMove(column); return;
}
handleColumnLeftMove(column);
}; };
const handleColumnMoveRight = () => { const handleColumnMoveRight = () => {
closeDropdownButton(); closeDropdownButton();
if (isLastColumn) return; if (isLastColumn) {
else handleColumnRightMove(column); return;
}
handleColumnRightMove(column);
}; };
const handleColumnVisibility = () => { const handleColumnVisibility = () => {
@ -64,26 +61,22 @@ export const EntityTableHeaderOptions = ({
return ( return (
<StyledDropdownContainer> <StyledDropdownContainer>
<DropdownButton <DropdownButton
dropdownId={ColumnHeaderDropdownId} dropdownId={ColumnHeadDropdownId}
dropdownComponents={ dropdownComponents={
<StyledDropdownMenu> <StyledDropdownMenu>
<StyledDropdownMenuItemsContainer> <StyledDropdownMenuItemsContainer>
<MenuItem <MenuItem
LeftIcon={() => ( LeftIcon={IconArrowLeft}
<IconArrowNarrowLeft size={theme.icon.size.md} />
)}
onClick={handleColumnMoveLeft} onClick={handleColumnMoveLeft}
text="Move left" text="Move left"
/> />
<MenuItem <MenuItem
LeftIcon={() => ( LeftIcon={IconArrowRight}
<IconArrowNarrowRight size={theme.icon.size.md} />
)}
onClick={handleColumnMoveRight} onClick={handleColumnMoveRight}
text="Move right" text="Move right"
/> />
<MenuItem <MenuItem
LeftIcon={() => <IconEyeOff size={theme.icon.size.md} />} LeftIcon={IconEyeOff}
onClick={handleColumnVisibility} onClick={handleColumnVisibility}
text="Hide" text="Hide"
/> />

View File

@ -0,0 +1 @@
export const ColumnHeadDropdownId = 'table-head-options';

View File

@ -1 +0,0 @@
export const ColumnHeaderDropdownId = 'table-header-options';

View File

@ -51,29 +51,29 @@ export const useTableColumns = () => {
const handleColumnMove = useCallback( const handleColumnMove = useCallback(
(direction: string, column: ColumnDefinition<ViewFieldMetadata>) => { (direction: string, column: ColumnDefinition<ViewFieldMetadata>) => {
const tableColumnIndex = tableColumns.findIndex( const currentColumnArrayIndex = tableColumns.findIndex(
(tableColumn) => tableColumn.key === column.key, (tableColumn) => tableColumn.key === column.key,
); );
if (tableColumnIndex >= 0) { const targetColumnArrayIndex =
const currentColumn = tableColumns[tableColumnIndex]; direction === 'left'
const targetColumn = ? currentColumnArrayIndex - 1
direction === 'left' : currentColumnArrayIndex + 1;
? tableColumns[tableColumnIndex - 1]
: tableColumns[tableColumnIndex + 1];
const updatedColumns = tableColumns
.map((tableColumn) => {
switch (tableColumn.key) {
case targetColumn.key:
return { ...tableColumn, index: currentColumn.index };
case currentColumn.key:
return { ...tableColumn, index: targetColumn.index };
default:
return tableColumn;
}
})
.sort((columnA, columnB) => columnA.index - columnB.index);
setTableColumns(updatedColumns); if (currentColumnArrayIndex >= 0) {
const currentColumn = tableColumns[currentColumnArrayIndex];
const targetColumn = tableColumns[targetColumnArrayIndex];
const newTableColumns = [...tableColumns];
newTableColumns[currentColumnArrayIndex] = {
...targetColumn,
index: currentColumn.index,
};
newTableColumns[targetColumnArrayIndex] = {
...currentColumn,
index: targetColumn.index,
};
setTableColumns(newTableColumns);
} }
}, },
[tableColumns, setTableColumns], [tableColumns, setTableColumns],

View File

@ -164,6 +164,7 @@ export const useTableViewFields = ({
(column) => (column) =>
savedTableColumnsByKey[column.key] && savedTableColumnsByKey[column.key] &&
(savedTableColumnsByKey[column.key].size !== column.size || (savedTableColumnsByKey[column.key].size !== column.size ||
savedTableColumnsByKey[column.key].index !== column.index ||
savedTableColumnsByKey[column.key].isVisible !== column.isVisible), savedTableColumnsByKey[column.key].isVisible !== column.isVisible),
); );
await updateViewFields(viewFieldsToUpdate); await updateViewFields(viewFieldsToUpdate);
@ -178,5 +179,5 @@ export const useTableViewFields = ({
updateViewFields, updateViewFields,
]); ]);
return { createViewFields, persistColumns, updateViewFields }; return { createViewFields, persistColumns };
}; };

View File

@ -41,12 +41,11 @@ export const useTableViews = ({
type: ViewType.Table, type: ViewType.Table,
RecoilScopeContext: TableRecoilScopeContext, RecoilScopeContext: TableRecoilScopeContext,
}); });
const { createViewFields, persistColumns, updateViewFields } = const { createViewFields, persistColumns } = useTableViewFields({
useTableViewFields({ objectId,
objectId, columnDefinitions,
columnDefinitions, skipFetch: isFetchingViews,
skipFetch: isFetchingViews, });
});
const { createViewFilters, persistFilters } = useViewFilters({ const { createViewFilters, persistFilters } = useViewFilters({
RecoilScopeContext: TableRecoilScopeContext, RecoilScopeContext: TableRecoilScopeContext,
@ -62,7 +61,6 @@ export const useTableViews = ({
await persistColumns(); await persistColumns();
await persistFilters(); await persistFilters();
await persistSorts(); await persistSorts();
await updateViewFields(tableColumns);
}; };
return { createView, deleteView, submitCurrentView, updateView }; return { createView, deleteView, submitCurrentView, updateView };

View File

@ -46,17 +46,16 @@ export const FilterByName: Story = {
delay: 200, delay: 200,
}); });
await sleep(50); await sleep(200);
expect(await canvas.findByText('Airbnb')).toBeInTheDocument(); expect(await canvas.findByText('Airbnb')).toBeInTheDocument();
expect(await canvas.findByText('Aircall')).toBeInTheDocument(); expect(await canvas.findByText('Aircall')).toBeInTheDocument();
await sleep(50); expect(await canvas.queryAllByText('Qonto')).toStrictEqual([]);
await expect(canvas.queryAllByText('Qonto')).toStrictEqual([]);
const accountOwnerFilter = canvas.getAllByText('Name').find((item) => { const nameFilter = canvas.getAllByText('Name').find((item) => {
return item.parentElement?.textContent?.includes('Name: Air'); return item.parentElement?.textContent?.includes('Name: Air');
}); });
expect(accountOwnerFilter).toBeInTheDocument(); expect(nameFilter).toBeInTheDocument();
}, },
}; };

View File

@ -280,7 +280,7 @@ export const SelectRelationWithKeys: Story = {
await userEvent.type(relationInput, '{enter}'); await userEvent.type(relationInput, '{enter}');
await sleep(50); await sleep(200);
const allAirbns = await canvas.findAllByText('Aircall'); const allAirbns = await canvas.findAllByText('Aircall');
expect(allAirbns.length).toBe(1); expect(allAirbns.length).toBe(1);

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "viewFields" ALTER COLUMN "index" SET DATA TYPE DOUBLE PRECISION;