From 02ef9f4ad1073215a923bbf14b19de5852459b50 Mon Sep 17 00:00:00 2001 From: Daniel DeMicco Date: Thu, 31 Jan 2019 13:10:03 -0800 Subject: [PATCH] perf: code splitting in ContentPreview (#737) --- README.md | 2 + babel.config.js | 2 + conf/webpack.config.js | 16 + package.json | 3 + src/elements/common/async-load/AsyncLoad.js | 58 ++++ .../async-load/__tests__/AsyncLoad-test.js | 51 ++++ .../__snapshots__/AsyncLoad-test.js.snap | 17 ++ src/elements/common/async-load/index.js | 2 + .../content-preview/ContentPreview.js | 7 +- src/elements/content-preview/Header.js | 8 +- .../content-sidebar/ActivitySidebar.js | 7 +- .../content-sidebar/DetailsSidebar.js | 10 +- .../content-sidebar/MetadataSidebar.js | 4 +- src/elements/content-sidebar/Sidebar.js | 32 +- .../content-sidebar/SidebarLoading.js | 23 ++ .../content-sidebar/SidebarLoading.scss | 5 + .../content-sidebar/SidebarLoadingError.js | 11 + .../content-sidebar/SidebarLoadingError.scss | 7 + src/elements/content-sidebar/SidebarUtils.js | 82 +++++- src/elements/content-sidebar/SkillsSidebar.js | 11 +- .../__tests__/SIdebarLoading-test.js | 14 + .../content-sidebar/__tests__/Sidebar-test.js | 9 +- .../__tests__/SidebarLoading-test.js | 14 + .../__tests__/SidebarLoadingError-test.js | 12 + .../__tests__/SidebarUtils-test.js | 62 ++++ .../__snapshots__/SIdebarLoading-test.js.snap | 9 + .../__snapshots__/Sidebar-test.js.snap | 8 +- .../__snapshots__/SidebarLoading-test.js.snap | 9 + .../SidebarLoadingError-test.js.snap | 9 + .../__snapshots__/SidebarUtils-test.js.snap | 33 +++ test/preview-react-with-sidebar.html | 96 ++++++ yarn.lock | 273 ++++++++++-------- 32 files changed, 749 insertions(+), 157 deletions(-) create mode 100644 src/elements/common/async-load/AsyncLoad.js create mode 100644 src/elements/common/async-load/__tests__/AsyncLoad-test.js create mode 100644 src/elements/common/async-load/__tests__/__snapshots__/AsyncLoad-test.js.snap create mode 100644 src/elements/common/async-load/index.js create mode 100644 src/elements/content-sidebar/SidebarLoading.js create mode 100644 src/elements/content-sidebar/SidebarLoading.scss create mode 100644 src/elements/content-sidebar/SidebarLoadingError.js create mode 100644 src/elements/content-sidebar/SidebarLoadingError.scss create mode 100644 src/elements/content-sidebar/__tests__/SIdebarLoading-test.js create mode 100644 src/elements/content-sidebar/__tests__/SidebarLoading-test.js create mode 100644 src/elements/content-sidebar/__tests__/SidebarLoadingError-test.js create mode 100644 src/elements/content-sidebar/__tests__/__snapshots__/SIdebarLoading-test.js.snap create mode 100644 src/elements/content-sidebar/__tests__/__snapshots__/SidebarLoading-test.js.snap create mode 100644 src/elements/content-sidebar/__tests__/__snapshots__/SidebarLoadingError-test.js.snap create mode 100644 src/elements/content-sidebar/__tests__/__snapshots__/SidebarUtils-test.js.snap create mode 100644 test/preview-react-with-sidebar.html diff --git a/README.md b/README.md index 08aebb0aba..9cb5d4b505 100644 --- a/README.md +++ b/README.md @@ -379,6 +379,8 @@ render( | --- | --- | --- | --- | | *tbd* | - | - | - | +# Code Splitting +[Code splitting](https://webpack.js.org/guides/code-splitting/) is currently supported for the `ContentPreview` and `ContentSidebar` UI Elements. In order to use an Element with code splitting, you will need to build the Element with webpack by importing the Element from the `es` folder in our npm package. # Questions If you have any questions, please visit our [developer forum](https://community.box.com/t5/Box-Developer-Forum/bd-p/DeveloperForum) or contact us via one of our [available support channels](https://community.box.com/t5/Community/ct-p/English). diff --git a/babel.config.js b/babel.config.js index 65f44d45e8..a2ec0bf7b0 100644 --- a/babel.config.js +++ b/babel.config.js @@ -10,6 +10,7 @@ module.exports = { '@babel/preset-flow', ], plugins: [ + '@babel/plugin-syntax-dynamic-import', '@babel/plugin-transform-flow-strip-types', '@babel/plugin-transform-object-assign', '@babel/plugin-proposal-class-properties', @@ -58,6 +59,7 @@ module.exports = { test: { plugins: [ '@babel/plugin-transform-modules-commonjs', + 'dynamic-import-node', // https://github.com/facebook/jest/issues/5920 [ 'react-intl', { diff --git a/conf/webpack.config.js b/conf/webpack.config.js index 1cacc63975..db1512ebbf 100644 --- a/conf/webpack.config.js +++ b/conf/webpack.config.js @@ -127,6 +127,22 @@ function getConfig(isReactExternalized) { } } + if (isRelease) { + // For release builds, disable code splitting. https://webpack.js.org/api/module-methods/#magic-comments + config.module.rules = [ + { + test: /\.js$/, + loader: 'string-replace-loader', + options: { + search: 'webpackMode: "lazy"', + replace: 'webpackMode: "eager"', + flags: 'g', + }, + }, + ...config.module.rules, + ]; + } + if (isRelease && language === 'en-US') { config.plugins.push( new BundleAnalyzerPlugin({ diff --git a/package.json b/package.json index 60d929da3f..1b32ac9923 100644 --- a/package.json +++ b/package.json @@ -123,6 +123,7 @@ "@babel/core": "^7.0.0", "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-flow-strip-types": "^7.0.0", "@babel/plugin-transform-object-assign": "^7.0.0", "@babel/polyfill": "^7.0.0", @@ -142,6 +143,7 @@ "babel-eslint": "^10.0.1", "babel-jest": "^23.6.0", "babel-loader": "^8.0.0", + "babel-plugin-dynamic-import-node": "^2.2.0", "babel-plugin-flow-react-proptypes": "^24.1.2", "babel-plugin-module-resolver": "^3.1.1", "babel-plugin-react-intl": "^2.4.0", @@ -225,6 +227,7 @@ "scroll-into-view-if-needed": "^1.5.0", "semantic-release": "^15.12.0", "sinon": "^2.3.7", + "string-replace-loader": "^2.1.1", "style-loader": "^0.23.0", "stylelint": "^9.6.0", "stylelint-config-standard": "^18.2.0", diff --git a/src/elements/common/async-load/AsyncLoad.js b/src/elements/common/async-load/AsyncLoad.js new file mode 100644 index 0000000000..8f8941a357 --- /dev/null +++ b/src/elements/common/async-load/AsyncLoad.js @@ -0,0 +1,58 @@ +// @flow +import * as React from 'react'; +import { retryNumOfTimes } from 'utils/function'; + +type Props = { + loader: () => Promise, + fallback?: React.Element, + errorComponent?: React.ComponentType, +}; + +type State = { + error: ?Error, +}; + +const DEFAULT_NUM_TIMES = 3; +const DEFAULT_INITIAL_DELAY = 500; +const DEFAULT_BACKOFF_FACTOR = 2; + +const NullComponent = () => null; + +const AsyncLoad = (asyncProps: Props = {}) => { + const asyncLoadWithRetry = () => + retryNumOfTimes( + (successCallback, errorCallback) => asyncProps.loader().then(successCallback, errorCallback), + DEFAULT_NUM_TIMES, + DEFAULT_INITIAL_DELAY, + DEFAULT_BACKOFF_FACTOR, + ); + const LazyComponent = React.lazy(asyncLoadWithRetry); + + return class extends React.Component { + state = { + error: null, + }; + + static getDerivedStateFromError(error: Error) { + return { + error, + }; + } + + render() { + const { fallback = null, errorComponent: ErrorComponent = NullComponent } = asyncProps; + const { error } = this.state; + if (error) { + return ; + } + + return ( + + + + ); + } + }; +}; + +export default AsyncLoad; diff --git a/src/elements/common/async-load/__tests__/AsyncLoad-test.js b/src/elements/common/async-load/__tests__/AsyncLoad-test.js new file mode 100644 index 0000000000..77546b3a3c --- /dev/null +++ b/src/elements/common/async-load/__tests__/AsyncLoad-test.js @@ -0,0 +1,51 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { retryNumOfTimes } from 'utils/function'; +import AsyncLoad from '../AsyncLoad'; + +jest.mock('utils/function', () => ({ + retryNumOfTimes: jest.fn(), +})); + +describe('elements/common/async-load/AsyncLoad', () => { + let defaultProps; + + const getAsyncComponent = (props = defaultProps) => AsyncLoad(props); + + beforeEach(() => { + defaultProps = { + loader: jest.fn(), + }; + }); + + test('should return a react component', () => { + const AsyncComponent = getAsyncComponent(); + expect(AsyncComponent.prototype).toBeInstanceOf(React.Component); + }); + + test('should load the lazy component', () => { + const AsyncComponent = getAsyncComponent(); + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); + }); + + test('should render the error component if there is an error', () => { + const errorComponent = () =>
ERROR!!
; + const AsyncComponent = getAsyncComponent({ + ...defaultProps, + errorComponent, + }); + + const wrapper = shallow(); + wrapper.setState({ + error: new Error('foo'), + }); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should not invoke the loader until component mounted', () => { + const AsyncComponent = getAsyncComponent(); + expect(retryNumOfTimes).not.toHaveBeenCalled(); + }); +}); diff --git a/src/elements/common/async-load/__tests__/__snapshots__/AsyncLoad-test.js.snap b/src/elements/common/async-load/__tests__/__snapshots__/AsyncLoad-test.js.snap new file mode 100644 index 0000000000..22451a4760 --- /dev/null +++ b/src/elements/common/async-load/__tests__/__snapshots__/AsyncLoad-test.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`elements/common/async-load/AsyncLoad should load the lazy component 1`] = ` + + + +`; + +exports[`elements/common/async-load/AsyncLoad should render the error component if there is an error 1`] = ` + +`; diff --git a/src/elements/common/async-load/index.js b/src/elements/common/async-load/index.js new file mode 100644 index 0000000000..00ea4ce99c --- /dev/null +++ b/src/elements/common/async-load/index.js @@ -0,0 +1,2 @@ +// @flow +export { default } from './AsyncLoad'; diff --git a/src/elements/content-preview/ContentPreview.js b/src/elements/content-preview/ContentPreview.js index be52785485..5469790d02 100644 --- a/src/elements/content-preview/ContentPreview.js +++ b/src/elements/content-preview/ContentPreview.js @@ -18,6 +18,7 @@ import Measure from 'react-measure'; import { decode } from 'box-react-ui/lib/utils/keys'; import makeResponsive from 'elements/common/makeResponsive'; import Internationalize from 'elements/common/Internationalize'; +import AsyncLoad from 'elements/common/async-load'; import TokenService from 'utils/TokenService'; import { isInputElement, focus } from 'utils/dom'; import { getTypedFileId } from 'utils/file'; @@ -140,6 +141,10 @@ const MARK_NAME_JS_READY = `${ORIGIN_CONTENT_PREVIEW}_${EVENT_JS_READY}`; mark(MARK_NAME_JS_READY); +const LoadableSidebar = AsyncLoad({ + loader: () => import(/* webpackMode: "lazy", webpackChunkName: "content-sidebar" */ '../content-sidebar'), +}); + class ContentPreview extends PureComponent { id: string; @@ -1116,7 +1121,7 @@ class ContentPreview extends PureComponent { /> {file && ( - import(/* webpackMode: "lazy", webpackChunkName: "content-open-with" */ '../content-open-with'), +}); + const Header = ({ canAnnotate, canDownload, @@ -58,7 +62,7 @@ const Header = ({
{shouldRenderOpenWith && ( - { constructor(props: Props) { super(props); + // eslint-disable-next-line react/prop-types const { logger } = this.props; logger.onReadyMetric({ endMarkName: MARK_NAME_JS_READY, @@ -331,6 +333,7 @@ class ActivitySidebar extends React.PureComponent { console.error(error); /* eslint-enable no-console */ + // eslint-disable-next-line react/prop-types this.props.onError(error, code, contextInfo); }; @@ -476,7 +479,7 @@ class ActivitySidebar extends React.PureComponent { return ( } + title={SidebarUtils.getTitleForView(SIDEBAR_VIEW_ACTIVITY)} actions={} > { } return ( - }> + {hasNotices && (
{hasNotices && }
)} diff --git a/src/elements/content-sidebar/MetadataSidebar.js b/src/elements/content-sidebar/MetadataSidebar.js index 923aa0f3ba..74dcd3c862 100644 --- a/src/elements/content-sidebar/MetadataSidebar.js +++ b/src/elements/content-sidebar/MetadataSidebar.js @@ -24,12 +24,14 @@ import { withLogger } from 'elements/common/logger'; import { mark } from 'utils/performance'; import { EVENT_JS_READY } from 'elements/common/logger/constants'; import SidebarContent from './SidebarContent'; +import SidebarUtils from './SidebarUtils'; import { FIELD_IS_EXTERNALLY_OWNED, FIELD_PERMISSIONS, FIELD_PERMISSIONS_CAN_UPLOAD, IS_ERROR_DISPLAYED, ORIGIN_METADATA_SIDEBAR, + SIDEBAR_VIEW_METADATA, } from '../../constants'; import './MetadataSidebar.scss'; @@ -379,7 +381,7 @@ class MetadataSidebar extends React.PureComponent { return ( } + title={SidebarUtils.getTitleForView(SIDEBAR_VIEW_METADATA)} actions={ showTemplateDropdown ? ( ( {selectedView === SIDEBAR_VIEW_DETAILS && ( - )} {selectedView === SIDEBAR_VIEW_SKILLS && ( - )} {selectedView === SIDEBAR_VIEW_ACTIVITY && ( - )} {selectedView === SIDEBAR_VIEW_METADATA && ( - + )} ); diff --git a/src/elements/content-sidebar/SidebarLoading.js b/src/elements/content-sidebar/SidebarLoading.js new file mode 100644 index 0000000000..4102471609 --- /dev/null +++ b/src/elements/content-sidebar/SidebarLoading.js @@ -0,0 +1,23 @@ +/** + * @flow + * @file a placeholder component which will be displayed while a code splitted sidebar chunk is being loaded asyncronously + * @author Box + */ +import * as React from 'react'; +import LoadingIndicator from 'box-react-ui/lib/components/loading-indicator/LoadingIndicator'; +import SidebarContent from './SidebarContent'; +import './SidebarLoading.scss'; + +type Props = { + title: React.Node, +}; + +const SidebarLoading = ({ title }: Props) => { + return ( + + + + ); +}; + +export default SidebarLoading; diff --git a/src/elements/content-sidebar/SidebarLoading.scss b/src/elements/content-sidebar/SidebarLoading.scss new file mode 100644 index 0000000000..ff5fd6a421 --- /dev/null +++ b/src/elements/content-sidebar/SidebarLoading.scss @@ -0,0 +1,5 @@ +.be { + .bcs-sidebar-loading { + padding-top: 20px; + } +} diff --git a/src/elements/content-sidebar/SidebarLoadingError.js b/src/elements/content-sidebar/SidebarLoadingError.js new file mode 100644 index 0000000000..702f777a9e --- /dev/null +++ b/src/elements/content-sidebar/SidebarLoadingError.js @@ -0,0 +1,11 @@ +import * as React from 'react'; +import DefaultError from 'elements/common/error-boundary/DefaultError'; +import './SidebarLoadingError.scss'; + +const SidebarLoadingError = () => ( +
+ +
+); + +export default SidebarLoadingError; diff --git a/src/elements/content-sidebar/SidebarLoadingError.scss b/src/elements/content-sidebar/SidebarLoadingError.scss new file mode 100644 index 0000000000..0a5112f30d --- /dev/null +++ b/src/elements/content-sidebar/SidebarLoadingError.scss @@ -0,0 +1,7 @@ +@import '../common/variables'; + +.be { + .bcs-loading-error { + width: $sidebarContentWidth; + } +} diff --git a/src/elements/content-sidebar/SidebarUtils.js b/src/elements/content-sidebar/SidebarUtils.js index 47cee3481c..b1eb2e9def 100644 --- a/src/elements/content-sidebar/SidebarUtils.js +++ b/src/elements/content-sidebar/SidebarUtils.js @@ -3,8 +3,20 @@ * @file Utility for sidebar * @author Box */ - +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import AsyncLoad from 'elements/common/async-load'; +import SidebarLoading from './SidebarLoading'; +import SidebarLoadingError from './SidebarLoadingError'; import { hasSkills as hasSkillsData } from './skills/skillUtils'; +import messages from '../common/messages'; +import { mark } from '../../utils/performance'; +import { + SIDEBAR_VIEW_SKILLS, + SIDEBAR_VIEW_ACTIVITY, + SIDEBAR_VIEW_METADATA, + SIDEBAR_VIEW_DETAILS, +} from '../../constants'; import type { MetadataSidebarProps } from './MetadataSidebar'; class SidebarUtils { @@ -119,6 +131,74 @@ class SidebarUtils { SidebarUtils.shouldRenderMetadataSidebar(props, editors)) ); } + + /** + * Gets the title for a given sidebar view + * + * @param {string} view - the view name + * @return {React.Node} - the node to render + */ + static getTitleForView(view: SidebarView): React.Node { + switch (view) { + case SIDEBAR_VIEW_SKILLS: + return ; + case SIDEBAR_VIEW_DETAILS: + return ; + case SIDEBAR_VIEW_METADATA: + return ; + case SIDEBAR_VIEW_ACTIVITY: + return ; + default: + return null; + } + } + + /** + * Marks and gets the loader for a given sidebar view + * + * @param {String} view - the view name + * @param {String} markName - the name to be used by performance.mark + * @return {Function} - a function which will resolve the module to load + */ + static getLoaderForView(view: SidebarView, markName: string): Promise { + mark(markName); + let importFn; + switch (view) { + case SIDEBAR_VIEW_SKILLS: + importFn = import(/* webpackMode: "lazy", webpackChunkName: "skills-sidebar" */ './SkillsSidebar'); + break; + case SIDEBAR_VIEW_DETAILS: + importFn = import(/* webpackMode: "lazy", webpackChunkName: "details-sidebar" */ './DetailsSidebar'); + break; + case SIDEBAR_VIEW_METADATA: + importFn = import(/* webpackMode: "lazy", webpackChunkName: "metadata-sidebar" */ './MetadataSidebar'); + break; + case SIDEBAR_VIEW_ACTIVITY: + importFn = import(/* webpackMode: "lazy", webpackChunkName: "activity-sidebar" */ './ActivitySidebar'); + break; + default: + return Promise.resolve(null); + } + + return importFn; + } + + /** + * Gets the component which async loads a given sidebar view + * + * @param {String} view - the view name + * @param {String} markName - the name to be used by performance.mark + * @param {Object} props - additional props + * @return {React.Node} - the node to render + */ + static getAsyncSidebarContent(view: SidebarView, markName: string, props: Object = {}) { + return AsyncLoad({ + errorComponent: SidebarLoadingError, + fallback: , + loader: () => this.getLoaderForView(view, markName), + ...props, + }); + } } export default SidebarUtils; diff --git a/src/elements/content-sidebar/SkillsSidebar.js b/src/elements/content-sidebar/SkillsSidebar.js index eab16a4ec6..658507af93 100644 --- a/src/elements/content-sidebar/SkillsSidebar.js +++ b/src/elements/content-sidebar/SkillsSidebar.js @@ -5,7 +5,6 @@ */ import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; import noop from 'lodash/noop'; import getProp from 'lodash/get'; import flow from 'lodash/flow'; @@ -18,7 +17,13 @@ import { withErrorBoundary } from 'elements/common/error-boundary'; import { withLogger } from 'elements/common/logger'; import API from 'api'; import SidebarContent from './SidebarContent'; -import { FIELD_PERMISSIONS_CAN_UPLOAD, SKILLS_TRANSCRIPT, ORIGIN_SKILLS_SIDEBAR } from '../../constants'; +import SidebarUtils from './SidebarUtils'; +import { + FIELD_PERMISSIONS_CAN_UPLOAD, + SKILLS_TRANSCRIPT, + ORIGIN_SKILLS_SIDEBAR, + SIDEBAR_VIEW_SKILLS, +} from '../../constants'; import SidebarSkills from './skills/SidebarSkills'; import './SkillsSidebar.scss'; @@ -232,7 +237,7 @@ class SkillsSidebar extends React.PureComponent { const { cards, errors }: State = this.state; return ( - }> + {cards ? ( { + const getWrapper = (props = {}) => shallow(); + + test('should render the component', () => { + const wrapper = getWrapper({ + tile: 'foo', + }); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/src/elements/content-sidebar/__tests__/Sidebar-test.js b/src/elements/content-sidebar/__tests__/Sidebar-test.js index 2b1b31c793..e63fd5396f 100644 --- a/src/elements/content-sidebar/__tests__/Sidebar-test.js +++ b/src/elements/content-sidebar/__tests__/Sidebar-test.js @@ -2,10 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import Sidebar from '../Sidebar'; -jest.mock('../ActivitySidebar', () => 'ActivitySidebar'); -jest.mock('../DetailsSidebar', () => 'DetailsSidebar'); -jest.mock('../SkillsSidebar', () => 'SkillsSidebar'); -jest.mock('../MetadataSidebar', () => 'MetadataSidebar'); +jest.mock('../../common/async-load', () => () => 'LoadableComponent'); describe('elements/content-sidebar/Sidebar', () => { const file = { id: 'id' }; @@ -18,25 +15,21 @@ describe('elements/content-sidebar/Sidebar', () => { test('should render skills sidebar', () => { const wrapper = getWrapper({ hasSkills: true, selectedView: 'skills' }); - expect(wrapper.find('SkillsSidebar')).toHaveLength(1); expect(wrapper).toMatchSnapshot(); }); test('should render activity sidebar', () => { const wrapper = getWrapper({ hasActivityFeed: true, selectedView: 'activity' }); - expect(wrapper.find('ActivitySidebar')).toHaveLength(1); expect(wrapper).toMatchSnapshot(); }); test('should render details sidebar', () => { const wrapper = getWrapper({ hasDetails: true, selectedView: 'details' }); - expect(wrapper.find('DetailsSidebar')).toHaveLength(1); expect(wrapper).toMatchSnapshot(); }); test('should render metadata sidebar', () => { const wrapper = getWrapper({ hasMetadata: true, selectedView: 'metadata' }); - expect(wrapper.find('MetadataSidebar')).toHaveLength(1); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/src/elements/content-sidebar/__tests__/SidebarLoading-test.js b/src/elements/content-sidebar/__tests__/SidebarLoading-test.js new file mode 100644 index 0000000000..9256f15ee7 --- /dev/null +++ b/src/elements/content-sidebar/__tests__/SidebarLoading-test.js @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import SidebarLoading from '../SidebarLoading'; + +describe('elements/content-sidebar/SidebarLoading', () => { + const getWrapper = (props = {}) => shallow(); + + test('should render the component', () => { + const wrapper = getWrapper({ + tile: 'foo', + }); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/src/elements/content-sidebar/__tests__/SidebarLoadingError-test.js b/src/elements/content-sidebar/__tests__/SidebarLoadingError-test.js new file mode 100644 index 0000000000..d515fff5aa --- /dev/null +++ b/src/elements/content-sidebar/__tests__/SidebarLoadingError-test.js @@ -0,0 +1,12 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import SidebarLoadingError from '../SidebarLoadingError'; + +describe('elements/content-sidebar/SidebarLoading', () => { + const getWrapper = (props = {}) => shallow(); + + test('should render the component', () => { + const wrapper = getWrapper(); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/src/elements/content-sidebar/__tests__/SidebarUtils-test.js b/src/elements/content-sidebar/__tests__/SidebarUtils-test.js index 9d15993f2d..36e8817b7b 100644 --- a/src/elements/content-sidebar/__tests__/SidebarUtils-test.js +++ b/src/elements/content-sidebar/__tests__/SidebarUtils-test.js @@ -1,5 +1,16 @@ +import noop from 'lodash/noop'; +import * as performance from 'utils/performance'; import SidebarUtils from '../SidebarUtils'; import * as skillUtils from '../skills/skillUtils'; +import { + SIDEBAR_VIEW_SKILLS, + SIDEBAR_VIEW_ACTIVITY, + SIDEBAR_VIEW_METADATA, + SIDEBAR_VIEW_DETAILS, +} from '../../../constants'; + +jest.mock('../../common/async-load', () => () => 'LoadableComponent'); +jest.mock('../SidebarLoadingError', () => 'sidebar-loading-error'); describe('elements/content-sidebar/SidebarUtil', () => { describe('canHaveSidebar()', () => { @@ -190,6 +201,7 @@ describe('elements/content-sidebar/SidebarUtil', () => { ).toBeTruthy(); }); }); + describe('shouldRenderSidebar()', () => { test('should return false when nothing is wanted in the sidebar', () => { expect(SidebarUtils.shouldRenderSidebar({})).toBeFalsy(); @@ -230,4 +242,54 @@ describe('elements/content-sidebar/SidebarUtil', () => { expect(SidebarUtils.shouldRenderSkillsSidebar).toHaveBeenCalledWith('props', 'file'); }); }); + + describe('getTitleForView()', () => { + test.each([SIDEBAR_VIEW_SKILLS, SIDEBAR_VIEW_DETAILS, SIDEBAR_VIEW_METADATA, SIDEBAR_VIEW_ACTIVITY])( + 'should return the title for %s', + view => { + const title = SidebarUtils.getTitleForView(view); + expect(title).toMatchSnapshot(); + }, + ); + + test('should return null if invalid view', () => { + const title = SidebarUtils.getTitleForView('foo'); + expect(title).toBe(null); + }); + }); + + describe('getLoaderForView()', () => { + const MARK_NAME = 'foo_mark'; + beforeEach(() => { + jest.spyOn(performance, 'mark').mockImplementation(noop); + }); + + test.each([SIDEBAR_VIEW_SKILLS, SIDEBAR_VIEW_DETAILS, SIDEBAR_VIEW_METADATA, SIDEBAR_VIEW_ACTIVITY, 'foo'])( + 'should return the loader for %s', + view => { + const loader = SidebarUtils.getLoaderForView(view, MARK_NAME); + expect(performance.mark).toHaveBeenCalledWith(MARK_NAME); + expect(loader).toBeInstanceOf(Promise); + }, + ); + }); + + describe('getAsyncSidebarContent()', () => { + beforeEach(() => { + jest.spyOn(SidebarUtils, 'getTitleForView').mockReturnValue('foo'); + }); + + test('should return the async component', () => { + const asyncComponent = SidebarUtils.getAsyncSidebarContent('foo_view', 'foo_mark'); + expect(asyncComponent).toMatchSnapshot(); + }); + + test('should mix in additional props', () => { + const asyncComponent = SidebarUtils.getAsyncSidebarContent('foo_view', 'foo_mark', { + foo: 'bar', + errorComponent: null, + }); + expect(asyncComponent).toMatchSnapshot(); + }); + }); }); diff --git a/src/elements/content-sidebar/__tests__/__snapshots__/SIdebarLoading-test.js.snap b/src/elements/content-sidebar/__tests__/__snapshots__/SIdebarLoading-test.js.snap new file mode 100644 index 0000000000..c7871d09dc --- /dev/null +++ b/src/elements/content-sidebar/__tests__/__snapshots__/SIdebarLoading-test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`elements/content-sidebar/SidebarLoading should render the component 1`] = ` + + + +`; diff --git a/src/elements/content-sidebar/__tests__/__snapshots__/Sidebar-test.js.snap b/src/elements/content-sidebar/__tests__/__snapshots__/Sidebar-test.js.snap index 695de18848..2c96d54019 100644 --- a/src/elements/content-sidebar/__tests__/__snapshots__/Sidebar-test.js.snap +++ b/src/elements/content-sidebar/__tests__/__snapshots__/Sidebar-test.js.snap @@ -2,7 +2,7 @@ exports[`elements/content-sidebar/Sidebar should render activity sidebar 1`] = ` - - @@ -23,7 +23,7 @@ exports[`elements/content-sidebar/Sidebar should render details sidebar 1`] = ` exports[`elements/content-sidebar/Sidebar should render metadata sidebar 1`] = ` - @@ -33,7 +33,7 @@ exports[`elements/content-sidebar/Sidebar should render no sidebar 1`] = ` - + + +`; diff --git a/src/elements/content-sidebar/__tests__/__snapshots__/SidebarLoadingError-test.js.snap b/src/elements/content-sidebar/__tests__/__snapshots__/SidebarLoadingError-test.js.snap new file mode 100644 index 0000000000..7b1e8d2497 --- /dev/null +++ b/src/elements/content-sidebar/__tests__/__snapshots__/SidebarLoadingError-test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`elements/content-sidebar/SidebarLoading should render the component 1`] = ` +
+ +
+`; diff --git a/src/elements/content-sidebar/__tests__/__snapshots__/SidebarUtils-test.js.snap b/src/elements/content-sidebar/__tests__/__snapshots__/SidebarUtils-test.js.snap new file mode 100644 index 0000000000..03a1c6fb57 --- /dev/null +++ b/src/elements/content-sidebar/__tests__/__snapshots__/SidebarUtils-test.js.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`elements/content-sidebar/SidebarUtil getAsyncSidebarContent() should mix in additional props 1`] = `"LoadableComponent"`; + +exports[`elements/content-sidebar/SidebarUtil getAsyncSidebarContent() should return the async component 1`] = `"LoadableComponent"`; + +exports[`elements/content-sidebar/SidebarUtil getTitleForView() should return the title for activity 1`] = ` + +`; + +exports[`elements/content-sidebar/SidebarUtil getTitleForView() should return the title for details 1`] = ` + +`; + +exports[`elements/content-sidebar/SidebarUtil getTitleForView() should return the title for metadata 1`] = ` + +`; + +exports[`elements/content-sidebar/SidebarUtil getTitleForView() should return the title for skills 1`] = ` + +`; diff --git a/test/preview-react-with-sidebar.html b/test/preview-react-with-sidebar.html new file mode 100644 index 0000000000..7449e1dacd --- /dev/null +++ b/test/preview-react-with-sidebar.html @@ -0,0 +1,96 @@ + + + + + + Preview Test Page + + + + + +
+
+ + +
+ +
+
+ + + diff --git a/yarn.lock b/yarn.lock index 50a42899da..deead73add 100644 --- a/yarn.lock +++ b/yarn.lock @@ -336,6 +336,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-dynamic-import@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612" + integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-flow@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.0.0.tgz#70638aeaad9ee426bc532e51523cff8ff02f6f17" @@ -1151,9 +1158,9 @@ integrity sha512-k2tWTQU8G4+iSMvqKi0Q9IIsWAp/n8xzdZS4Q4YVIltApoMA00wFBFdlJnmoaK1/z7B0Cy0yPe6GgXteSmdUNw== "@types/node@*": - version "8.0.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.55.tgz#015966c0af809216b8a46cc527b5c211994d36f0" - integrity sha512-K8w0FWNsIRcw615d/Et90wMRvLfg8XH1T77fC0xObbusE3+eXwnitdoF9j0CS9zBt8A57J/TKgRVe7RX9ZlT1g== + version "10.12.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" + integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== "@types/prop-types@*": version "15.5.5" @@ -2150,6 +2157,13 @@ babel-messages@^6.23.0: dependencies: babel-runtime "^6.22.0" +babel-plugin-dynamic-import-node@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.2.0.tgz#c0adfb07d95f4a4495e9aaac6ec386c4d7c2524e" + integrity sha512-fP899ELUnTaBcIzmrW7nniyqqdYWrWuJUyPWHxFa/c7r7hS6KC8FscNfLlBNIoPSc55kYMGEEKjPjJGCLbE1qA== + dependencies: + object.assign "^4.1.0" + babel-plugin-flow-react-proptypes@^24.1.2: version "24.1.2" resolved "https://registry.yarnpkg.com/babel-plugin-flow-react-proptypes/-/babel-plugin-flow-react-proptypes-24.1.2.tgz#efd10966b609b01b50c4252c29759f1e333a2dc0" @@ -3414,11 +3428,6 @@ color@^3.0.0: color-convert "^1.9.1" color-string "^1.5.2" -colors@0.5.x: - version "0.5.1" - resolved "https://registry.yarnpkg.com/colors/-/colors-0.5.1.tgz#7d0023eaeb154e8ee9fce75dcb923d0ed1667774" - integrity sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q= - colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -4241,9 +4250,9 @@ css-url-regex@^1.1.0: integrity sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w= css-what@2.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" - integrity sha1-lGfQMsOM+u+58teVASUwYvh/ob0= + version "2.1.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" + integrity sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ== cssesc@^0.1.0: version "0.1.0" @@ -4581,7 +4590,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.2: +define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -4804,9 +4813,9 @@ domain-browser@^1.1.1: integrity sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw= domelementtype@1, domelementtype@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" - integrity sha1-sXrtguirWeUt2cGbF1bg/BhyBMI= + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== domelementtype@~1.1.1: version "1.1.3" @@ -4821,13 +4830,13 @@ domexception@^1.0.1: webidl-conversions "^4.0.2" domhandler@^2.3.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259" - integrity sha1-iS5HAAqZvlW783dP/qBWHYh5wlk= + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== dependencies: domelementtype "1" -domutils@1.5.1, domutils@^1.5.1: +domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= @@ -4835,6 +4844,14 @@ domutils@1.5.1, domutils@^1.5.1: dom-serializer "0" domelementtype "1" +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + dot-prop@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" @@ -4988,9 +5005,9 @@ enhanced-resolve@^4.1.0: tapable "^1.0.0" entities@^1.1.1, entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" - integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== env-ci@^3.0.0: version "3.1.0" @@ -5001,38 +5018,40 @@ env-ci@^3.0.0: java-properties "^0.2.9" enzyme-adapter-react-16@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.5.0.tgz#50af8d76a45fe0915de932bd95d34cdca75c0be3" - integrity sha512-R2LcVvMB2UwPH763d5jDtVedAIcEj+uZjOnq0nd1sOUs6z8TDbyHDvt8VwfrS4wMt7CawoyPmH0XzC8MtEqqDw== + version "1.7.1" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.7.1.tgz#c37c4cb0fd75e88a063154a7a88096474914496a" + integrity sha512-OQXKgfHWyHN3sFu2nKj3mhgRcqIPIJX6aOzq5AHVFES4R9Dw/vCBZFMPyaG81g2AZ5DogVh39P3MMNUbqNLTcw== dependencies: - enzyme-adapter-utils "^1.8.0" + enzyme-adapter-utils "^1.9.0" function.prototype.name "^1.1.0" object.assign "^4.1.0" object.values "^1.0.4" prop-types "^15.6.2" - react-is "^16.4.2" + react-is "^16.6.1" react-test-renderer "^16.0.0-0" -enzyme-adapter-utils@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.8.0.tgz#ee9f07250663a985f1f2caaf297720787da559f1" - integrity sha512-K9U2RGr1pvWPGEAIRQRVH4UdlqzpfLsKonuHyAK6lxu46yfGsMDVlO3+YvQwQpVjVw8eviEVIOmlFAnMbIhv/w== +enzyme-adapter-utils@^1.9.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz#5836169f68b9e8733cb5b69cad5da2a49e34f550" + integrity sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA== dependencies: function.prototype.name "^1.1.0" object.assign "^4.1.0" + object.fromentries "^2.0.0" prop-types "^15.6.2" + semver "^5.6.0" enzyme-to-json@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.3.4.tgz#67c6040e931182f183418af2eb9f4323258aa77f" - integrity sha1-Z8YEDpMRgvGDQYry659DIyWKp38= + version "3.3.5" + resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.3.5.tgz#f8eb82bd3d5941c9d8bc6fd9140030777d17d0af" + integrity sha512-DmH1wJ68HyPqKSYXdQqB33ZotwfUhwQZW3IGXaNXgR69Iodaoj8TF/D9RjLdz4pEhGq2Tx2zwNUIjBuqoZeTgA== dependencies: lodash "^4.17.4" enzyme@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.6.0.tgz#d213f280a258f61e901bc663d4cc2d6fd9a9dec8" - integrity sha512-onsINzVLGqKIapTVfWkkw6bYvm1o4CyJ9s8POExtQhAkVa4qFDW6DGCQGRy/5bfZYk+gmUbMNyayXiWDzTkHFQ== + version "3.8.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.8.0.tgz#646d2d5d0798cb98fdec39afcee8a53237b47ad5" + integrity sha512-bfsWo5nHyZm1O1vnIsbwdfhU989jk+squU9NKvB+Puwo5j6/Wg9pN5CO0YJelm98Dao3NPjkDZk+vvgwpMwYxw== dependencies: array.prototype.flat "^1.2.1" cheerio "^1.0.0-rc.2" @@ -5087,7 +5106,19 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.10.0, es-abstract@^1.4.3, es-abstract@^1.5.0: +es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" + integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-keys "^1.0.12" + +es-abstract@^1.4.3: version "1.12.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" integrity sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA== @@ -5109,7 +5140,7 @@ es-abstract@^1.5.1: is-callable "^1.1.3" is-regex "^1.0.4" -es-abstract@^1.6.1, es-abstract@^1.7.0: +es-abstract@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c" integrity sha1-363ndOAb/Nl/lhgCmMRJyGI/uUw= @@ -5119,7 +5150,7 @@ es-abstract@^1.6.1, es-abstract@^1.7.0: is-callable "^1.1.3" is-regex "^1.0.3" -es-to-primitive@^1.1.1: +es-to-primitive@^1.1.1, es-to-primitive@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== @@ -6418,16 +6449,11 @@ fstream@^1.0.0, fstream@^1.0.2: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.0.2, function-bind@^1.1.1: +function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function-bind@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" - integrity sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E= - function.name-polyfill@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/function.name-polyfill/-/function.name-polyfill-1.0.6.tgz#c54e37cae0a77dfcb49d47982815b0826b5c60d9" @@ -7192,7 +7218,19 @@ html-tags@^2.0.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= -htmlparser2@^3.9.1, htmlparser2@^3.9.2: +htmlparser2@^3.9.1: + version "3.10.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.0.tgz#5f5e422dcf6119c0d983ed36260ce9ded0bee464" + integrity sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ== + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.0.6" + +htmlparser2@^3.9.2: version "3.9.2" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" integrity sha1-G9+HrMoPP55T+k/M6w9LTLsAszg= @@ -9533,7 +9571,7 @@ lodash.without@~4.4.0: resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= -lodash@4.17.11, lodash@^4.13.1, lodash@^4.17.11: +lodash@4.17.11, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.4: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== @@ -9543,7 +9581,7 @@ lodash@^3.10.1: resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= -lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.4: +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= @@ -10290,6 +10328,11 @@ mousetrap@^1.6.1: resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.2.tgz#caadd9cf886db0986fb2fee59a82f6bd37527587" integrity sha512-jDjhi7wlHwdO6q6DS7YRmSHcuI+RVxadBkLt3KHrhd3C2b+w5pKefg3oj5beTcHZyVFA9Aksf+yEE1y5jxUjVA== +moo@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" + integrity sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -10391,13 +10434,15 @@ natural-compare@^1.4.0: integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= nearley@^2.7.10: - version "2.11.0" - resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.11.0.tgz#5e626c79a6cd2f6ab9e7e5d5805e7668967757ae" - integrity sha512-clqqhEuP0ZCJQ85Xv2I/4o2Gs/fvSR6fCg5ZHVE2c8evWyNk2G++ih4JOO3lMb/k/09x6ihQ2nzKUlB/APCWjg== + version "2.16.0" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.16.0.tgz#77c297d041941d268290ec84b739d0ee297e83a7" + integrity sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg== dependencies: - nomnom "~1.6.2" + commander "^2.19.0" + moo "^0.4.3" railroad-diagrams "^1.0.0" - randexp "^0.4.2" + randexp "0.4.6" + semver "^5.4.1" needle@^2.2.1: version "2.2.4" @@ -10584,14 +10629,6 @@ nodesecurity-npm-utils@^6.0.0: resolved "https://registry.yarnpkg.com/nodesecurity-npm-utils/-/nodesecurity-npm-utils-6.0.0.tgz#5fb5974008c0c97a5c01844faa8fd3fc5520806c" integrity sha512-NLRle1woNaT2orR6fue2jNqkhxDTktgJj3sZxvR/8kp21pvOY7Gwlx5wvo0H8ZVPqdgd2nE2ADB9wDu5Cl8zNg== -nomnom@~1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971" - integrity sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE= - dependencies: - colors "0.5.x" - underscore "~1.4.4" - noms@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859" @@ -10986,13 +11023,20 @@ nsp@^3.2.1: wreck "^12.5.1" yargs "^9.0.1" -nth-check@^1.0.1, nth-check@~1.0.1: +nth-check@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" integrity sha1-mSms32KPwsQQmN6rgqxYDPFJquQ= dependencies: boolbase "~1.0.0" +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" @@ -11042,12 +11086,7 @@ object-is@^1.0.1: resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY= -object-keys@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" - integrity sha1-xUYBd4rVYPEULODgG8yotW0TQm0= - -object-keys@^1.0.12: +object-keys@^1.0.11, object-keys@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" integrity sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag== @@ -11070,13 +11109,23 @@ object.assign@^4.1.0: object-keys "^1.0.11" object.entries@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f" - integrity sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8= + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" + integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + +object.fromentries@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" + integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== dependencies: define-properties "^1.1.2" - es-abstract "^1.6.1" - function-bind "^1.1.0" + es-abstract "^1.11.0" + function-bind "^1.1.1" has "^1.0.1" object.getownpropertydescriptors@^2.0.3: @@ -11103,14 +11152,14 @@ object.pick@^1.3.0: isobject "^3.0.1" object.values@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a" - integrity sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo= + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" + integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== dependencies: - define-properties "^1.1.2" - es-abstract "^1.6.1" - function-bind "^1.1.0" - has "^1.0.1" + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" @@ -12637,7 +12686,7 @@ ramda@0.24.1: resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc= -randexp@^0.4.2: +randexp@0.4.6: version "0.4.6" resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== @@ -12837,10 +12886,10 @@ react-intl@^2.3.0: intl-relativeformat "^2.0.0" invariant "^2.1.1" -react-is@^16.4.2, react-is@^16.5.2: - version "16.5.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3" - integrity sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ== +react-is@^16.6.1, react-is@^16.7.0: + version "16.7.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.7.0.tgz#c1bd21c64f1f1364c6f70695ec02d69392f41bfa" + integrity sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g== react-is@^16.6.0, react-is@^16.7.0: version "16.7.0" @@ -12966,14 +13015,14 @@ react-styleguidist@^8.0.6: webpack-merge "^4.1.4" react-test-renderer@^16.0.0-0: - version "16.5.2" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.5.2.tgz#92e9d2c6f763b9821b2e0b22f994ee675068b5ae" - integrity sha512-AGbJYbCVx1J6jdUgI4s0hNp+9LxlgzKvXl0ROA3DHTrtjAr00Po1RhDZ/eAq2VC/ww8AHgpDXULh5V2rhEqqJg== + version "16.7.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.7.0.tgz#1ca96c2b450ab47c36ba92cd8c03fcefc52ea01c" + integrity sha512-tFbhSjknSQ6+ttzmuGdv+SjQfmvGcq3PFKyPItohwhhOBmRoTf1We3Mlt3rJtIn85mjPXOkKV+TaKK4irvk9Yg== dependencies: object-assign "^4.1.1" prop-types "^15.6.2" - react-is "^16.5.2" - schedule "^0.5.0" + react-is "^16.7.0" + scheduler "^0.12.0" react-tether@^0.6.1: version "0.6.1" @@ -13144,7 +13193,7 @@ read@1, read@~1.0.1, read@~1.0.7: string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6: +readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6: version "2.3.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.0.tgz#640f5dcda88c91a8dc60787145629170813a1ed2" integrity sha512-c7KMXGd4b48nN3OJ1U9qOsn6pXNzf6kLd3kdZCkg2sxAcoiufInqF0XckwEnlrcwuaYwonlNK8GQUIOC/WC7sg== @@ -13157,7 +13206,7 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable string_decoder "~1.0.0" util-deprecate "~1.0.1" -readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.2, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -13865,21 +13914,16 @@ safe-buffer@5.1.1, safe-buffer@^5.1.1, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== -safe-buffer@5.1.2, safe-buffer@^5.1.2: +safe-buffer@5.1.2, safe-buffer@^5.1.2, safe-buffer@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.1.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223" integrity sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ== -safe-buffer@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" - integrity sha1-0mPKVGls2KMGtcplUekt5XkY++c= - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -13960,13 +14004,6 @@ sax@^1.2.4, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -schedule@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/schedule/-/schedule-0.5.0.tgz#c128fffa0b402488b08b55ae74bb9df55cc29cc8" - integrity sha512-HUcJicG5Ou8xfR//c2rPT0lPIRR09vVvN81T9fqfVgBmhERUbDEQoYKjpBxbueJnCPpSu2ujXzOnRQt6x9o/jw== - dependencies: - object-assign "^4.1.1" - scheduler@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b" @@ -14756,6 +14793,14 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" +string-replace-loader@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-2.1.1.tgz#b72e7b57b6ef04efe615aff0ad989b5c14ca63d1" + integrity sha512-0Nvw1LDclF45AFNuYPcD2Jvkv0mwb/dQSnJZMvhqGrT+zzmrpG3OJFD600qfQfNUd5aqfp7fCm2mQMfF7zLbyQ== + dependencies: + loader-utils "^1.1.0" + schema-utils "^0.4.5" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -14816,14 +14861,7 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.1.0" -string_decoder@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.2.tgz#b29e1f4e1125fa97a10382b8a533737b7491e179" - integrity sha1-sp4fThEl+pehA4K4pTNze3SR4Xk= - dependencies: - safe-buffer "~5.0.1" - -string_decoder@~1.0.3: +string_decoder@~1.0.0, string_decoder@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" integrity sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ== @@ -15667,11 +15705,6 @@ umask@^1.1.0, umask@~1.1.0: resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= -underscore@~1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" - integrity sha1-YaajIBBiKvoHljvzJSA88SI51gQ= - unherit@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.0.tgz#6b9aaedfbf73df1756ad9e316dd981885840cd7d"