-
Notifications
You must be signed in to change notification settings - Fork 314
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(content-explorer): Migrate Create New Folder (#3896)
- Loading branch information
1 parent
ec98688
commit 4e0712b
Showing
8 changed files
with
242 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
src/elements/common/create-folder-dialog/CreateFolderDialog.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import React, { useState } from 'react'; | ||
import Modal from 'react-modal'; | ||
import { useIntl } from 'react-intl'; | ||
import { Modal as BlueprintModal, TextInput } from '@box/blueprint-web'; | ||
import { | ||
CLASS_MODAL_CONTENT, | ||
CLASS_MODAL_OVERLAY, | ||
CLASS_MODAL, | ||
ERROR_CODE_ITEM_NAME_TOO_LONG, | ||
ERROR_CODE_ITEM_NAME_IN_USE, | ||
} from '../../../constants'; | ||
|
||
import messages from '../messages'; | ||
|
||
export interface CreateFolderDialogProps { | ||
appElement: HTMLElement; | ||
errorCode: string; | ||
isLoading: boolean; | ||
isOpen: boolean; | ||
onCancel: () => void; | ||
onCreate: (value: string) => void; | ||
parentElement: HTMLElement; | ||
} | ||
|
||
const CreateFolderDialog = ({ | ||
appElement, | ||
errorCode, | ||
isOpen, | ||
isLoading, | ||
onCancel, | ||
onCreate, | ||
parentElement, | ||
}: CreateFolderDialogProps) => { | ||
const { formatMessage } = useIntl(); | ||
const [value, setValue] = useState(''); | ||
let error; | ||
|
||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
setValue(e.target.value); | ||
}; | ||
|
||
const handleCreate = () => { | ||
if (value) { | ||
onCreate(value); | ||
} | ||
}; | ||
|
||
const handleKeyDown = ({ key }) => { | ||
switch (key) { | ||
case 'Enter': | ||
handleCreate(); | ||
break; | ||
default: | ||
break; | ||
} | ||
}; | ||
|
||
switch (errorCode) { | ||
case ERROR_CODE_ITEM_NAME_IN_USE: | ||
error = formatMessage(messages.createDialogErrorInUse); | ||
break; | ||
case ERROR_CODE_ITEM_NAME_TOO_LONG: | ||
error = formatMessage(messages.createDialogErrorTooLong); | ||
break; | ||
default: | ||
error = errorCode ? formatMessage(messages.createDialogErrorInvalid) : null; | ||
break; | ||
} | ||
|
||
return ( | ||
<Modal | ||
appElement={appElement} | ||
className={CLASS_MODAL_CONTENT} | ||
contentLabel={formatMessage(messages.createDialogLabel)} | ||
isOpen={isOpen} | ||
onRequestClose={onCancel} | ||
overlayClassName={CLASS_MODAL_OVERLAY} | ||
parentSelector={() => parentElement} | ||
portalClassName={`${CLASS_MODAL} be-modal-create-folder`} | ||
> | ||
<BlueprintModal.Body> | ||
<TextInput | ||
autoFocus | ||
error={error} | ||
label={formatMessage(messages.createDialogText)} | ||
onChange={handleChange} | ||
onKeyDown={handleKeyDown} | ||
required | ||
value={value} | ||
/> | ||
</BlueprintModal.Body> | ||
<BlueprintModal.Footer> | ||
<BlueprintModal.Footer.SecondaryButton disabled={isLoading} onClick={onCancel} size="large"> | ||
{formatMessage(messages.cancel)} | ||
</BlueprintModal.Footer.SecondaryButton> | ||
<BlueprintModal.Footer.PrimaryButton | ||
loading={isLoading} | ||
loadingAriaLabel={formatMessage(messages.loading)} | ||
onClick={handleCreate} | ||
size="large" | ||
> | ||
{formatMessage(messages.create)} | ||
</BlueprintModal.Footer.PrimaryButton> | ||
</BlueprintModal.Footer> | ||
</Modal> | ||
); | ||
}; | ||
|
||
export default CreateFolderDialog; |
88 changes: 88 additions & 0 deletions
88
src/elements/common/create-folder-dialog/__tests__/CreateFolderDialog.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import * as React from 'react'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
import { render, screen } from '../../../../test-utils/testing-library'; | ||
import CreateFolderDialog, { CreateFolderDialogProps } from '../CreateFolderDialog'; | ||
import { ERROR_CODE_ITEM_NAME_TOO_LONG, ERROR_CODE_ITEM_NAME_IN_USE } from '../../../../constants'; | ||
|
||
jest.mock('react-modal', () => { | ||
return jest.fn(({ children }) => <div>{children}</div>); | ||
}); | ||
|
||
const defaultProps = { | ||
appElement: document.createElement('div'), | ||
errorCode: '', | ||
isLoading: false, | ||
isOpen: true, | ||
onCancel: jest.fn(), | ||
onCreate: jest.fn(), | ||
parentElement: document.createElement('div'), | ||
}; | ||
|
||
describe('elements/common/create-folder-dialog/CreateFolderDialog', () => { | ||
const renderComponent = (props: Partial<CreateFolderDialogProps> = {}) => | ||
render(<CreateFolderDialog {...defaultProps} {...props} />); | ||
|
||
test('renders the dialog with the correct initial state', () => { | ||
renderComponent(); | ||
|
||
expect(screen.getByText('Please enter a name.')).toBeInTheDocument(); | ||
expect(screen.getByRole('textbox')).toBeEmptyDOMElement(); | ||
expect(screen.getByRole('button', { name: 'Create' })).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); | ||
}); | ||
|
||
test('calls onCancel when cancel button is clicked', async () => { | ||
const onCancel = jest.fn(); | ||
|
||
renderComponent({ onCancel }); | ||
await userEvent.click(screen.getByRole('button', { name: 'Cancel' })); | ||
expect(onCancel).toHaveBeenCalled(); | ||
}); | ||
|
||
test('calls onCreate with the correct values when create button is clicked', async () => { | ||
const onCreate = jest.fn(); | ||
|
||
renderComponent({ onCreate }); | ||
const input = screen.getByRole('textbox'); | ||
await userEvent.clear(input); | ||
await userEvent.type(input, 'newname'); | ||
await userEvent.click(screen.getByRole('button', { name: 'Create' })); | ||
expect(onCreate).toHaveBeenCalledWith('newname'); | ||
}); | ||
|
||
test('displays an error message when errorCode is neither ERROR_CODE_ITEM_NAME_IN_USE nor ERROR_CODE_ITEM_NAME_TOO_LONG', () => { | ||
renderComponent({ errorCode: 'bad' }); | ||
expect(screen.getByText('This is an invalid folder name.')).toBeInTheDocument(); | ||
}); | ||
|
||
test('displays an error message when errorCode is ERROR_CODE_ITEM_NAME_IN_USE', () => { | ||
renderComponent({ errorCode: ERROR_CODE_ITEM_NAME_IN_USE }); | ||
expect(screen.getByText('A folder with the same name already exists.')).toBeInTheDocument(); | ||
}); | ||
|
||
test('displays an error message when errorCode is ERROR_CODE_ITEM_NAME_TOO_LONG', () => { | ||
renderComponent({ errorCode: ERROR_CODE_ITEM_NAME_TOO_LONG }); | ||
expect(screen.getByText('This folder name is too long.')).toBeInTheDocument(); | ||
}); | ||
|
||
test('does not call onCreate if the name has not changed', async () => { | ||
const onCancel = jest.fn(); | ||
const onCreate = jest.fn(); | ||
|
||
renderComponent({ onCancel, onCreate }); | ||
await userEvent.click(screen.getByText('Create')); | ||
expect(onCreate).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test('calls handleOnCreate on Enter key press', async () => { | ||
const onCreate = jest.fn(); | ||
|
||
renderComponent({ onCreate }); | ||
const input = screen.getByRole('textbox'); | ||
await userEvent.clear(input); | ||
await userEvent.type(input, 'newname'); | ||
await userEvent.type(input, '{enter}'); | ||
expect(onCreate).toHaveBeenCalledWith('newname'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export {default} from './CreateFolderDialog'; |
1 change: 0 additions & 1 deletion
1
...ents/common/create-folder-dialog/index.js → ...ents/common/create-folder-dialog/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1 @@ | ||
// @flow | ||
export { default } from './CreateFolderDialog'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters