feat(customHomePage): add remove module confirmation (#14363)

Co-authored-by: Chris Collins <chriscollins3456@gmail.com>
This commit is contained in:
v-tarasevich-blitz-brain 2025-08-11 20:40:17 +03:00 committed by GitHub
parent 519fc3fd24
commit acc7785e88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 99 additions and 46 deletions

View File

@ -1,11 +1,13 @@
import { Icon, Text, Tooltip, colors } from '@components'; import { Icon, Text, Tooltip, colors } from '@components';
import { Dropdown } from 'antd'; import { Dropdown } from 'antd';
import React, { useCallback } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { usePageTemplateContext } from '@app/homeV3/context/PageTemplateContext'; import { usePageTemplateContext } from '@app/homeV3/context/PageTemplateContext';
import { DEFAULT_GLOBAL_MODULE_TYPES } from '@app/homeV3/modules/constants'; import { DEFAULT_GLOBAL_MODULE_TYPES } from '@app/homeV3/modules/constants';
import { getCustomGlobalModules } from '@app/homeV3/template/components/addModuleMenu/utils';
import { ModulePositionInput } from '@app/homeV3/template/types'; import { ModulePositionInput } from '@app/homeV3/template/types';
import { ConfirmationModal } from '@app/sharedV2/modals/ConfirmationModal';
import { PageModuleFragment } from '@graphql/template.generated'; import { PageModuleFragment } from '@graphql/template.generated';
@ -32,9 +34,16 @@ interface Props {
} }
export default function ModuleMenu({ module, position }: Props) { export default function ModuleMenu({ module, position }: Props) {
const [showRemoveModuleConfirmation, setShowRemoveModuleConfirmation] = useState<boolean>(false);
const { type } = module.properties; const { type } = module.properties;
const canEdit = !DEFAULT_GLOBAL_MODULE_TYPES.includes(type); const canEdit = !DEFAULT_GLOBAL_MODULE_TYPES.includes(type);
const { globalTemplate } = usePageTemplateContext();
const isAdminCreatedModule = useMemo(() => {
const adminCreatedModules = getCustomGlobalModules(globalTemplate);
return adminCreatedModules.some((adminCreatedModule) => adminCreatedModule.urn === module.urn);
}, [globalTemplate, module.urn]);
const { const {
removeModule, removeModule,
moduleModalState: { openToEdit }, moduleModalState: { openToEdit },
@ -58,6 +67,7 @@ export default function ModuleMenu({ module, position }: Props) {
const menuItemStyle = { fontSize: '14px', padding: '5px 16px' }; const menuItemStyle = { fontSize: '14px', padding: '5px 16px' };
return ( return (
<>
<DropdownWrapper onClick={handleMenuClick}> <DropdownWrapper onClick={handleMenuClick}>
<Dropdown <Dropdown
trigger={['click']} trigger={['click']}
@ -97,7 +107,7 @@ export default function ModuleMenu({ module, position }: Props) {
...menuItemStyle, ...menuItemStyle,
color: colors.red[500], color: colors.red[500],
}, },
onClick: handleRemove, onClick: () => setShowRemoveModuleConfirmation(true),
}, },
], ],
}} }}
@ -105,5 +115,20 @@ export default function ModuleMenu({ module, position }: Props) {
<StyledIcon icon="DotsThreeVertical" source="phosphor" size="lg" /> <StyledIcon icon="DotsThreeVertical" source="phosphor" size="lg" />
</Dropdown> </Dropdown>
</DropdownWrapper> </DropdownWrapper>
<ConfirmationModal
isOpen={!!showRemoveModuleConfirmation}
handleConfirm={handleRemove}
handleClose={() => setShowRemoveModuleConfirmation(false)}
modalTitle="Remove Module?"
modalText={
isAdminCreatedModule
? 'Are you sure you want to remove this module? You can re-add it later from the Home Defaults section when adding a new module.'
: 'Are you sure you want to remove this module? You can always create a new one later if needed.'
}
closeButtonText="Cancel"
confirmButtonText="Confirm"
/>
</>
); );
} }

View File

