Skip to content

Commit

Permalink
chore(content-explorer): Migrate renameDialog
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-in-a-box committed Nov 12, 2024
1 parent c76176e commit d685f6e
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 50 deletions.
7 changes: 7 additions & 0 deletions src/elements/common/modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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;

Expand All @@ -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();
Expand All @@ -81,7 +73,7 @@ const RenameDialog = ({
const onKeyDown = ({ key }) => {
switch (key) {
case 'Enter':
rename();
handleRename();
break;
default:
break;
Expand All @@ -104,32 +96,41 @@ const RenameDialog = ({
<Modal
appElement={appElement}
className={CLASS_MODAL_CONTENT}
contentLabel={intl.formatMessage(messages.renameDialogLabel)}
contentLabel={formatMessage(messages.renameDialogLabel)}
isOpen={isOpen}
onRequestClose={onCancel}
overlayClassName={CLASS_MODAL_OVERLAY}
parentSelector={() => parentElement}
portalClassName={`${CLASS_MODAL} be-modal-rename`}
>
<label>
{error ? (
<div className="be-modal-error">
<FormattedMessage {...error} values={{ name: nameWithoutExt }} />
</div>
) : null}
<FormattedMessage tagName="div" {...messages.renameDialogText} values={{ name: nameWithoutExt }} />
<BlueprintModal.Body>
<Text as="label">
{error && (
<div className="be-modal-error">
<FormattedMessage {...error} values={{ name: nameWithoutExt }} />
</div>
)}
</Text>
<Text as="label">
<FormattedMessage {...messages.renameDialogText} values={{ name: nameWithoutExt }} />
</Text>
<input ref={ref} defaultValue={nameWithoutExt} onKeyDown={onKeyDown} required type="text" />
</label>
<div className="be-modal-btns">
<PrimaryButton isLoading={isLoading} onClick={rename} type="button">
<FormattedMessage {...messages.rename} />
</PrimaryButton>
<Button isDisabled={isLoading} onClick={onCancel} type="button">
<FormattedMessage {...messages.cancel} />
</Button>
</div>
</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={handleRename}
size="large"
>
{formatMessage(messages.rename)}
</BlueprintModal.Footer.PrimaryButton>
</BlueprintModal.Footer>
</Modal>
);
};

export default injectIntl(RenameDialog);
export default RenameDialog;
136 changes: 136 additions & 0 deletions src/elements/content-explorer/RenameDialog.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Modal
appElement={appElement}
className={CLASS_MODAL_CONTENT}
contentLabel={formatMessage(messages.renameDialogLabel)}
isOpen={isOpen}
onRequestClose={onCancel}
overlayClassName={CLASS_MODAL_OVERLAY}
parentSelector={() => parentElement}
portalClassName={`${CLASS_MODAL} be-modal-rename`}
>
<BlueprintModal.Body>
<Text as="label">
{error && (
<div className="be-modal-error">
<FormattedMessage {...error} values={{ name: nameWithoutExt }} />
</div>
)}
</Text>
<Text as="label">
<FormattedMessage {...messages.renameDialogText} values={{ name: nameWithoutExt }} />
</Text>
<input ref={ref} defaultValue={nameWithoutExt} onKeyDown={onKeyDown} required type="text" />
</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={handleRename}
size="large"
>
{formatMessage(messages.rename)}
</BlueprintModal.Footer.PrimaryButton>
</BlueprintModal.Footer>
</Modal>
);
};

export default RenameDialog;
49 changes: 49 additions & 0 deletions src/elements/content-explorer/stories/RenameDialog.stories.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<RenameDialog
appElement={appElement}
item={{
id: '123456',
name: 'mockItem',
}}
onCancel={handleCloseModal}
parentElement={rootElement}
{...args}
/>
<Button onClick={handleOpenModal} variant="primary">
Launch RenameDialog
</Button>
</div>
);
},
};

export default {
title: 'Elements/ContentExplorer',
component: RenameDialog,
args: {
isLoading: false,
isOpen: false,
},
};
Loading

0 comments on commit d685f6e

Please sign in to comment.