mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-07 09:17:31 +03:00
Refactor fast follow on column move feature (#1665)
* Refactor fast follow on column move feature * Fix lint
This commit is contained in:
parent
cb05b1fbc9
commit
8c21dc8bba
@ -5,11 +5,8 @@ export {
|
||||
IconAlertTriangle,
|
||||
IconArchive,
|
||||
IconArrowBack,
|
||||
IconArrowNarrowDown,
|
||||
IconArrowNarrowLeft,
|
||||
IconArrowNarrowRight,
|
||||
IconArrowNarrowUp,
|
||||
IconArrowDown,
|
||||
IconArrowLeft,
|
||||
IconArrowRight,
|
||||
IconArrowUp,
|
||||
IconArrowUpRight,
|
||||
|
@ -4,7 +4,7 @@ import styled from '@emotion/styled';
|
||||
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||
import { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
import { ColumnHeaderDropdownId } from '../constants/ColumnHeaderDropdownId';
|
||||
import { ColumnHeadDropdownId } from '../constants/ColumnHeadDropdownId';
|
||||
import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
|
||||
import { EntityTableHeaderOptions } from './EntityTableHeaderOptions';
|
||||
@ -49,7 +49,7 @@ export const ColumnHead = ({
|
||||
const theme = useTheme();
|
||||
|
||||
const { openDropdownButton } = useDropdownButton({
|
||||
dropdownId: ColumnHeaderDropdownId,
|
||||
dropdownId: ColumnHeadDropdownId,
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -166,21 +166,22 @@ export const EntityTableHeader = () => {
|
||||
</th>
|
||||
|
||||
{visibleTableColumns.map((column, index) => (
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<StyledColumnHeaderCell
|
||||
key={column.key}
|
||||
isResizing={resizedFieldKey === column.key}
|
||||
columnWidth={Math.max(
|
||||
tableColumnsByKey[column.key].size +
|
||||
(resizedFieldKey === column.key ? resizeFieldOffset : 0),
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
>
|
||||
<StyledColumnHeaderCell
|
||||
key={column.key}
|
||||
isResizing={resizedFieldKey === column.key}
|
||||
columnWidth={Math.max(
|
||||
tableColumnsByKey[column.key].size +
|
||||
(resizedFieldKey === column.key ? resizeFieldOffset : 0),
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<ColumnHead
|
||||
column={column}
|
||||
isFirstColumn={index === 0}
|
||||
isLastColumn={index === visibleTableColumns.length - 1}
|
||||
/>
|
||||
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
@ -188,8 +189,8 @@ export const EntityTableHeader = () => {
|
||||
setResizedFieldKey(column.key);
|
||||
}}
|
||||
/>
|
||||
</StyledColumnHeaderCell>
|
||||
</RecoilScope>
|
||||
</RecoilScope>
|
||||
</StyledColumnHeaderCell>
|
||||
))}
|
||||
<th>
|
||||
{hiddenTableColumns.length > 0 && (
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
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 { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||
import { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import {
|
||||
IconArrowNarrowLeft,
|
||||
IconArrowNarrowRight,
|
||||
IconEyeOff,
|
||||
} from '@/ui/icon';
|
||||
import { IconArrowLeft, IconArrowRight, IconEyeOff } from '@/ui/icon';
|
||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||
|
||||
import { ColumnHeaderDropdownId } from '../constants/ColumnHeaderDropdownId';
|
||||
import { ColumnHeadDropdownId } from '../constants/ColumnHeadDropdownId';
|
||||
import { useTableColumns } from '../hooks/useTableColumns';
|
||||
import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
|
||||
@ -33,8 +28,6 @@ export const EntityTableHeaderOptions = ({
|
||||
isFirstColumn,
|
||||
isLastColumn,
|
||||
}: EntityTableHeaderOptionsProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const {
|
||||
handleColumnVisibilityChange,
|
||||
handleColumnLeftMove,
|
||||
@ -42,19 +35,23 @@ export const EntityTableHeaderOptions = ({
|
||||
} = useTableColumns();
|
||||
|
||||
const { closeDropdownButton } = useDropdownButton({
|
||||
dropdownId: ColumnHeaderDropdownId,
|
||||
dropdownId: ColumnHeadDropdownId,
|
||||
});
|
||||
|
||||
const handleColumnMoveLeft = () => {
|
||||
closeDropdownButton();
|
||||
if (isFirstColumn) return;
|
||||
else handleColumnLeftMove(column);
|
||||
if (isFirstColumn) {
|
||||
return;
|
||||
}
|
||||
handleColumnLeftMove(column);
|
||||
};
|
||||
|
||||
const handleColumnMoveRight = () => {
|
||||
closeDropdownButton();
|
||||
if (isLastColumn) return;
|
||||
else handleColumnRightMove(column);
|
||||
if (isLastColumn) {
|
||||
return;
|
||||
}
|
||||
handleColumnRightMove(column);
|
||||
};
|
||||
|
||||
const handleColumnVisibility = () => {
|
||||
@ -64,26 +61,22 @@ export const EntityTableHeaderOptions = ({
|
||||
return (
|
||||
<StyledDropdownContainer>
|
||||
<DropdownButton
|
||||
dropdownId={ColumnHeaderDropdownId}
|
||||
dropdownId={ColumnHeadDropdownId}
|
||||
dropdownComponents={
|
||||
<StyledDropdownMenu>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={() => (
|
||||
<IconArrowNarrowLeft size={theme.icon.size.md} />
|
||||
)}
|
||||
LeftIcon={IconArrowLeft}
|
||||
onClick={handleColumnMoveLeft}
|
||||
text="Move left"
|
||||
/>
|
||||
<MenuItem
|
||||
LeftIcon={() => (
|
||||
<IconArrowNarrowRight size={theme.icon.size.md} />
|
||||
)}
|
||||
LeftIcon={IconArrowRight}
|
||||
onClick={handleColumnMoveRight}
|
||||
text="Move right"
|
||||
/>
|
||||
<MenuItem
|
||||
LeftIcon={() => <IconEyeOff size={theme.icon.size.md} />}
|
||||
LeftIcon={IconEyeOff}
|
||||
onClick={handleColumnVisibility}
|
||||
text="Hide"
|
||||
/>
|
||||
|
@ -0,0 +1 @@
|
||||
export const ColumnHeadDropdownId = 'table-head-options';
|
@ -1 +0,0 @@
|
||||
export const ColumnHeaderDropdownId = 'table-header-options';
|
@ -51,29 +51,29 @@ export const useTableColumns = () => {
|
||||
|
||||
const handleColumnMove = useCallback(
|
||||
(direction: string, column: ColumnDefinition<ViewFieldMetadata>) => {
|
||||
const tableColumnIndex = tableColumns.findIndex(
|
||||
const currentColumnArrayIndex = tableColumns.findIndex(
|
||||
(tableColumn) => tableColumn.key === column.key,
|
||||
);
|
||||
if (tableColumnIndex >= 0) {
|
||||
const currentColumn = tableColumns[tableColumnIndex];
|
||||
const targetColumn =
|
||||
direction === 'left'
|
||||
? 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);
|
||||
const targetColumnArrayIndex =
|
||||
direction === 'left'
|
||||
? currentColumnArrayIndex - 1
|
||||
: currentColumnArrayIndex + 1;
|
||||
|
||||
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],
|
||||
|
@ -164,6 +164,7 @@ export const useTableViewFields = ({
|
||||
(column) =>
|
||||
savedTableColumnsByKey[column.key] &&
|
||||
(savedTableColumnsByKey[column.key].size !== column.size ||
|
||||
savedTableColumnsByKey[column.key].index !== column.index ||
|
||||
savedTableColumnsByKey[column.key].isVisible !== column.isVisible),
|
||||
);
|
||||
await updateViewFields(viewFieldsToUpdate);
|
||||
@ -178,5 +179,5 @@ export const useTableViewFields = ({
|
||||
updateViewFields,
|
||||
]);
|
||||
|
||||
return { createViewFields, persistColumns, updateViewFields };
|
||||
return { createViewFields, persistColumns };
|
||||
};
|
||||
|
@ -41,12 +41,11 @@ export const useTableViews = ({
|
||||
type: ViewType.Table,
|
||||
RecoilScopeContext: TableRecoilScopeContext,
|
||||
});
|
||||
const { createViewFields, persistColumns, updateViewFields } =
|
||||
useTableViewFields({
|
||||
objectId,
|
||||
columnDefinitions,
|
||||
skipFetch: isFetchingViews,
|
||||
});
|
||||
const { createViewFields, persistColumns } = useTableViewFields({
|
||||
objectId,
|
||||
columnDefinitions,
|
||||
skipFetch: isFetchingViews,
|
||||
});
|
||||
|
||||
const { createViewFilters, persistFilters } = useViewFilters({
|
||||
RecoilScopeContext: TableRecoilScopeContext,
|
||||
@ -62,7 +61,6 @@ export const useTableViews = ({
|
||||
await persistColumns();
|
||||
await persistFilters();
|
||||
await persistSorts();
|
||||
await updateViewFields(tableColumns);
|
||||
};
|
||||
|
||||
return { createView, deleteView, submitCurrentView, updateView };
|
||||
|
@ -46,17 +46,16 @@ export const FilterByName: Story = {
|
||||
delay: 200,
|
||||
});
|
||||
|
||||
await sleep(50);
|
||||
await sleep(200);
|
||||
|
||||
expect(await canvas.findByText('Airbnb')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Aircall')).toBeInTheDocument();
|
||||
await sleep(50);
|
||||
await expect(canvas.queryAllByText('Qonto')).toStrictEqual([]);
|
||||
expect(await 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');
|
||||
});
|
||||
expect(accountOwnerFilter).toBeInTheDocument();
|
||||
expect(nameFilter).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -280,7 +280,7 @@ export const SelectRelationWithKeys: Story = {
|
||||
|
||||
await userEvent.type(relationInput, '{enter}');
|
||||
|
||||
await sleep(50);
|
||||
await sleep(200);
|
||||
|
||||
const allAirbns = await canvas.findAllByText('Aircall');
|
||||
expect(allAirbns.length).toBe(1);
|
||||
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "viewFields" ALTER COLUMN "index" SET DATA TYPE DOUBLE PRECISION;
|
Loading…
Reference in New Issue
Block a user