@ -27,6 +27,13 @@ vi.mock('@components', () => ({
)), )),
Tooltip: (props: any) => <span {...props} />, Tooltip: (props: any) => <span {...props} />,
Text: (props: any) => <p {...props} />, Text: (props: any) => <p {...props} />,
Button: (props: any) => <button type="button" data-testid="confirm" {...props} />,
Heading: (props: any) => <p {...props} />,
typography: {
fonts: {
body: '#eeeeee',
},
},
colors: { colors: {
gray: { gray: {
600: '#4B5563', 600: '#4B5563',
@ -81,6 +88,9 @@ describe('ModuleMenu', () => {
// Click the remove option // Click the remove option
const removeButton = screen.getByText('Remove'); const removeButton = screen.getByText('Remove');
fireEvent.click(removeButton); fireEvent.click(removeButton);
// Confirm removing
const confirm = screen.getByTestId('modal-confirm-button');
fireEvent.click(confirm);
// Verify that removeModule was called with correct parameters // Verify that removeModule was called with correct parameters
expect(mockRemoveModule).toHaveBeenCalledWith({ expect(mockRemoveModule).toHaveBeenCalledWith({
@ -115,6 +125,9 @@ describe('ModuleMenu', () => {
// Click the remove option // Click the remove option
const removeButton = screen.getByText('Remove'); const removeButton = screen.getByText('Remove');
fireEvent.click(removeButton); fireEvent.click(removeButton);
// Confirm removing
const confirm = screen.getByTestId('modal-confirm-button');
fireEvent.click(confirm);
// Verify that removeModule was called with correct parameters // Verify that removeModule was called with correct parameters
expect(mockRemoveModule).toHaveBeenCalledWith({ expect(mockRemoveModule).toHaveBeenCalledWith({
@ -152,6 +165,9 @@ describe('ModuleMenu', () => {
// Click the remove option // Click the remove option
const removeButton = screen.getByText('Remove'); const removeButton = screen.getByText('Remove');
fireEvent.click(removeButton); fireEvent.click(removeButton);
// Confirm removing
const confirm = screen.getByTestId('modal-confirm-button');
fireEvent.click(confirm);
// Verify that removeModule was called with moduleIndex // Verify that removeModule was called with moduleIndex
expect(mockRemoveModule).toHaveBeenCalledWith({ expect(mockRemoveModule).toHaveBeenCalledWith({
@ -181,6 +197,9 @@ describe('ModuleMenu', () => {
// Click the remove option // Click the remove option
const removeButton = screen.getByText('Remove'); const removeButton = screen.getByText('Remove');
fireEvent.click(removeButton); fireEvent.click(removeButton);
// Confirm removing
const confirm = screen.getByTestId('modal-confirm-button');
fireEvent.click(confirm);
// Verify that removeModule was called with correct URN // Verify that removeModule was called with correct URN
expect(mockRemoveModule).toHaveBeenCalledWith({ expect(mockRemoveModule).toHaveBeenCalledWith({
@ -204,6 +223,9 @@ describe('ModuleMenu', () => {
// Click the remove option // Click the remove option
const removeButton = screen.getByText('Remove'); const removeButton = screen.getByText('Remove');
fireEvent.click(removeButton); fireEvent.click(removeButton);
// Confirm removing
const confirm = screen.getByTestId('modal-confirm-button');
fireEvent.click(confirm);
// Verify that removeModule was called with minimal position // Verify that removeModule was called with minimal position
expect(mockRemoveModule).toHaveBeenCalledWith({ expect(mockRemoveModule).toHaveBeenCalledWith({
@ -233,6 +255,9 @@ describe('ModuleMenu', () => {
// Click the remove option // Click the remove option
const removeButton = screen.getByText('Remove'); const removeButton = screen.getByText('Remove');
fireEvent.click(removeButton); fireEvent.click(removeButton);
// Confirm removing
const confirm = screen.getByTestId('modal-confirm-button');
fireEvent.click(confirm);
// Verify that removeModule was called with special character URN // Verify that removeModule was called with special character URN
expect(mockRemoveModule).toHaveBeenCalledWith({ expect(mockRemoveModule).toHaveBeenCalledWith({
@ -270,11 +295,14 @@ describe('ModuleMenu', () => {
const menuButton = screen.getByTestId('icon'); const menuButton = screen.getByTestId('icon');
fireEvent.click(menuButton); fireEvent.click(menuButton);
// Click the remove option multiple times rapidly // Click the remove option
const removeButton = screen.getByText('Remove'); const removeButton = screen.getByText('Remove');
fireEvent.click(removeButton); fireEvent.click(removeButton);
fireEvent.click(removeButton); // Click confirm multiple times rapidly
fireEvent.click(removeButton); const confirm = screen.getByTestId('modal-confirm-button');
fireEvent.click(confirm);
fireEvent.click(confirm);
fireEvent.click(confirm);
// Verify that removeModule was called multiple times (as expected) // Verify that removeModule was called multiple times (as expected)
expect(mockRemoveModule).toHaveBeenCalledTimes(3); expect(mockRemoveModule).toHaveBeenCalledTimes(3);