Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(content-explorer): Migrate previewDialog #3702

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,36 @@
*/

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';

type PreviewDialogProps = {
apiHost: string,
appElement: HTMLElement,
greg-in-a-box marked this conversation as resolved.
Show resolved Hide resolved
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,
Comment on lines +31 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function -> any seems backwards in terms of helpfulness

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thats the codemods doing, if i recall correctly

sharedLink?: string,
sharedLinkPassword?: string,
staticHost: string,
Expand All @@ -42,29 +43,29 @@ 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;
const onLoad = (data: any): void => {
onPreview(cloneDeep(data));
Expand All @@ -77,39 +78,38 @@ const PreviewDialog = ({
const files: BoxItem[] = items.filter(({ type }) => type === TYPE_FILE);
return (
<Modal
isOpen={isOpen}
parentSelector={() => parentElement}
portalClassName={`${CLASS_MODAL} be-modal-preview`}
appElement={appElement}
className={CLASS_MODAL_CONTENT_FULL_BLEED}
overlayClassName={CLASS_MODAL_OVERLAY}
contentLabel={intl.formatMessage(messages.preview)}
contentLabel={formatMessage(messages.preview)}
isOpen={isOpen}
onRequestClose={onCancel}
appElement={appElement}
overlayClassName={CLASS_MODAL_OVERLAY}
parentSelector={() => parentElement}
portalClassName={CLASS_MODAL}
>
<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;
109 changes: 109 additions & 0 deletions src/elements/content-explorer/PreviewDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
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;
onPreview: (data: unknown) => 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;
const onLoad = (data: unknown): void => {
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}
>
<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;
69 changes: 69 additions & 0 deletions src/elements/content-explorer/__tests__/PreviewDialog.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as React from 'react';
import userEvent from '@testing-library/user-event';
import { render, screen, waitFor } from '../../../test-utils/testing-library';
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));
});
});
});
Loading
Loading