Skip to content

Commit

Permalink
chore(content-explorer): Migrate previewDialog
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-in-a-box committed Nov 4, 2024
1 parent 82f2cc7 commit 5b1fc0a
Show file tree
Hide file tree
Showing 7 changed files with 421 additions and 50 deletions.
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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,38 @@
*/

import * as React from 'react';
import { useIntl } from 'react-intl';
import Modal from 'react-modal';
import { injectIntl } from 'react-intl';
import type { IntlShape } from 'react-intl';
import cloneDeep from 'lodash/cloneDeep';
import messages from '../common/messages';

import ContentPreview from '../content-preview';
import { TYPE_FILE, CLASS_MODAL_CONTENT_FULL_BLEED, CLASS_MODAL_OVERLAY, CLASS_MODAL } from '../../constants';
import type { ContentPreviewProps } from '../content-preview';
import type { Token, BoxItem, Collection } from '../../common/types/core';
import type APICache from '../../utils/Cache';

type Props = {
import messages from '../common/messages';

import './PreviewDialog.scss';

type PreviewDialogProps = {
apiHost: string,
appElement: HTMLElement,
appHost: string,
cache: APICache,
canDownload: boolean,
contentPreviewProps: ContentPreviewProps,
currentCollection: Collection,
intl: IntlShape,
isOpen: boolean,
isTouch: boolean,
item: BoxItem,
onCancel: Function,
onDownload: Function,
onPreview: Function,
onCancel: any,
onDownload: any,
onPreview: any,
parentElement: HTMLElement,
previewLibraryVersion: string,
requestInterceptor?: Function,
responseInterceptor?: Function,
responseInterceptor?: any,
requestInterceptor?: any,
sharedLink?: string,
sharedLinkPassword?: string,
staticHost: string,
Expand All @@ -42,32 +45,35 @@ type Props = {
};

const PreviewDialog = ({
item,
isOpen,
parentElement,
appElement,
token,
cache,
currentCollection,
canDownload,
onCancel,
onPreview,
onDownload,
apiHost,
appHost,
staticHost,
staticPath,
previewLibraryVersion,
sharedLink,
sharedLinkPassword,
contentPreviewProps,
requestInterceptor,
responseInterceptor,
intl,
}: Props) => {
apiHost,
appElement,
appHost,
cache,
canDownload,
contentPreviewProps,
currentCollection,
isOpen,
item,
onCancel,
onDownload,
onPreview,
parentElement,
previewLibraryVersion,
requestInterceptor,
responseInterceptor,
sharedLink,
sharedLinkPassword,
staticHost,
staticPath,
token,
}: PreviewDialogProps) => {
const { formatMessage } = useIntl();
const { items }: Collection = currentCollection;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const onLoad = (data: any): void => {
onPreview(cloneDeep(data));
if (onPreview) {
onPreview(cloneDeep(data));
}
};

if (!item || !items) {
Expand All @@ -77,39 +83,38 @@ const PreviewDialog = ({
const files: BoxItem[] = items.filter(({ type }) => type === TYPE_FILE);
return (
<Modal
appElement={appElement}
className={CLASS_MODAL_CONTENT_FULL_BLEED}
contentLabel={formatMessage(messages.preview)}
isOpen={isOpen}
onRequestClose={onCancel}
overlayClassName={CLASS_MODAL_OVERLAY}
parentSelector={() => parentElement}
portalClassName={`${CLASS_MODAL} be-modal-preview`}
className={CLASS_MODAL_CONTENT_FULL_BLEED}
overlayClassName={CLASS_MODAL_OVERLAY}
contentLabel={intl.formatMessage(messages.preview)}
onRequestClose={onCancel}
appElement={appElement}
>
<ContentPreview
{...contentPreviewProps}
fileId={item.id}
autoFocus
apiHost={apiHost}
appHost={appHost}
staticHost={staticHost}
staticPath={staticPath}
previewLibraryVersion={previewLibraryVersion}
cache={cache}
token={token}
hasHeader
autoFocus
canDownload={canDownload}
collection={files}
onLoad={onLoad}
fileId={item.id}
hasHeader
onClose={onCancel}
onDownload={onDownload}
canDownload={canDownload}
onLoad={onLoad}
previewLibraryVersion={previewLibraryVersion}
staticHost={staticHost}
staticPath={staticPath}
sharedLink={sharedLink}
sharedLinkPassword={sharedLinkPassword}
requestInterceptor={requestInterceptor}
responseInterceptor={responseInterceptor}
token={token}
/>
</Modal>
);
};

export default injectIntl(PreviewDialog);
export default PreviewDialog;
113 changes: 113 additions & 0 deletions src/elements/content-explorer/PreviewDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import * as React from 'react';
import { useIntl } from 'react-intl';
import Modal from 'react-modal';
import cloneDeep from 'lodash/cloneDeep';

import ContentPreview, { ContentPreviewProps } from '../content-preview';
import { TYPE_FILE, CLASS_MODAL_CONTENT_FULL_BLEED, CLASS_MODAL_OVERLAY, CLASS_MODAL } from '../../constants';
import type { Token, BoxItem, Collection } from '../../common/types/core';
import type APICache from '../../utils/Cache';

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

export interface PreviewDialogProps {
apiHost: string;
appElement: HTMLElement;
appHost: string;
cache: APICache;
canDownload: boolean;
contentPreviewProps: ContentPreviewProps;
currentCollection: Collection;
isOpen: boolean;
isTouch: boolean;
item: BoxItem;
onCancel: () => void;
onDownload: () => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onPreview: (data: any) => void;
parentElement: HTMLElement;
previewLibraryVersion: string;
requestInterceptor?: () => void;
responseInterceptor?: () => void;
sharedLink?: string;
sharedLinkPassword?: string;
staticHost: string;
staticPath: string;
token: Token;
}

const PreviewDialog = ({
apiHost,
appElement,
appHost,
cache,
canDownload,
contentPreviewProps,
currentCollection,
isOpen,
item,
onCancel,
onDownload,
onPreview,
parentElement,
previewLibraryVersion,
requestInterceptor,
responseInterceptor,
sharedLink,
sharedLinkPassword,
staticHost,
staticPath,
token,
}: PreviewDialogProps) => {
const { formatMessage } = useIntl();
const { items }: Collection = currentCollection;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const onLoad = (data: any): void => {
if (onPreview) {
onPreview(cloneDeep(data));
}
};

if (!item || !items) {
return null;
}

const files: BoxItem[] = items.filter(({ type }) => type === TYPE_FILE);
return (
<Modal
appElement={appElement}
className={CLASS_MODAL_CONTENT_FULL_BLEED}
contentLabel={formatMessage(messages.preview)}
isOpen={isOpen}
onRequestClose={onCancel}
overlayClassName={CLASS_MODAL_OVERLAY}
parentSelector={() => parentElement}
portalClassName={`${CLASS_MODAL} be-modal-preview`}
>
<ContentPreview
{...contentPreviewProps}
autoFocus
apiHost={apiHost}
appHost={appHost}
cache={cache}
canDownload={canDownload}
collection={files}
fileId={item.id}
hasHeader
onClose={onCancel}
onDownload={onDownload}
onLoad={onLoad}
previewLibraryVersion={previewLibraryVersion}
staticHost={staticHost}
staticPath={staticPath}
sharedLink={sharedLink}
sharedLinkPassword={sharedLinkPassword}
requestInterceptor={requestInterceptor}
responseInterceptor={responseInterceptor}
token={token}
/>
</Modal>
);
};

export default PreviewDialog;
80 changes: 80 additions & 0 deletions src/elements/content-explorer/__tests__/PreviewDialog.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as React from 'react';
import userEvent from '@testing-library/user-event';
import { render, screen, waitFor } from '../../../test-utils/testing-library';

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import APICache from '../../../utils/Cache';

import PreviewDialog, { PreviewDialogProps } from '../PreviewDialog';

jest.mock('react-modal', () => {
return jest.fn(({ children }) => <div aria-label="Preview">{children}</div>);
});

describe('elements/content-explorer/PreviewDialog', () => {
const defaultProps = {
appElement: document.body,
apiHost: 'https://api.box.com',
appHost: 'https://app.box.com',
cache: new APICache(),
canDownload: true,
contentPreviewProps: {},
currentCollection: { items: [{ id: '1', type: 'file' }] },
isOpen: true,
isTouch: false,
item: { id: '1', type: 'file' },
onCancel: jest.fn(),
onDownload: jest.fn(),
onPreview: jest.fn(),
parentElement: document.createElement('div'),
previewLibraryVersion: '1.0.0',
staticHost: 'https://static.box.com',
staticPath: '/static',
token: 'token',
};

const renderComponent = (props?: Partial<PreviewDialogProps>) =>
render(<PreviewDialog {...defaultProps} {...props} />);

test('renders', async () => {
renderComponent();
expect(await screen.findByLabelText('Preview')).toBeInTheDocument();
expect(await screen.findByRole('button', { name: 'Close' }));
});

test('calls onCancel when modal is closed', async () => {
renderComponent();
const closeButton = screen.getByRole('button', { name: 'Close' });
await userEvent.click(closeButton);
expect(defaultProps.onCancel).toHaveBeenCalled();
});

test('does not render when item is null', () => {
const props = { item: null };
const { container } = renderComponent(props);
expect(container.firstChild).toBeNull();
});

test('does not render when items are null', () => {
const props = { currentCollection: { items: null } };
const { container } = renderComponent(props);
expect(container.firstChild).toBeNull();
});

test('calls onPreview with cloned data on load', () => {
const data = { id: '1', type: 'file' };
renderComponent();
waitFor(() => {
expect(defaultProps.onPreview).toHaveBeenCalledWith(expect.objectContaining(data));
});
});

test('does not call onPreview with cloned data on load', () => {
const data = { id: '1', type: 'file' };
renderComponent({ onPreview: null });
waitFor(() => {
expect(defaultProps.onPreview).not.toHaveBeenCalledWith(expect.objectContaining(data));
});
});
});
Loading

0 comments on commit 5b1fc0a

Please sign in to comment.