Skip to content
Open
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
19 changes: 18 additions & 1 deletion .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React from 'react';
import { BlueprintProvider, useNoopTreatment } from '@box/blueprint-web';
import { boxLanguages } from '@box/languages';
import { addWindowFocusTracking } from '@react-aria/interactions';
import { initialize, mswLoader } from 'msw-storybook-addon';
import type { Decorator, Preview } from '@storybook/react-webpack5';

import '../src/styles/variables';
import '../src/styles/base.scss';
Expand All @@ -24,7 +27,21 @@ initialize({
},
});

const preview = {
const withStorybookBlueprintAnimations: Decorator = Story => (
<BlueprintProvider
useTreatment={useNoopTreatment}
configurationOverrides={{
animationsPhase1Enabled: true,
animationsPhase2Enabled: true,
}}
>
<Story />
</BlueprintProvider>
);

const preview: Preview = {
decorators: [withStorybookBlueprintAnimations],

parameters: {
chromatic: {
cropToViewport: true,
Expand Down
105 changes: 105 additions & 0 deletions src/elements/common/__tests__/withBlueprintAnimations.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import * as React from 'react';
import { render } from '../../../test-utils/testing-library';
import { type BlueprintAnimationsProps, withBlueprintAnimations } from '../withBlueprintAnimations';

jest.mock('@box/blueprint-web', () => {
const ReactMock = jest.requireActual('react');

return {
BlueprintProvider: ({ children, configurationOverrides }) =>
ReactMock.createElement(
'div',
{
'data-testid': 'blueprint-animations-provider',
'data-animations-phase1': String(configurationOverrides?.animationsPhase1Enabled),
'data-animations-phase2': String(configurationOverrides?.animationsPhase2Enabled),
},
children,
),
useNoopTreatment: () => 'control',
TooltipProvider: ({ children }) =>
ReactMock.createElement('div', { 'data-testid': 'tooltip-provider' }, children),
};
});

type TestComponentProps = {
value?: string;
} & BlueprintAnimationsProps;

describe('src/elements/common/withBlueprintAnimations', () => {
const TestComponent = ({ value }: TestComponentProps) => (
<div data-testid="test-component">{`Test ${value || 'default'}`}</div>
);

const WrappedComponent = withBlueprintAnimations(TestComponent);

const renderComponent = (props?: TestComponentProps) => render(<WrappedComponent {...props} />);

test('should enable Blueprint animations by default', () => {
const { getByTestId } = renderComponent();

expect(getByTestId('test-component')).toHaveTextContent('Test default');
expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase1', 'true');
expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase2', 'true');
});

test('should opt out when enableBlueprintAnimations is false', () => {
const { getByTestId } = renderComponent({ enableBlueprintAnimations: false });

expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase1', 'false');
expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase2', 'false');
});

test('should enable all phases when enableBlueprintAnimations is true', () => {
const { getByTestId } = renderComponent({ enableBlueprintAnimations: true });

expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase1', 'true');
expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase2', 'true');
});

test('should honor per-phase overrides matching Blueprint configurationOverrides keys', () => {
const { getByTestId } = renderComponent({
enableBlueprintAnimations: {
animationsPhase1Enabled: true,
animationsPhase2Enabled: false,
},
});

expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase1', 'true');
expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase2', 'false');
});

test('should default omitted phase keys to on when an object is passed', () => {
const { getByTestId } = renderComponent({
enableBlueprintAnimations: {
animationsPhase2Enabled: false,
},
});

expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase1', 'true');
expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase2', 'false');
});

test('should pass props to wrapped component', () => {
const { getByTestId } = renderComponent({ value: 'test-value' });

expect(getByTestId('test-component')).toHaveTextContent('Test test-value');
});

test('should not forward enableBlueprintAnimations to the wrapped component', () => {
const DomFacingComponent = ({ value, ...rest }: TestComponentProps & Record<string, unknown>) => (
<div data-testid="dom-facing" {...rest}>
{value}
</div>
);
const WrappedDomFacing = withBlueprintAnimations(DomFacingComponent);

const { getByTestId } = render(
<WrappedDomFacing value="ok" enableBlueprintAnimations={false} data-extra="keep" />,
);

expect(getByTestId('dom-facing')).not.toHaveAttribute('enableBlueprintAnimations');
expect(getByTestId('dom-facing')).toHaveAttribute('data-extra', 'keep');
expect(getByTestId('blueprint-animations-provider')).toHaveAttribute('data-animations-phase1', 'false');
});
});
53 changes: 53 additions & 0 deletions src/elements/common/withBlueprintAnimations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import { BlueprintProvider, useNoopTreatment } from '@box/blueprint-web';
import type { BlueprintConfigurationOverrides } from '@box/blueprint-web';

export type BlueprintAnimationsProps = {
/**
* Blueprint component animations for this element.
* - omitted / `true`: all phases on (default)
* - `false`: all phases off
* - object: per-phase overrides from Blueprint `configurationOverrides` (omitted keys default on)
*/
enableBlueprintAnimations?: boolean | BlueprintConfigurationOverrides;
};

export function getBlueprintAnimationOverrides(
value: boolean | BlueprintConfigurationOverrides | undefined,
): Pick<BlueprintConfigurationOverrides, 'animationsPhase1Enabled' | 'animationsPhase2Enabled'> {
if (value === false) {
return {
animationsPhase1Enabled: false,
animationsPhase2Enabled: false,
};
}

if (value === true || value == null) {
return {
animationsPhase1Enabled: true,
animationsPhase2Enabled: true,
};
}

return {
animationsPhase1Enabled: value.animationsPhase1Enabled !== false,
animationsPhase2Enabled: value.animationsPhase2Enabled !== false,
};
}

export function withBlueprintAnimations<P>(
Component: React.ComponentType<P>,
): React.FC<P & BlueprintAnimationsProps> {
return function WithBlueprintAnimations(props: P & BlueprintAnimationsProps) {
const { enableBlueprintAnimations, ...rest } = props;

return (
<BlueprintProvider
useTreatment={useNoopTreatment}
configurationOverrides={getBlueprintAnimationOverrides(enableBlueprintAnimations)}
>
<Component {...(rest as P)} />
</BlueprintProvider>
);
};
}
11 changes: 8 additions & 3 deletions src/elements/content-explorer/ContentExplorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ import '../common/fonts.scss';
import '../common/base.scss';
import '../common/modal.scss';
import './ContentExplorer.scss';
import { withBlueprintAnimations } from '../common/withBlueprintAnimations';
import { withBlueprintModernization } from '../common/withBlueprintModernization';
import Providers from '../common/Providers';

Expand Down Expand Up @@ -2100,6 +2101,10 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
}

export { ContentExplorer as ContentExplorerComponent };
export default flow([makeResponsive, withFeatureConsumer, withFeatureProvider, withBlueprintModernization])(
ContentExplorer,
);
export default flow([
makeResponsive,
withFeatureConsumer,
withFeatureProvider,
withBlueprintModernization,
withBlueprintAnimations,
])(ContentExplorer);
4 changes: 3 additions & 1 deletion src/elements/content-picker/ContentPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import CreateFolderDialog from '../common/create-folder-dialog';
import Internationalize from '../common/Internationalize';
import makeResponsive from '../common/makeResponsive';
// $FlowFixMe
import { withBlueprintAnimations } from '../common/withBlueprintAnimations';
// $FlowFixMe
import { withBlueprintModernization } from '../common/withBlueprintModernization';
// $FlowFixMe TypeScript file
import ThemingStyles from '../common/theming';
Expand Down Expand Up @@ -1350,4 +1352,4 @@ class ContentPicker extends Component<Props, State> {
}

export { ContentPicker as ContentPickerComponent };
export default flow([makeResponsive, withBlueprintModernization])(ContentPicker);
export default flow([makeResponsive, withBlueprintModernization, withBlueprintAnimations])(ContentPicker);
3 changes: 3 additions & 0 deletions src/elements/content-preview/ContentPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import { mark } from '../../utils/performance';
import { convertTimestampToSeconds } from '../../utils/timestamp';
import { isFeatureEnabled, withFeatureConsumer, withFeatureProvider } from '../common/feature-checking';
// $FlowFixMe
import { withBlueprintAnimations } from '../common/withBlueprintAnimations';
// $FlowFixMe
import { withBlueprintModernization } from '../common/withBlueprintModernization';
import { EVENT_JS_READY } from '../common/logger/constants';
import ReloadNotification from './ReloadNotification';
Expand Down Expand Up @@ -1763,6 +1765,7 @@ export default flow([
withFeatureConsumer,
withFeatureProvider,
withBlueprintModernization,
withBlueprintAnimations,
withLogger(ORIGIN_CONTENT_PREVIEW),
withErrorBoundary(ORIGIN_CONTENT_PREVIEW),
])(ContentPreview);
12 changes: 9 additions & 3 deletions src/elements/content-sharing/ContentSharing.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import { isFeatureEnabled } from '../common/feature-checking';
import Internationalize from '../common/Internationalize';
import Providers from '../common/Providers';
// $FlowFixMe
import { withBlueprintModernization } from '../common/withBlueprintModernization';
// $FlowFixMe
import ContentSharingV2 from './ContentSharingV2';
import SharingModal from './SharingModal';

Expand Down Expand Up @@ -51,6 +49,10 @@ type ContentSharingProps = {
* the modal will appear on page load. See ContentSharing.stories.js for examples.
*/
displayInModal: boolean,
/** Forwarded to ContentSharingV2 Blueprint HOCs when contentSharingV2 is enabled */
enableBlueprintAnimations?: boolean | Object,
/** Forwarded to ContentSharingV2 Blueprint HOCs when contentSharingV2 is enabled */
enableModernizedComponents?: boolean,
/** features - Features for the element */
features?: FeatureConfig,
/** hasProviders - Whether the element has providers for USM already */
Expand Down Expand Up @@ -92,6 +94,8 @@ function ContentSharing({
config,
customButton,
displayInModal,
enableBlueprintAnimations,
enableModernizedComponents,
features = {},
hasProviders = true,
itemID,
Expand Down Expand Up @@ -141,6 +145,8 @@ function ContentSharing({
<ContentSharingV2
api={api}
config={config}
enableBlueprintAnimations={enableBlueprintAnimations}
enableModernizedComponents={enableModernizedComponents}
itemId={itemID}
itemType={itemType}
onClose={onClose}
Expand Down Expand Up @@ -177,4 +183,4 @@ function ContentSharing({
);
}

export default withBlueprintModernization(ContentSharing);
export default ContentSharing;
4 changes: 3 additions & 1 deletion src/elements/content-sharing/ContentSharingV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
} from '@box/unified-share-modal';

import API from '../../api';
import { withBlueprintAnimations } from '../common/withBlueprintAnimations';
import { withBlueprintModernization } from '../common/withBlueprintModernization';
import { fetchAvatars, fetchCollaborators, fetchCurrentUser, fetchItem } from './apis';
import { CONTENT_SHARING_ERRORS } from './constants';
Expand Down Expand Up @@ -300,4 +301,5 @@ function ContentSharingV2({
);
}

export default withBlueprintModernization(ContentSharingV2);
export { ContentSharingV2 as ContentSharingV2Component };
export default withBlueprintAnimations(withBlueprintModernization(ContentSharingV2));
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ export const withModernization = {
expect(button).toBeInTheDocument();
await userEvent.click(button);

expect(screen.getByRole('heading', { name: 'Share ‘Box Development Guide.pdf’' })).toBeVisible();
// Wait for modal to finish animation before checking visibility
await waitFor(() => {
expect(screen.getByRole('heading', { name: 'Share ‘Box Development Guide.pdf’' })).toBeVisible();
Comment thread
jfox-box marked this conversation as resolved.
});
expect(screen.getByRole('combobox', { name: 'Invite People' })).toBeVisible();
expect(screen.getByRole('switch', { name: 'Shared link' })).toBeVisible();
},
Expand Down
3 changes: 3 additions & 0 deletions src/elements/content-sidebar/ContentSidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { mark } from '../../utils/performance';
import { SIDEBAR_FIELDS_TO_FETCH, SIDEBAR_FIELDS_TO_FETCH_ARCHIVE } from '../../utils/fields';
import { withErrorBoundary } from '../common/error-boundary';
// $FlowFixMe
import { withBlueprintAnimations } from '../common/withBlueprintAnimations';
// $FlowFixMe
import { withBlueprintModernization } from '../common/withBlueprintModernization';
import {
isFeatureEnabled as isFeatureEnabledInContext,
Expand Down Expand Up @@ -474,6 +476,7 @@ export default flow([
withFeatureConsumer,
withFeatureProvider,
withBlueprintModernization,
withBlueprintAnimations,
withLogger(ORIGIN_CONTENT_SIDEBAR),
withErrorBoundary(ORIGIN_CONTENT_SIDEBAR),
])(ContentSidebar);
3 changes: 2 additions & 1 deletion src/elements/content-uploader/ContentUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import API from '../../api';
import Browser from '../../utils/Browser';
import Internationalize from '../common/Internationalize';
import makeResponsive from '../common/makeResponsive';
import { withBlueprintAnimations } from '../common/withBlueprintAnimations';
import { withBlueprintModernization } from '../common/withBlueprintModernization';
import ThemingStyles, { Theme } from '../common/theming';
import FolderUpload from '../../api/uploads/FolderUpload';
Expand Down Expand Up @@ -1982,5 +1983,5 @@ class ContentUploader extends Component<ContentUploaderProps, State> {
}
}

export default flow([makeResponsive, withBlueprintModernization])(ContentUploader);
export default flow([makeResponsive, withBlueprintModernization, withBlueprintAnimations])(ContentUploader);
export { ContentUploader as ContentUploaderComponent, CHUNKED_UPLOAD_MIN_SIZE_BYTES };
Loading