From 95c6a1225d08c1c66c4807ffc4763cb48c1cb7b3 Mon Sep 17 00:00:00 2001 From: Jerry Jiang Date: Tue, 8 Oct 2024 15:11:03 -0500 Subject: [PATCH 1/2] Revert "chore(content-explorer): migrate renameDialog (#3666)" This reverts commit f5f1bac2eb2b750f748fdf1d8ad6901cb2f7042b. --- i18n/en-US.properties | 6 - scripts/jest/jest-setup.ts | 14 -- src/elements/common/messages.js | 15 -- .../content-explorer/ContentExplorer.js | 1 + .../{RenameDialog.js.flow => RenameDialog.js} | 2 + .../content-explorer/RenameDialog.scss | 6 - .../content-explorer/RenameDialog.tsx | 130 ------------------ .../__tests__/RenameDialog.test.tsx | 115 ---------------- .../stories/RenameDialog.stories.tsx | 51 ------- .../tests/RenameDialog-visual.stories.js | 23 ++-- src/utils/storybook.js | 2 +- 11 files changed, 18 insertions(+), 347 deletions(-) rename src/elements/content-explorer/{RenameDialog.js.flow => RenameDialog.js} (98%) delete mode 100644 src/elements/content-explorer/RenameDialog.scss delete mode 100644 src/elements/content-explorer/RenameDialog.tsx delete mode 100644 src/elements/content-explorer/__tests__/RenameDialog.test.tsx delete mode 100644 src/elements/content-explorer/stories/RenameDialog.stories.tsx diff --git a/i18n/en-US.properties b/i18n/en-US.properties index 95f9d576da..61faa80f99 100644 --- a/i18n/en-US.properties +++ b/i18n/en-US.properties @@ -592,16 +592,10 @@ be.renameDialogErrorInUse = An item with the same name already exists. be.renameDialogErrorInvalid = This name is invalid. # Error text for rename dialog when name is too long be.renameDialogErrorTooLong = This name is too long. -# Header for rename file dialog -be.renameDialogFileHeader = Rename File -# Header for rename folder dialog -be.renameDialogFolderHeader = Rename Folder # Label for rename dialog be.renameDialogLabel = Rename # Text for rename dialog be.renameDialogText = Please enter a new name for {name}: -# Header for rename web link dialog -be.renameDialogWebLinkHeader = Rename Link # Label for resume action for a single file. be.resume = Resume # Label for resume action for multiple files. diff --git a/scripts/jest/jest-setup.ts b/scripts/jest/jest-setup.ts index a6f4614737..d5b25dbc2d 100644 --- a/scripts/jest/jest-setup.ts +++ b/scripts/jest/jest-setup.ts @@ -2,20 +2,6 @@ import '@testing-library/jest-dom'; import util from 'util'; -Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation(query => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), // Deprecated - removeListener: jest.fn(), // Deprecated - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })), -}); - global.setImmediate = cb => { setTimeout(cb, 0); }; diff --git a/src/elements/common/messages.js b/src/elements/common/messages.js index 329eb1b589..950672d4e6 100644 --- a/src/elements/common/messages.js +++ b/src/elements/common/messages.js @@ -317,21 +317,6 @@ const messages = defineMessages({ description: 'Error text for rename dialog when name is too long', defaultMessage: 'This name is too long.', }, - renameDialogFileHeader: { - id: 'be.renameDialogFileHeader', - description: 'Header for rename file dialog', - defaultMessage: 'Rename File', - }, - renameDialogFolderHeader: { - id: 'be.renameDialogFolderHeader', - description: 'Header for rename folder dialog', - defaultMessage: 'Rename Folder', - }, - renameDialogWebLinkHeader: { - id: 'be.renameDialogWebLinkHeader', - description: 'Header for rename web link dialog', - defaultMessage: 'Rename Link', - }, createDialogLabel: { id: 'be.createDialogLabel', description: 'Label for create folder dialog', diff --git a/src/elements/content-explorer/ContentExplorer.js b/src/elements/content-explorer/ContentExplorer.js index 5ed3df003d..7983e76e47 100644 --- a/src/elements/content-explorer/ContentExplorer.js +++ b/src/elements/content-explorer/ContentExplorer.js @@ -1783,6 +1783,7 @@ class ContentExplorer extends Component { isLoading={isLoading} errorCode={errorCode} parentElement={this.rootElement} + appElement={this.appElement} /> ) : null} {canShare && selected && !!this.appElement ? ( diff --git a/src/elements/content-explorer/RenameDialog.js.flow b/src/elements/content-explorer/RenameDialog.js similarity index 98% rename from src/elements/content-explorer/RenameDialog.js.flow rename to src/elements/content-explorer/RenameDialog.js index d72f573935..1ae780c58c 100644 --- a/src/elements/content-explorer/RenameDialog.js.flow +++ b/src/elements/content-explorer/RenameDialog.js @@ -21,6 +21,7 @@ import { import type { BoxItem } from '../../common/types/core'; type Props = { + appElement: HTMLElement, errorCode: string, intl: IntlShape, isLoading: boolean, @@ -101,6 +102,7 @@ const RenameDialog = ({ return ( void; - onRename: (nameWithoutExt: string, extension: string) => void; - parentElement: HTMLElement; -} - -const RenameDialog = ({ errorCode, isLoading, isOpen, item, onCancel, onRename, parentElement }: RenameDialogProps) => { - const { formatMessage } = useIntl(); - - let textInput = null; - let error; - - const { name = '', extension, type } = item; - const ext = extension ? `.${extension}` : ''; - const nameWithoutExt = extension ? name.replace(ext, '') : name; - - const headerMessages = { - [TYPE_FILE]: messages.renameDialogFileHeader, - [TYPE_FOLDER]: messages.renameDialogFolderHeader, - [TYPE_WEBLINK]: messages.renameDialogWebLinkHeader, - }; - - /** - * Appends the extension and calls rename function - */ - const rename = () => { - if (textInput && textInput.value) { - if (textInput.value === nameWithoutExt) { - onCancel(); - } else { - onRename(textInput.value, ext); - } - } - }; - - /** - * Grabs reference to the input element - */ - const ref = input => { - textInput = input; - if (textInput instanceof HTMLInputElement) { - textInput.focus(); - textInput.select(); - } - }; - - /** - * Handles enter key down - */ - const onKeyDown = ({ key }) => { - switch (key) { - case 'Enter': - rename(); - break; - default: - break; - } - }; - - switch (errorCode) { - case ERROR_CODE_ITEM_NAME_IN_USE: - error = messages.renameDialogErrorInUse; - break; - case ERROR_CODE_ITEM_NAME_TOO_LONG: - error = messages.renameDialogErrorTooLong; - break; - default: - error = errorCode ? messages.renameDialogErrorInvalid : null; - break; - } - - return ( - - - {formatMessage(headerMessages[type])} - - - - - - {formatMessage(messages.cancel)} - - - {formatMessage(messages.rename)} - - - - - - ); -}; - -export default RenameDialog; diff --git a/src/elements/content-explorer/__tests__/RenameDialog.test.tsx b/src/elements/content-explorer/__tests__/RenameDialog.test.tsx deleted file mode 100644 index 8e7b784e52..0000000000 --- a/src/elements/content-explorer/__tests__/RenameDialog.test.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import * as React from 'react'; -import userEvent from '@testing-library/user-event'; -import { render, screen } from '../../../test-utils/testing-library'; - -import RenameDialog, { RenameDialogProps } from '../RenameDialog'; - -describe('elements/content-explorer/RenameDialog', () => { - const mockItem = { - id: '123456', - name: 'mockFile', - extension: 'txt', - type: 'file', - }; - - const defaultProps = { - errorCode: '', - isLoading: false, - isOpen: false, - item: mockItem, - onCancel: jest.fn(), - onRename: jest.fn(), - parentElement: document.body, - }; - - const renderComponent = (props: Partial) => - render(); - - test('render rename dialog correctly when it is open and not loading', () => { - renderComponent({ isOpen: true, item: mockItem }); - - expect(screen.getByText('Rename File')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Rename' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument(); - - const textInput = screen.getByLabelText('Name'); - expect(textInput).toBeInTheDocument(); - expect(textInput).toHaveValue('mockFile'); - }); - - test('render dialog footer correctly when it is open and loading', () => { - renderComponent({ isOpen: true, isLoading: true }); - - const cancelButton = screen.getByRole('button', { name: 'Cancel' }); - expect(cancelButton).toBeInTheDocument(); - expect(cancelButton).toBeDisabled(); - - const loadingIndicator = screen.getByRole('status', { name: 'Loading' }); - expect(loadingIndicator).toBeInTheDocument(); - - const renameButton = screen.queryByRole('button', { name: 'Rename' }); - expect(renameButton).not.toBeInTheDocument(); - }); - - test('call onRename with input value and extension when rename button is clicked', async () => { - const mockRenameFunction = jest.fn(); - renderComponent({ isOpen: true, item: mockItem, onRename: mockRenameFunction }); - - const textInput = screen.getByLabelText('Name'); - await userEvent.clear(textInput); - await userEvent.type(textInput, 'newFileName'); - await userEvent.click(screen.getByRole('button', { name: 'Rename' })); - - expect(mockRenameFunction).toHaveBeenCalledTimes(1); - expect(mockRenameFunction).toHaveBeenCalledWith('newFileName', '.txt'); - }); - - test('call onRename with input value and extension when enter key is pressed', async () => { - const mockRenameFunction = jest.fn(); - renderComponent({ isOpen: true, item: mockItem, onRename: mockRenameFunction }); - - const textInput = screen.getByLabelText('Name'); - await userEvent.clear(textInput); - await userEvent.type(textInput, 'newFileName'); - await userEvent.keyboard('{Enter}'); - - expect(mockRenameFunction).toHaveBeenCalledTimes(1); - expect(mockRenameFunction).toHaveBeenCalledWith('newFileName', '.txt'); - }); - - test('call onCancel when cancel button is clicked', async () => { - const mockCancelFunction = jest.fn(); - renderComponent({ isOpen: true, onCancel: mockCancelFunction }); - - await userEvent.click(screen.getByRole('button', { name: 'Cancel' })); - expect(mockCancelFunction).toHaveBeenCalledTimes(1); - }); - - test('call onCancel when close icon is clicked', async () => { - const mockCancelFunction = jest.fn(); - renderComponent({ isOpen: true, onCancel: mockCancelFunction }); - - await userEvent.click(screen.getByRole('button', { name: 'Close' })); - expect(mockCancelFunction).toHaveBeenCalledTimes(1); - }); - - test('call onCancel when text value is not changed and rename button is clicked', async () => { - const mockCancelFunction = jest.fn(); - renderComponent({ isOpen: true, onCancel: mockCancelFunction }); - - await userEvent.click(screen.getByRole('button', { name: 'Rename' })); - expect(mockCancelFunction).toHaveBeenCalledTimes(1); - }); - - test.each` - errorCode | expectedError - ${'item_name_in_use'} | ${'An item with the same name already exists.'} - ${'item_name_too_long'} | ${'This name is too long.'} - ${'default'} | ${'This name is invalid.'} - `('render correct error message based on errorCode', ({ errorCode, expectedError }) => { - renderComponent({ isOpen: true, errorCode }); - - expect(screen.getByText(expectedError)).toBeInTheDocument(); - }); -}); diff --git a/src/elements/content-explorer/stories/RenameDialog.stories.tsx b/src/elements/content-explorer/stories/RenameDialog.stories.tsx deleted file mode 100644 index 6fc08fe994..0000000000 --- a/src/elements/content-explorer/stories/RenameDialog.stories.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import * as React from 'react'; -import { useArgs } from '@storybook/preview-api'; - -import { Button } from '@box/blueprint-web'; -import { addRootElement } from '../../../utils/storybook'; - -import RenameDialog from '../RenameDialog'; - -export const renameDialog = { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - render: (args: any) => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const [, setArgs] = useArgs(); - - const handleOpenModal = () => setArgs({ isOpen: true }); - - const handleCloseModal = () => { - setArgs({ isOpen: false }); - }; - - const { rootElement } = addRootElement(); - - return ( -
- - - -
- ); - }, -}; - -export default { - title: 'Elements/ContentExplorer', - component: RenameDialog, - args: { - isLoading: false, - isOpen: false, - }, -}; diff --git a/src/elements/content-explorer/stories/tests/RenameDialog-visual.stories.js b/src/elements/content-explorer/stories/tests/RenameDialog-visual.stories.js index fbb97f42b5..06721da6ca 100644 --- a/src/elements/content-explorer/stories/tests/RenameDialog-visual.stories.js +++ b/src/elements/content-explorer/stories/tests/RenameDialog-visual.stories.js @@ -9,40 +9,43 @@ import { ERROR_CODE_ITEM_NAME_TOO_LONG, } from '../../../../constants'; +import '../../../common/modal.scss'; + const item = { id: '123456', name: 'mockItem', - type: 'file', }; const itemWithLongName = { 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 ', - type: 'file', }; export const renameDialogNotLoading = { render: () => { - const { rootElement } = addRootElement(); + const { appElement, rootElement } = addRootElement(); - return ; + return ( + + ); }, }; export const renameDialogIsLoading = { render: () => { - const { rootElement } = addRootElement(); + const { appElement, rootElement } = addRootElement(); - return ; + return ; }, }; export const renameDialogNameInvalidError = { render: () => { - const { rootElement } = addRootElement(); + const { appElement, rootElement } = addRootElement(); return ( { - const { rootElement } = addRootElement(); + const { appElement, rootElement } = addRootElement(); return ( { - const { rootElement } = addRootElement(); + const { appElement, rootElement } = addRootElement(); return ( { - let appElement = document.getElementById('appElement'); + let appElement = document.getElementById('rootElement'); let rootElement = document.getElementById('rootElement'); if (document.body && rootElement === null) { From c08db4bb8641d0d8c3bbcfe43cb447cfb639e7cc Mon Sep 17 00:00:00 2001 From: Jerry Jiang Date: Tue, 8 Oct 2024 15:55:40 -0500 Subject: [PATCH 2/2] chore(content-explorer): keep changes depended by other elements --- scripts/jest/jest-setup.ts | 14 ++++++++++++++ .../tests/ContentExplorer-visual.stories.js | 6 +++--- src/utils/storybook.js | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/scripts/jest/jest-setup.ts b/scripts/jest/jest-setup.ts index d5b25dbc2d..a6f4614737 100644 --- a/scripts/jest/jest-setup.ts +++ b/scripts/jest/jest-setup.ts @@ -2,6 +2,20 @@ import '@testing-library/jest-dom'; import util from 'util'; +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + global.setImmediate = cb => { setTimeout(cb, 0); }; diff --git a/src/elements/content-explorer/stories/tests/ContentExplorer-visual.stories.js b/src/elements/content-explorer/stories/tests/ContentExplorer-visual.stories.js index 1cdd99410f..178b5d94cb 100644 --- a/src/elements/content-explorer/stories/tests/ContentExplorer-visual.stories.js +++ b/src/elements/content-explorer/stories/tests/ContentExplorer-visual.stories.js @@ -81,7 +81,7 @@ export const openRenameDialog = { expect(renameButton).toBeInTheDocument(); await userEvent.click(renameButton); - expect(await screen.findByText('Rename File')).toBeInTheDocument(); + expect(await screen.findByText('Please enter a new name for Book Sample:')).toBeInTheDocument(); }, }; @@ -97,12 +97,12 @@ export const closeRenameDialog = { expect(renameButton).toBeInTheDocument(); await userEvent.click(renameButton); - expect(await screen.findByText('Rename File')).toBeInTheDocument(); + expect(await screen.findByText('Please enter a new name for Book Sample:')).toBeInTheDocument(); const cancelButton = screen.getByText('Cancel'); await userEvent.click(cancelButton); await waitFor(() => { - expect(screen.queryByText('Rename File')).not.toBeInTheDocument(); + expect(screen.queryByText('Please enter a new name for Book Sample:')).not.toBeInTheDocument(); }); }, }; diff --git a/src/utils/storybook.js b/src/utils/storybook.js index b7c26c34e2..4da818c4f8 100644 --- a/src/utils/storybook.js +++ b/src/utils/storybook.js @@ -1,6 +1,6 @@ // eslint-disable-next-line import/prefer-default-export export const addRootElement = () => { - let appElement = document.getElementById('rootElement'); + let appElement = document.getElementById('appElement'); let rootElement = document.getElementById('rootElement'); if (document.body && rootElement === null) {