diff --git a/src/elements/common/modal.scss b/src/elements/common/modal.scss index 2af639a7bd..8c922c8b91 100644 --- a/src/elements/common/modal.scss +++ b/src/elements/common/modal.scss @@ -44,12 +44,19 @@ background-color: $darker-black; } +.be-modal-rename .be-modal-dialog-content, .be-modal-share .be-modal-dialog-content, .be-modal-delete .be-modal-dialog-content { padding: 0; border-radius: tokens.$radius-4; } +.be-modal-rename .be-modal-dialog-content { + input { + width: 100%; + } +} + .be-modal-dialog-content { @extend .be-modal-wrapper-content; diff --git a/src/elements/content-explorer/RenameDialog.js b/src/elements/content-explorer/RenameDialog.js.flow similarity index 60% rename from src/elements/content-explorer/RenameDialog.js rename to src/elements/content-explorer/RenameDialog.js.flow index 1ae780c58c..4bf89189e7 100644 --- a/src/elements/content-explorer/RenameDialog.js +++ b/src/elements/content-explorer/RenameDialog.js.flow @@ -1,16 +1,8 @@ -/** - * @flow - * @file Content Explorer Rename Dialog - * @author Box - */ - import * as React from 'react'; import Modal from 'react-modal'; -import { injectIntl, FormattedMessage } from 'react-intl'; -import type { IntlShape } from 'react-intl'; -import PrimaryButton from '../../components/primary-button/PrimaryButton'; -import Button from '../../components/button/Button'; -import messages from '../common/messages'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { Modal as BlueprintModal, Text } from '@box/blueprint-web'; + import { CLASS_MODAL_CONTENT, CLASS_MODAL_OVERLAY, @@ -20,30 +12,30 @@ import { } from '../../constants'; import type { BoxItem } from '../../common/types/core'; +import messages from '../common/messages'; + type Props = { appElement: HTMLElement, errorCode: string, - intl: IntlShape, isLoading: boolean, isOpen: boolean, item: BoxItem, - onCancel: Function, - onRename: Function, - parentElement: HTMLElement, + onCancel: any, + onRename: any, + parentElement: HTMLElement }; -/* eslint-disable jsx-a11y/label-has-for */ const RenameDialog = ({ + appElement, + errorCode, isOpen, - onRename, - onCancel, - item, isLoading, - errorCode, + item, + onCancel, + onRename, parentElement, - appElement, - intl, }: Props) => { + const { formatMessage } = useIntl(); let textInput = null; let error; @@ -54,7 +46,7 @@ const RenameDialog = ({ /** * Appends the extension and calls rename function */ - const rename = () => { + const handleRename = () => { if (textInput && textInput.value) { if (textInput.value === nameWithoutExt) { onCancel(); @@ -81,7 +73,7 @@ const RenameDialog = ({ const onKeyDown = ({ key }) => { switch (key) { case 'Enter': - rename(); + handleRename(); break; default: break; @@ -104,32 +96,41 @@ const RenameDialog = ({ parentElement} portalClassName={`${CLASS_MODAL} be-modal-rename`} > - -
- - - - -
+ + + + {formatMessage(messages.cancel)} + + + {formatMessage(messages.rename)} + +
); }; -export default injectIntl(RenameDialog); +export default RenameDialog; diff --git a/src/elements/content-explorer/RenameDialog.tsx b/src/elements/content-explorer/RenameDialog.tsx new file mode 100644 index 0000000000..575bfbb1e1 --- /dev/null +++ b/src/elements/content-explorer/RenameDialog.tsx @@ -0,0 +1,136 @@ +import * as React from 'react'; +import Modal from 'react-modal'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { Modal as BlueprintModal, Text } 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 type { BoxItem } from '../../common/types/core'; + +import messages from '../common/messages'; + +export interface RenameDialogProps { + appElement: HTMLElement; + errorCode: string; + isLoading: boolean; + isOpen: boolean; + item: BoxItem; + onCancel: () => void; + onRename: (value: string, extension: string) => void; + parentElement: HTMLElement; +} + +const RenameDialog = ({ + appElement, + errorCode, + isOpen, + isLoading, + item, + onCancel, + onRename, + parentElement, +}: RenameDialogProps) => { + const { formatMessage } = useIntl(); + let textInput = null; + let error; + + const { name = '', extension } = item; + const ext = extension ? `.${extension}` : ''; + const nameWithoutExt = extension ? name.replace(ext, '') : name; + + /** + * Appends the extension and calls rename function + */ + const handleRename = () => { + 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': + handleRename(); + 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 ( + parentElement} + portalClassName={`${CLASS_MODAL} be-modal-rename`} + > + + + {error && ( +
+ +
+ )} +
+ + + + +
+ + + {formatMessage(messages.cancel)} + + + {formatMessage(messages.rename)} + + +
+ ); +}; + +export default RenameDialog; diff --git a/src/elements/content-explorer/stories/RenameDialog.stories.tsx b/src/elements/content-explorer/stories/RenameDialog.stories.tsx new file mode 100644 index 0000000000..b51bd41754 --- /dev/null +++ b/src/elements/content-explorer/stories/RenameDialog.stories.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import { useArgs } from '@storybook/preview-api'; +import { Button } from '@box/blueprint-web'; + +import { addRootElement } from '../../../utils/storybook'; + +import RenameDialog, { RenameDialogProps } from '../RenameDialog'; + +export const renameDialog = { + render: (args: RenameDialogProps) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [, setArgs] = useArgs(); + + const handleOpenModal = () => setArgs({ isOpen: true }); + + const handleCloseModal = () => { + setArgs({ isOpen: false }); + }; + + const { appElement, 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.tsx similarity index 79% rename from src/elements/content-explorer/stories/tests/RenameDialog-visual.stories.js rename to src/elements/content-explorer/stories/tests/RenameDialog-visual.stories.tsx index 06721da6ca..8ef71df648 100644 --- a/src/elements/content-explorer/stories/tests/RenameDialog-visual.stories.js +++ b/src/elements/content-explorer/stories/tests/RenameDialog-visual.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { addRootElement, defaultVisualConfig } from '../../../../utils/storybook'; -import RenameDialog from '../../RenameDialog'; +import RenameDialog, { RenameDialogProps } from '../../RenameDialog'; import { ERROR_CODE_ITEM_NAME_IN_USE, ERROR_CODE_ITEM_NAME_INVALID, @@ -22,25 +22,34 @@ const itemWithLongName = { }; export const renameDialogNotLoading = { - render: () => { + render: (args: RenameDialogProps) => { const { appElement, rootElement } = addRootElement(); return ( - + ); }, }; export const renameDialogIsLoading = { - render: () => { + render: (args: RenameDialogProps) => { const { appElement, rootElement } = addRootElement(); - return ; + return ( + + ); }, }; export const renameDialogNameInvalidError = { - render: () => { + render: (args: RenameDialogProps) => { const { appElement, rootElement } = addRootElement(); return ( @@ -51,13 +60,14 @@ export const renameDialogNameInvalidError = { isOpen item={item} parentElement={rootElement} + {...args} /> ); }, }; export const renameDialogNameInUseError = { - render: () => { + render: (args: RenameDialogProps) => { const { appElement, rootElement } = addRootElement(); return ( @@ -68,13 +78,14 @@ export const renameDialogNameInUseError = { isOpen item={item} parentElement={rootElement} + {...args} /> ); }, }; export const renameDialogNameTooLongError = { - render: () => { + render: (args: RenameDialogProps) => { const { appElement, rootElement } = addRootElement(); return ( @@ -85,6 +96,7 @@ export const renameDialogNameTooLongError = { isOpen item={itemWithLongName} parentElement={rootElement} + {...args} /> ); },