Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(content-explorer): Migrate deleteConfirmationDialog #3696

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
README.md
flow-typed/*
1 change: 1 addition & 0 deletions flow-typed/npm/react-intl_v2.x.x.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,4 +260,5 @@ declare module "react-intl" {
> {}
declare type IntlShape = $npm$ReactIntl$IntlShape;
declare type MessageDescriptor = $npm$ReactIntl$MessageDescriptor;
declare function useIntl(): $npm$ReactIntl$IntlShape;
}
2 changes: 2 additions & 0 deletions i18n/en-US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,8 @@ be.delete = Delete
be.deleteDialogFileText = Are you sure you want to delete {name}?
# Text for delete confirmation dialog for folders
be.deleteDialogFolderText = Are you sure you want to delete {name} and all its contents?
# Header for delete confirmation dialog
be.deleteDialogHeader = Delete Item
# Label for delete confirmation dialog
be.deleteDialogLabel = Confirm Delete
# Label for the description field in the preview sidebar.
Expand Down
5 changes: 5 additions & 0 deletions src/elements/common/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ const messages = defineMessages({
description: 'Label for item size attribute.',
defaultMessage: 'Size',
},
deleteDialogHeader: {
id: 'be.deleteDialogHeader',
description: 'Header for delete confirmation dialog',
defaultMessage: 'Delete Item',
},
deleteDialogLabel: {
id: 'be.deleteDialogLabel',
description: 'Label for delete confirmation dialog',
Expand Down
65 changes: 0 additions & 65 deletions src/elements/content-explorer/DeleteConfirmationDialog.js

This file was deleted.

51 changes: 51 additions & 0 deletions src/elements/content-explorer/DeleteConfirmationDialog.js.flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// @flow
import * as React from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { Modal } from '@box/blueprint-web';
import type { BoxItem } from '../../common/types/core';

import { TYPE_FOLDER } from '../../constants';

import messages from '../common/messages';

type Props = {
isLoading: boolean,
isOpen: boolean,
item: BoxItem,
onCancel: any,
onDelete: any,
parentElement: HTMLElement,
};

const DeleteConfirmationDialog = ({ isOpen, onDelete, onCancel, item, isLoading, parentElement }: Props) => {
const { formatMessage } = useIntl();
const message = item.type === TYPE_FOLDER ? messages.deleteDialogFolderText : messages.deleteDialogFileText;
return (
<Modal onOpenChange={onCancel} open={isOpen}>
<Modal.Content
aria-label={formatMessage(messages.deleteDialogLabel)}
className="bce-DeleteConfirmationDialog"
container={parentElement}
size="small"
>
<Modal.Body>
<FormattedMessage {...message} values={{ name: item.name }} />
</Modal.Body>
<Modal.Footer>
<Modal.Footer.SecondaryButton disabled={isLoading} onClick={onCancel}>
{formatMessage(messages.cancel)}
</Modal.Footer.SecondaryButton>
<Modal.Footer.PrimaryButton
loading={isLoading}
loadingAriaLabel={formatMessage(messages.loading)}
onClick={onDelete}
>
{formatMessage(messages.delete)}
</Modal.Footer.PrimaryButton>
</Modal.Footer>
</Modal.Content>
</Modal>
);
};

export default DeleteConfirmationDialog;
5 changes: 0 additions & 5 deletions src/elements/content-explorer/DeleteConfirmationDialog.scss

This file was deleted.

58 changes: 58 additions & 0 deletions src/elements/content-explorer/DeleteConfirmationDialog.tsx
greg-in-a-box marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when the modal is opened, what is the element that is focused?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

close is focused, confirmed by hitting enter and a console.log after its open

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as React from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { Modal } from '@box/blueprint-web';
import type { BoxItem } from '../../common/types/core';

import { TYPE_FOLDER } from '../../constants';

import messages from '../common/messages';

export interface DeleteConfirmationDialogProps {
isLoading: boolean;
isOpen: boolean;
item: BoxItem;
onCancel: () => void;
onDelete: () => void;
parentElement: HTMLElement;
}

const DeleteConfirmationDialog = ({
isLoading,
isOpen,
item,
onCancel,
onDelete,
parentElement,
}: DeleteConfirmationDialogProps) => {
const { formatMessage } = useIntl();
const message = item.type === TYPE_FOLDER ? messages.deleteDialogFolderText : messages.deleteDialogFileText;
return (
<Modal onOpenChange={onCancel} open={isOpen}>
<Modal.Content
aria-label={formatMessage(messages.deleteDialogLabel)}
container={parentElement}
size="small"
>
<Modal.Header>{formatMessage(messages.deleteDialogHeader)}</Modal.Header>
<Modal.Body>
<FormattedMessage {...message} values={{ name: item.name }} />
</Modal.Body>
<Modal.Footer>
<Modal.Footer.SecondaryButton disabled={isLoading} onClick={onCancel}>
{formatMessage(messages.cancel)}
</Modal.Footer.SecondaryButton>
<Modal.Footer.PrimaryButton
loading={isLoading}
loadingAriaLabel={formatMessage(messages.loading)}
onClick={onDelete}
>
{formatMessage(messages.delete)}
</Modal.Footer.PrimaryButton>
</Modal.Footer>
<Modal.Close aria-label={formatMessage(messages.close)} />
</Modal.Content>
</Modal>
);
};

export default DeleteConfirmationDialog;
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import * as React from 'react';
import userEvent from '@testing-library/user-event';
import { render, screen } from '../../../test-utils/testing-library';
import DeleteConfirmationDialog, { DeleteConfirmationDialogProps } from '../DeleteConfirmationDialog';

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { TYPE_FOLDER } from '../../../constants';

const mockItem = { type: 'pdf', name: 'Test File' };
const mockFolderItem = { type: TYPE_FOLDER, name: 'Test Folder' };
const mockOnCancel = jest.fn();
const mockOnDelete = jest.fn();

const defaultProps = {
errorCode: '',
isLoading: false,
isOpen: false,
item: mockItem,
onCancel: jest.fn(),
onDelete: jest.fn(),
parentElement: document.body,
};

const renderComponent = (props: Partial<DeleteConfirmationDialogProps>) =>
render(<DeleteConfirmationDialog {...defaultProps} {...props} />);

describe('elements/content-explorer/DeleteConfirmationDialog', () => {
test('should render the dialog with file message', async () => {
renderComponent({ isOpen: true });

expect(await screen.findByText('Are you sure you want to delete Test File?')).toBeInTheDocument();
});

test('should render the dialog with folder message', async () => {
renderComponent({ isOpen: true, item: mockFolderItem });

expect(
await screen.findByText('Are you sure you want to delete Test Folder and all its contents?'),
).toBeInTheDocument();
});

test('should call onCancel when cancel button is clicked', async () => {
renderComponent({ isOpen: true, onCancel: mockOnCancel });

const cancelButton = screen.getByRole('button', { name: 'Cancel' });
await userEvent.click(cancelButton);
expect(mockOnCancel).toBeCalledTimes(1);
});

test('should call onDelete when delete button is clicked', async () => {
renderComponent({ isOpen: true, onDelete: mockOnDelete });

const deleteButton = screen.getByRole('button', { name: 'Delete' });
await userEvent.click(deleteButton);
expect(mockOnDelete).toBeCalledTimes(1);
});

test('should disable buttons when isLoading is true', () => {
renderComponent({ isOpen: true, isLoading: true });

const cancelButton = screen.getByRole('button', { name: 'Cancel' });
expect(cancelButton).toBeDisabled();
const loadingIndicator = screen.getByRole('status', { name: 'Loading' });
expect(loadingIndicator).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
// @flow
import * as React from 'react';
import { useArgs } from '@storybook/preview-api';

import PrimaryButton from '../../../components/primary-button/PrimaryButton';
import { Button } from '@box/blueprint-web';
import { addRootElement } from '../../../utils/storybook';

import DeleteConfirmationDialog from '../DeleteConfirmationDialog';

// need to import this into the story because it's usually in ContentExplorer
import '../../common/modal.scss';

export const deleteDialog = {
// eslint-disable-next-line react/prop-types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
render: (args: any) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [, setArgs] = useArgs();
Expand All @@ -22,27 +18,26 @@ export const deleteDialog = {
setArgs({ isOpen: false });
};

const { appElement, rootElement } = addRootElement();
const { rootElement } = addRootElement();

return (
<div>
<DeleteConfirmationDialog
appElement={appElement}
item={{
id: '123456',
name:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Eget nulla facilisi etiam dignissim diam quis enim lobortis scelerisque. Aliquam faucibus purus in massa tempor nec. Ut consequat semper viverra nam libero justo laoreet sit amet. Purus gravida quis blandit turpis cursus in hac. Dui ut ornare lectus sit amet est. Nisl condimentum id venenatis a condimentum vitae sapien.',
name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Eget nulla facilisi etiam dignissim diam quis enim lobortis scelerisque. Aliquam faucibus purus in massa tempor nec. Ut consequat semper viverra nam libero justo laoreet sit amet. Purus gravida quis blandit turpis cursus in hac. Dui ut ornare lectus sit amet est. Nisl condimentum id venenatis a condimentum vitae sapien.',
}}
onCancel={handleCloseModal}
parentElement={rootElement}
{...args}
/>

<PrimaryButton onClick={handleOpenModal}>Launch DeleteConfirmationDialog</PrimaryButton>
<Button onClick={handleOpenModal} variant="primary">
Launch DeleteConfirmationDialog
</Button>
</div>
);
},
};
} as const;

export default {
title: 'Elements/ContentExplorer',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const openDeleteConfirmationDialog = {
await userEvent.click(moreOptionsButton);

const dropdown = await screen.findByRole('menu');
const deleteButton = within(dropdown).getByText('Delete');
const deleteButton = within(dropdown).findByText('Delete');
expect(deleteButton).toBeInTheDocument();
await userEvent.click(deleteButton);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,24 @@ import { addRootElement, defaultVisualConfig } from '../../../../utils/storybook

import DeleteConfirmationDialog from '../../DeleteConfirmationDialog';

// need to import this into the story because it's usually in ContentExplorer
import '../../../common/modal.scss';

const item = {
id: '123456',
name:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Eget nulla facilisi etiam dignissim diam quis enim lobortis scelerisque. Aliquam faucibus purus in massa tempor nec. Ut consequat semper viverra nam libero justo laoreet sit amet. Purus gravida quis blandit turpis cursus in hac. Dui ut ornare lectus sit amet est. Nisl condimentum id venenatis a condimentum vitae sapien ',
name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Eget nulla facilisi etiam dignissim diam quis enim lobortis scelerisque. Aliquam faucibus purus in massa tempor nec. Ut consequat semper viverra nam libero justo laoreet sit amet. Purus gravida quis blandit turpis cursus in hac. Dui ut ornare lectus sit amet est. Nisl condimentum id venenatis a condimentum vitae sapien ',
};

export const deleteDialogNotLoading = {
render: () => {
const { appElement, rootElement } = addRootElement();

return (
<DeleteConfirmationDialog
appElement={appElement}
isLoading={false}
isOpen
item={item}
parentElement={rootElement}
/>
);
const { rootElement } = addRootElement();

return <DeleteConfirmationDialog isLoading={false} isOpen item={item} parentElement={rootElement} />;
},
};

export const deleteDialogIsLoading = {
render: () => {
const { appElement, rootElement } = addRootElement();

return (
<DeleteConfirmationDialog
appElement={appElement}
isLoading
isOpen
item={item}
parentElement={rootElement}
/>
);
const { rootElement } = addRootElement();

return <DeleteConfirmationDialog isLoading isOpen item={item} parentElement={rootElement} />;
},
};

Expand Down
Loading