Refactor modal (#18377)

## Summary

- Move Modal UI components (`Modal`, `ModalContent`, `ModalHeader`,
`ModalFooter`, `ModalBackdrop`) from `twenty-front` to `twenty-ui` as
stateless, reusable components
- Create `ModalStatefulWrapper` in `twenty-front` that connects Jotai
state (`isModalOpenedComponentState`) to the stateless `Modal` via an
`isOpen` prop
- Rename `modalVariant` prop to `overlay` with clearer values: `'dark'`
(default), `'light'` (in-container), `'transparent'` (invisible panel).
Remove unused `'medium'` overlay
- Rename `modalId` to `modalInstanceId` across the entire modal zone
(~30 consumer files)
- Extract `ModalProps` to its own file in
`twenty-ui/types/ModalProps.ts`; extract `ModalStatefulWrapperProps` to
its own file using `Pick<ModalProps, ...>` for shared props
- Extract `ModalBackdrop` to its own file and export from `twenty-ui`;
use it in `UserOrMetadataLoader` instead of a local styled component
- Use `ModalFooter` in `StepNavigationButton` and `ModalHeader` in
`SpreadsheetImportStepperContainer` instead of duplicated `styled.div`
definitions
- Remove unused `onClose` prop from stateless `Modal`; fix `typeof
document` guard in `ModalStatefulWrapper`
- Split shared types into individual files: `ModalSize.ts`,
`ModalPadding.ts`, `ModalOverlay.ts`
- Extract wyw profiling instrumentation from `vite.config.ts` into
reusable `createWywProfilingPlugin` with parametrized threshold and
improved logging
- Delete old `Modal.tsx`, `Modal.styles.ts`, `ModalContent.tsx`,
`ModalHeader.tsx`, `ModalFooter.tsx` from `twenty-front`
- Add comprehensive Storybook stories in `twenty-ui` covering Default,
Confirmation, Small, ExtraLarge, Closed, and Interactive variants
This commit is contained in:
Charles Bochet
2026-03-04 13:22:31 +01:00
committed by GitHub
parent 995793c0ac
commit 3b2bf39565
99 changed files with 4283 additions and 3688 deletions
@@ -9,7 +9,8 @@ import { type Attachment } from '@/activities/files/types/Attachment';
import { downloadFile } from '@/activities/files/utils/downloadFile';
import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { isAttachmentPreviewEnabledState } from '@/client-config/states/isAttachmentPreviewEnabledState';
import { Modal } from '@/ui/layout/modal/components/Modal';
import { ModalStatefulWrapper } from '@/ui/layout/modal/components/ModalStatefulWrapper';
import { ModalContent, ModalHeader } from 'twenty-ui/layout';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
@@ -104,20 +105,6 @@ const StyledModalTitle = styled.span`
color: ${themeCssVariables.font.color.primary};
`;
const StyledModalHeader = styled(Modal.Header)`
height: auto;
padding: 0;
`;
const StyledModalContent = styled(Modal.Content)`
padding: 0;
`;
const StyledModal = styled(Modal)`
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[3]};
`;
const StyledButtonContainer = styled.div`
display: flex;
flex-direction: row;
@@ -231,14 +218,16 @@ export const AttachmentList = ({
{previewedAttachment &&
isAttachmentPreviewEnabled &&
createPortal(
<StyledModal
modalId={PREVIEW_MODAL_ID}
<ModalStatefulWrapper
modalInstanceId={PREVIEW_MODAL_ID}
size="large"
isClosable
onClose={handleClosePreview}
ignoreContainer
renderInDocumentBody
gap={2}
padding="small"
>
<StyledModalHeader>
<ModalHeader noPadding autoHeight>
<StyledHeader>
<StyledModalTitle>{previewedAttachment.name}</StyledModalTitle>
<StyledButtonContainer>
@@ -256,11 +245,11 @@ export const AttachmentList = ({
/>
</StyledButtonContainer>
</StyledHeader>
</StyledModalHeader>
</ModalHeader>
<ScrollWrapper
componentInstanceId={`preview-modal-${previewedAttachment.id}`}
>
<StyledModalContent>
<ModalContent noPadding>
<Suspense
fallback={
<StyledLoadingContainer>
@@ -275,9 +264,9 @@ export const AttachmentList = ({
documentUrl={getAttachmentUrl(previewedAttachment)}
/>
</Suspense>
</StyledModalContent>
</ModalContent>
</ScrollWrapper>
</StyledModal>,
</ModalStatefulWrapper>,
document.body,
)}
</>