-
Notifications
You must be signed in to change notification settings - Fork 350
feat(preview): support loading the Preview library from npm #4695
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -196,6 +196,7 @@ | |
| "babel-plugin-react-remove-properties": "^0.3.0", | ||
| "babel-plugin-rewire": "^1.0.0", | ||
| "babel-plugin-styled-components": "^1.10.7", | ||
| "box-content-preview": "3.62.1", | ||
| "chromatic": "^18.0.1", | ||
| "circular-dependency-plugin": "^5.2.2", | ||
| "classnames": "^2.5.1", | ||
|
|
@@ -322,6 +323,7 @@ | |
| "@hapi/address": "^2.1.4", | ||
| "@tanstack/react-virtual": "^3.13.12", | ||
| "axios": "^0.32.0", | ||
| "box-content-preview": "^3.62.0", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [question] Why is this version different from the one above?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. whoops good catch
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but the ^ caret would allow us to get 3.62.1 also |
||
| "classnames": "^2.5.1", | ||
| "color": "^3.2.1", | ||
| "draft-js": "^0.11.7", | ||
|
|
@@ -364,6 +366,11 @@ | |
| "tabbable": "^1.1.3", | ||
| "uuid": "^8.3.2" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "box-content-preview": { | ||
| "optional": true | ||
| } | ||
| }, | ||
| "resolutions": { | ||
| "@sigstore/core": "3.2.1", | ||
| "draft-js/immutable": "^3.8.3", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,9 @@ const getConfig = isReactBundle => { | |
| alias: { | ||
| 'box-ui-elements-locale-data': path.resolve(`i18n/bundles/${language}`), | ||
| 'box-locale-data': path.resolve(`node_modules/@box/cldr-data/locale-data/${language}`), | ||
| // box-content-preview (devDependency) imports its box-ui-elements peer via | ||
| // es/ paths; inside this repo those resolve to the src/ they are built from. | ||
| 'box-ui-elements/es': path.resolve('src'), | ||
|
Comment on lines
+53
to
+55
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [question] Just curious, how were you testing that the bundle size is not doubled and installed both version of BUIE |
||
| }, | ||
| }, | ||
|
|
||
|
|
@@ -72,8 +75,10 @@ const getConfig = isReactBundle => { | |
| { | ||
| test: /\.(js|mjs|ts|tsx)$/, | ||
| // Exclude node_modules in development mode to improve build performance | ||
| // box-content-preview ships a pre-bundled dist with class private methods | ||
| // that this babel config cannot parse; it needs no transpilation. | ||
| exclude: hasAllBrowserSupport | ||
| ? /@babel(?:\/|\\{1,2})runtime|pikaday|core-js/ | ||
| ? /@babel(?:\/|\\{1,2})runtime|box-content-preview|pikaday|core-js/ | ||
| : /node_modules\/(?!@box\/cldr-data)/, // Exclude node_modules except for @box/cldr-data which is needed for i18n | ||
| loader: 'babel-loader', | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -155,6 +155,9 @@ type Props = { | |
| }, | ||
| previewLibraryVersion: string, | ||
| previewMode?: 'default' | 'shared_file' | 'shared_folder' | 'editable_shared_file' | 'inline_feed', | ||
| // npm path only: URL of the pdfjs worker file, resolved by the consumer's bundler. | ||
| // Passed to box-content-preview in the show() options as `pdfjs.workerSrc`. | ||
| pdfjsWorkerSrc?: string, | ||
| requestInterceptor?: Function, | ||
| responseInterceptor?: Function, | ||
| sharedLink?: string, | ||
|
|
@@ -276,6 +279,10 @@ class ContentPreview extends React.PureComponent<Props, State> { | |
|
|
||
| preview: any; | ||
|
|
||
| // Cached module reference once box-content-preview is dynamically imported | ||
| // under the npm-load path (useNpmBoxContentPreview feature flag). | ||
| npmPreviewModule: ?{ Preview: any }; | ||
|
|
||
| api: API; | ||
|
|
||
| // Defines a generic type for ContentSidebar, since an import would interfere with code splitting | ||
|
|
@@ -467,10 +474,14 @@ class ContentPreview extends React.PureComponent<Props, State> { | |
| * @return {void} | ||
| */ | ||
| componentDidMount(): void { | ||
| // Always load Box.Preview library assets | ||
| // Always load preview library assets (npm module or CDN script) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [question] This mean when shouldUseNpmPreview is true, you will always have to rely on the BUIE installed box-content-preview package version since that will be the npm version and never able to leverage the older CDN version. Is this intended?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes |
||
| // Even when children are provided, we need assets ready for transitions | ||
| this.loadStylesheet(); | ||
| this.loadScript(); | ||
| if (this.shouldUseNpmPreview()) { | ||
| this.loadNpmPreview(); | ||
| } else { | ||
| this.loadStylesheet(); | ||
| this.loadScript(); | ||
|
Comment on lines
+479
to
+483
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [question] Eventually shouldUseNpmPreview feature related code will be cleaned up I assume? In that case, will other consumer that has been leveraging the CDN script also be forced to switch to using npm installed version only?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, the CDN will stay for legacy users. |
||
| } | ||
|
|
||
| const { currentFileId } = this.state; | ||
| const { loadingIndicatorDelayMs } = this.props; | ||
|
|
@@ -487,6 +498,77 @@ class ContentPreview extends React.PureComponent<Props, State> { | |
| this.focusPreview(); | ||
| } | ||
|
|
||
| /** | ||
| * Whether to load Preview from the npm package (box-content-preview) | ||
| * instead of injecting the CDN script tag. Driven by the | ||
| * `useNpmBoxContentPreview` feature flag. | ||
| */ | ||
| shouldUseNpmPreview(): boolean { | ||
| return isFeatureEnabled(this.props.features, 'useNpmBoxContentPreview'); | ||
| } | ||
|
|
||
| /** | ||
| * Asynchronously loads the npm-published box-content-preview package | ||
| * (and its CSS) via bundler-resolved dynamic imports. Stashes the module | ||
| * so loadPreview() can read the Preview constructor synchronously. | ||
| */ | ||
| loadNpmPreview = async (): Promise<void> => { | ||
| if (this.npmPreviewModule) { | ||
| return; | ||
| } | ||
|
|
||
| let previewModule; | ||
| try { | ||
| [previewModule] = await Promise.all([ | ||
| import(/* webpackChunkName: "box-content-preview" */ 'box-content-preview'), | ||
| import(/* webpackChunkName: "box-content-preview" */ 'box-content-preview/styles.css'), | ||
| ]); | ||
| } catch (error) { | ||
| this.onNpmPreviewLoadError( | ||
| `Failed to load the box-content-preview module: ${error?.message || String(error)}`, | ||
| ); | ||
| return; | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| if (!previewModule.Preview) { | ||
| this.onNpmPreviewLoadError('box-content-preview module has no Preview export'); | ||
| return; | ||
| } | ||
|
|
||
| this.npmPreviewModule = previewModule; | ||
| this.loadPreview(); | ||
| }; | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| /** | ||
| * Builds the `location` option for the npm-loaded Preview library, which | ||
| * cannot self-derive it from a CDN script tag. | ||
| */ | ||
| getNpmPreviewLocation(): { locale: string, staticBaseURI: string, version: string } { | ||
| const { language, previewLibraryVersion, staticHost, staticPath } = this.props; | ||
| const trailingSlash = staticHost.endsWith('/') ? '' : '/'; | ||
| return { | ||
| locale: language, | ||
| staticBaseURI: `${staticHost}${trailingSlash}${staticPath}/`, | ||
| version: previewLibraryVersion, | ||
| }; | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| /** | ||
| * Surfaces a failure to load the npm box-content-preview package through | ||
| * the standard error path so the host gets an onError callback and the | ||
| * loading mask is replaced with an error state. | ||
| */ | ||
| onNpmPreviewLoadError(message: string): void { | ||
| const { onError } = this.props; | ||
| const error = { | ||
| code: ERROR_CODE_UNKNOWN, | ||
| message, | ||
| }; | ||
| this.endLoadingSession(); | ||
| this.setState({ error }); | ||
| onError(error, ERROR_CODE_UNKNOWN, { error }, ORIGIN_PREVIEW); | ||
| } | ||
|
|
||
| static getDerivedStateFromProps(props: Props, state: State) { | ||
| const { fileId } = props; | ||
|
|
||
|
|
@@ -621,6 +703,9 @@ class ContentPreview extends React.PureComponent<Props, State> { | |
| * @return {boolean} true if preview is loaded | ||
| */ | ||
| isPreviewLibraryLoaded(): boolean { | ||
| if (this.npmPreviewModule) { | ||
| return true; | ||
| } | ||
| return !!global.Box && !!global.Box.Preview; | ||
| } | ||
|
|
||
|
|
@@ -1006,8 +1091,17 @@ class ContentPreview extends React.PureComponent<Props, State> { | |
| showProgress: false, | ||
| skipServerUpdate: true, | ||
| useHotkeys: false, | ||
| // npm path: forward the pdfjs worker URL the consumer's bundler emitted. | ||
| ...(this.npmPreviewModule && this.props.pdfjsWorkerSrc | ||
| ? { pdfjs: { workerSrc: this.props.pdfjsWorkerSrc } } | ||
| : {}), | ||
| // npm path: forward location data to box-content-preview's `this.location`. | ||
| // Without this, viewer code that builds asset URLs from staticBaseURI (e.g., the | ||
| // video viewer's Shaka loader) gets `undefined` and fails silently. | ||
| ...(this.npmPreviewModule ? { location: this.getNpmPreviewLocation() } : {}), | ||
| }; | ||
| const { Preview } = global.Box; | ||
|
|
||
| const Preview = this.npmPreviewModule ? this.npmPreviewModule.Preview : global.Box.Preview; | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| this.preview = new Preview(); | ||
| this.preview.addListener('load', this.onPreviewLoad); | ||
| this.preview.addListener('preload', this.endLoadingSession); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] was the storybook breaking without this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes