Fix the "Delete" action on the Kaban view (#5646)
# This PR - Fixes #5520 - Created a shared confirmation modal component for the `ContextMenu` and the `ActionBar` components to avoid code repetition - with its storybook file Looking forward to getting feedback @charlesBochet
This commit is contained in:
committed by
GitHub
parent
e9cf449706
commit
520a883c73
@ -1,14 +1,17 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
|
||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||
|
||||
export const useRecordBoardSelection = (recordBoardId?: string) => {
|
||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||
const { selectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState } =
|
||||
useRecordBoardStates(recordBoardId);
|
||||
|
||||
const resetRecordSelection = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
() => {
|
||||
setContextMenuOpenState(false);
|
||||
const recordIds = snapshot
|
||||
.getLoadable(selectedRecordIdsSelector())
|
||||
.getValue();
|
||||
@ -17,7 +20,11 @@ export const useRecordBoardSelection = (recordBoardId?: string) => {
|
||||
set(isRecordBoardCardSelectedFamilyState(recordId), false);
|
||||
}
|
||||
},
|
||||
[selectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState],
|
||||
[
|
||||
selectedRecordIdsSelector,
|
||||
isRecordBoardCardSelectedFamilyState,
|
||||
setContextMenuOpenState,
|
||||
],
|
||||
);
|
||||
|
||||
const setRecordAsSelected = useRecoilCallback(
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { useRef } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
|
||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||
import SharedNavigationModal from '@/ui/navigation/shared/components/NavigationModal';
|
||||
|
||||
import { ActionBarItem } from './ActionBarItem';
|
||||
|
||||
@ -39,7 +40,15 @@ const StyledLabel = styled.div`
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const ActionBar = ({ selectedIds }: ActionBarProps) => {
|
||||
export const ActionBar = ({ selectedIds = [] }: ActionBarProps) => {
|
||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedIds && selectedIds.length > 1) {
|
||||
setContextMenuOpenState(false);
|
||||
}
|
||||
}, [selectedIds, setContextMenuOpenState]);
|
||||
|
||||
const contextMenuIsOpen = useRecoilValue(contextMenuIsOpenState);
|
||||
const actionBarEntries = useRecoilValue(actionBarEntriesState);
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
@ -62,9 +71,10 @@ export const ActionBar = ({ selectedIds }: ActionBarProps) => {
|
||||
<ActionBarItem key={index} item={item} />
|
||||
))}
|
||||
</StyledContainerActionBar>
|
||||
<div data-select-disable className="action-bar">
|
||||
{actionBarEntries[0]?.ConfirmationModal}
|
||||
</div>
|
||||
<SharedNavigationModal
|
||||
actionBarEntries={actionBarEntries}
|
||||
customClassName="action-bar"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import React, { useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
|
||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import SharedNavigationModal from '@/ui/navigation/shared/components/NavigationModal';
|
||||
|
||||
import { contextMenuEntriesState } from '../states/contextMenuEntriesState';
|
||||
import { contextMenuIsOpenState } from '../states/contextMenuIsOpenState';
|
||||
@ -40,15 +41,8 @@ export const ContextMenu = () => {
|
||||
const contextMenuPosition = useRecoilValue(contextMenuPositionState);
|
||||
const contextMenuIsOpen = useRecoilValue(contextMenuIsOpenState);
|
||||
const contextMenuEntries = useRecoilValue(contextMenuEntriesState);
|
||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [wrapperRef],
|
||||
callback: () => {
|
||||
setContextMenuOpenState(false);
|
||||
},
|
||||
});
|
||||
const actionBarEntries = useRecoilValue(actionBarEntriesState);
|
||||
|
||||
if (!contextMenuIsOpen) {
|
||||
return null;
|
||||
@ -61,18 +55,24 @@ export const ContextMenu = () => {
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<StyledContainerContextMenu
|
||||
className="context-menu"
|
||||
ref={wrapperRef}
|
||||
position={contextMenuPosition}
|
||||
>
|
||||
<DropdownMenu data-select-disable width={width}>
|
||||
<DropdownMenuItemsContainer>
|
||||
{contextMenuEntries.map((item, index) => {
|
||||
return <ContextMenuItem key={index} item={item} />;
|
||||
})}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownMenu>
|
||||
</StyledContainerContextMenu>
|
||||
<>
|
||||
<StyledContainerContextMenu
|
||||
className="context-menu"
|
||||
ref={wrapperRef}
|
||||
position={contextMenuPosition}
|
||||
>
|
||||
<DropdownMenu data-select-disable width={width}>
|
||||
<DropdownMenuItemsContainer>
|
||||
{contextMenuEntries.map((item, index) => {
|
||||
return <ContextMenuItem key={index} item={item} />;
|
||||
})}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownMenu>
|
||||
</StyledContainerContextMenu>
|
||||
<SharedNavigationModal
|
||||
actionBarEntries={actionBarEntries}
|
||||
customClassName="context-menu"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { IconTrash } from 'twenty-ui';
|
||||
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import SharedNavigationModal from '@/ui/navigation/shared/components/NavigationModal';
|
||||
|
||||
const meta: Meta<typeof SharedNavigationModal> = {
|
||||
title: 'UI/Navigation/Shared/SharedNavigationModal',
|
||||
component: SharedNavigationModal,
|
||||
args: {
|
||||
actionBarEntries: [
|
||||
{
|
||||
ConfirmationModal: (
|
||||
<ConfirmationModal
|
||||
title="Title"
|
||||
deleteButtonText="Delete"
|
||||
onConfirmClick={() => {}}
|
||||
setIsOpen={() => {}}
|
||||
isOpen={false}
|
||||
subtitle="Subtitle"
|
||||
/>
|
||||
),
|
||||
Icon: IconTrash,
|
||||
label: 'Label',
|
||||
onClick: () => {},
|
||||
},
|
||||
],
|
||||
customClassName: 'customClassName',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof SharedNavigationModal>;
|
||||
|
||||
export const Default: Story = {};
|
||||
@ -0,0 +1,19 @@
|
||||
import { ActionBarEntry } from '@/ui/navigation/action-bar/types/ActionBarEntry';
|
||||
|
||||
type SharedNavigationModalProps = {
|
||||
actionBarEntries: ActionBarEntry[];
|
||||
customClassName: string;
|
||||
};
|
||||
|
||||
const SharedNavigationModal = ({
|
||||
actionBarEntries,
|
||||
customClassName,
|
||||
}: SharedNavigationModalProps) => {
|
||||
return (
|
||||
<div data-select-disable className={customClassName}>
|
||||
{actionBarEntries[0]?.ConfirmationModal ?? null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SharedNavigationModal;
|
||||
Reference in New Issue
Block a user