diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..206aee1295 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +node_modules +dist +vendor +/.idea +/.vscode +/example-integrations +/www +/docs-site/cache +/docs-site/vendor +/docs-site/node_modules +# /apps/pattern-lab/cache diff --git a/.travis.yml b/.travis.yml index 19cddcd488..78d31e36c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ -sudo: false +sudo: required dist: trusty +services: + - docker + stages: - build-and-deploy # lint code and docs @@ -36,14 +39,26 @@ jobs: - stage: build-and-deploy name: 'Build + Deploy' - before_script: - - phpenv config-rm xdebug.ini - - composer global require hirak/prestissimo - - yarn run setup + # before_script: + # - phpenv config-rm xdebug.ini + # - composer global require hirak/prestissimo + # - yarn run setup script: - - yarn run build - - yarn run deploy + # - yarn run build + # - yarn run deploy # - sh ./scripts/update-read-only-git-repos.sh @todo: run this only on Bolt releases + - docker --log-level error pull boltdesignsystem/bolt || true + - docker build --cache-from boltdesignsystem/bolt --tag boltdesignsystem/bolt . + - docker images + - echo $DOCKER_HUB_PASS | docker login --username $DOCKER_HUB_USER --password-stdin + - GIT_SHA="$(git rev-parse --short HEAD)" + - docker tag boltdesignsystem/bolt boltdesignsystem/bolt:latest + - docker tag boltdesignsystem/bolt boltdesignsystem/bolt:${GIT_SHA} + - docker --log-level error push boltdesignsystem/bolt:latest + - docker --log-level error push boltdesignsystem/bolt:${GIT_SHA} + after_script: + - cd scripts && npm install + - npm run deploy cache: yarn: true directories: @@ -70,7 +85,7 @@ before_install: notifications: email: on_success: never - on_failure: always + on_failure: never # @todo re-enable slack: secure: cNto+gWAoK1JM9jBNG4i4rMSybv3twMbqlFSCohQFBDMwKFMdlyWqFDX6iYKtHxWEDzrZyRz3qiJ8/S44mgjeKJ/xHbHDtPchp/KL2P1htipvwD2EZXobcBEGl83v2rmtFO1WNJUPB3RIJE2yt1wJsX7NIXpDw82hePmaIvNJmtbLpK/J5uaFqGNHIsctmULgVmGSNSTyK4nYxxjNNLd0EvO37Y6VN8FhsKNu2NHMKeeQxinEvETDUh8XuqXZYNWE3PBvVa4OiDhgnr5K27jsnWX+wEmqg0xY+CMf7mUSTqVN61fA7LnHyM0qcGGmB6YTv4QYLMwPydp+nsjDcm3St9D+KOTsQ4ExOaEAL/6EnAEpl8GtxST+ytdqswhCC4yMCO61Hy+M5AoXgDSGrrXHgZakDMAcEVcJdH38791hRxcuM3ldVmHAlAWFdgRLG5rRMVh3qoXz7jbraoTdjyKMegQIQdKR2SX7O9Dv0EEtLz4lTFN2RENvAjLggUPPU+ESoUHmSbwmPGnt7jy3ra2AI3nnYpfn/0e6Op/A3z7HLbdm3XyuNWoTPhy1mc4Adca+HosJ37UPv7nDRIGds1sKYAeWq94+rEk+/6IQ/oRIDRhSYsQbLLWnU6DH4o7iOj7D+X/ngjqmF75nW2s5+7rtdBHFvNzOJalCKHiDTMfdlQ= diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..ee30a49480 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM basaltinc/docker-node-php-base:latest +# @todo replace with `boltdesignsystem/bolt-docker:latest` once that is configured correctly +WORKDIR /app +COPY . . +EXPOSE 3123 +RUN composer global require hirak/prestissimo +RUN yarn run setup +RUN yarn run build + +CMD yarn serve diff --git a/deploys/Dockerfile b/deploys/Dockerfile new file mode 100644 index 0000000000..a89aacaf74 --- /dev/null +++ b/deploys/Dockerfile @@ -0,0 +1,4 @@ +FROM boltdesignsystem/bolt:${DOCKER_TAG:-latest} +RUN echo "Using DOCKER_TAG: ${DOCKER_TAG:-latest}" +# tag being built from will be env var `$DOCKER_TAG` if set, else it will be `latest` +EXPOSE 3123 diff --git a/deploys/now.json b/deploys/now.json new file mode 100644 index 0000000000..c1c767e874 --- /dev/null +++ b/deploys/now.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "name": "boltdesignsystem", + "type": "docker", + "scale": { + "sfo1": { + "min": 1, + "max": 1 + } + }, + "features": { + "cloud": "v1" + } +} diff --git a/docs-site/.boltrc.js b/docs-site/.boltrc.js index df64a7d2a9..7fe808b5e1 100644 --- a/docs-site/.boltrc.js +++ b/docs-site/.boltrc.js @@ -6,7 +6,6 @@ const config = { // array of languages to compile the design system. note, these are ignored when the --i18n flag is set to false // Note: if lang is defined, the first item is currently the one used by default in the Pattern Lab build, pending further iterations on this! // lang: ['en', 'ja'], - lang: ['en'], renderingService: false, // starts PHP service for rendering Twig templates openServerAtStart: false, @@ -73,7 +72,7 @@ const config = { components: { global: [ '@bolt/global', - // '@bolt/schema-form', + '@bolt/schema-form', '@bolt/components-placeholder', '@bolt/components-action-blocks', '@bolt/components-dropdown', diff --git a/docs-site/src/components/handle-iframe-height/index.js b/docs-site/src/components/handle-iframe-height/index.js index 5f4a2eb6a3..0b7b5010d6 100644 --- a/docs-site/src/components/handle-iframe-height/index.js +++ b/docs-site/src/components/handle-iframe-height/index.js @@ -10,6 +10,8 @@ if (window.self !== window.top) { } else { import('iframe-resizer/src/iframeResizer.js').then(module => { const iFrameResize = module.default; - iFrameResize(); + iFrameResize({ + checkOrigin: false, + }); }); } diff --git a/docs-site/src/components/pattern-lab-utils/docs.twig b/docs-site/src/components/pattern-lab-utils/docs.twig index bc781b1e17..7dd94ede66 100644 --- a/docs-site/src/components/pattern-lab-utils/docs.twig +++ b/docs-site/src/components/pattern-lab-utils/docs.twig @@ -3,11 +3,82 @@ {% set readmeFile = "@bolt-components-#{componentName}/README.md" %} {% endif %} -{% if schemaForm and schema and bolt.data.config.renderingService == true %} +{% if schemaForm and schema %} {% set schemaForm = schemaForm|merge({ schema: schema }) %} - -
+ {% set definitions = {} %} + {% set properties = {} %} + + {% for key, prop in schema.properties %} + {% set propertyValues = {} %} + {% set propertyValues = schema.properties[key] %} + + {% if prop.ref %} + {% set refName = prop.ref|split("/")[0]|replace({'@':''}) %} + {% set referenceData = get_data(prop.ref) %} + {% set reference = {} %} + + {% if referenceData.type %} + {% set reference = reference|merge({ + type: referenceData.type + }) %} + {% endif %} + + {% if referenceData.properties %} + {% set reference = reference|merge({ + properties: referenceData.properties + }) %} + {% endif %} + + {% if referenceData.required %} + {% set reference = reference|merge({ + required: referenceData.required + }) %} + {% endif %} + + {% if referenceData.enum %} + {% set reference = reference|merge({ + enum: referenceData.enum + }) %} + {% endif %} + + {% if referenceData.default %} + {% set reference = reference|merge({ + default: referenceData.default + }) %} + {% endif %} + + {% set definitions = definitions | merge({ + ("" ~ refName): reference + }) %} + + {% set propertyValues = propertyValues|merge({ + "$ref": '#/definitions/' ~ prop.ref|split("/")[0]|replace({'@': ''}) + }) %} + {# {% set newSchema = newSchema|merge(newSchema.properties[key]['$ref'] = '#/definitions/' ~ prop.ref %} + {% set newSchema newSchema|merge({ + definitions + }.['definitions'][prop.ref] = reference %} #} + {% endif %} + + {% set properties = properties|merge({ + ("" ~ key): propertyValues + }) %} + {% endfor %} + + {% if definitions %} + {% set schema = schema|merge({ + definitions: definitions, + }) %} + {% endif %} + + {% if properties %} + {% set schema = schema|merge({ + properties: properties, + }) %} + {% endif %} + +
{% endif %} diff --git a/docs-site/src/components/schema-form/component-explorer.js b/docs-site/src/components/schema-form/component-explorer.js new file mode 100644 index 0000000000..da8754a498 --- /dev/null +++ b/docs-site/src/components/schema-form/component-explorer.js @@ -0,0 +1,262 @@ +import Form from 'react-jsonschema-form'; +import qs from 'querystring'; +import { define, props } from '@bolt/core/utils'; +import { prepSchema } from './utils'; +import ResponsiveLocalStorageLayout from './grid-layout'; +import { h, withPreact } from '@bolt/core/renderers'; +// import { store } from '@bolt/core/store.js'; // redux store + +import { + updateComponentExplorerForm, + updateComponentExplorerPreview, +} from '@bolt/core/actions'; // redux actions needed by this element. + +import styles from './component-explorer.scss'; +import globalStyles from '@bolt/global/styles/index.scss'; + +const Iframe = (data) => { + const { linkTags, headScripts, bodyScripts, html } = data.props; + + return ( + + ); +}; + + +@define +export default class ComponentExplorer extends withPreact() { + static is = 'bolt-component-explorer'; + + static props = { + schema: props.object, + formData: props.object, + template: props.string, + initialLayout: props.any, //PropTypes.oneOf(['vertical', 'horizontal']), + }; + + constructor(props) { + super(props); + this.timeout = null; + this.state = { + formData: '', + renderedHTML: '', + schema: '', + }; + this.requestRender = this.requestRender.bind(this); + this.onFormChange = this.onFormChange.bind(this); + // this.requestRender = debouce(this.requestRender.bind(this), 10); + // this.requestRender = debouce(this.requestRender.bind(this), 150); + + } + + connecting() { + super.connecting && super.connecting(); + + this.setState({ + formData: this.props.formData, + renderedHTML: '', + schema: prepSchema(this.props.schema), + }); + // const state = store.getState(); + // if (state.componentExplorer.formData !== undefined){ + // this.setState({ + // formData: state.componentExplorer.formData !== '' ? state.componentExplorer.formData : this.props.formData, + // renderedHTML: state.componentExplorer.renderedHTML || '', + // schema: prepSchema(this.props.schema), + // }); + // } else { + // this.setState({ + // formData: this.props.formData, + // renderedHTML: state.componentExplorer.renderedHTML || '', + // schema: prepSchema(this.props.schema), + // }); + // } + + } + + // stateChanged(state) { + // this.setState({ + // formData: state.componentExplorer.formData || this.state.formData, + // renderedHTML: state.componentExplorer.renderedHTML || '', + // }); + // } + + resetForm() { + this.setState({ + formData: '' + }); + this.setState({ + formData: this.props.formData, + }); + + // store.dispatch(updateComponentExplorerForm(this.props.formData)); + } + + async onFormChange(value) { + this.state.formData = value.formData; + this.requestRender(value.formData); + // // store.dispatch(updateComponentExplorerForm(value.formData)); + }; + + async requestRender(formData) { + const self = this; + + self.state.isTyping = true; + + if (formData && formData !== '') { + + const res = await fetch( + `/api/render?${qs.stringify({ + template: this.props.template, + })}`, + { + method: 'POST', + body: JSON.stringify(formData), + headers: { + 'Content-Type': 'application/json', + }, + }, + ); + + const body = await res.text(); + + if (!res.ok) { + console.error(`Error: rendering ${this.props.template}`, body); + return; + } else { + if (self.state.renderedHTML !== body){ + if (self.state.typingTimeout) { + clearTimeout(self.state.typingTimeout); + } + + self.state.isTyping = false; + self.state.typingTimeout = setTimeout(function () { + if (self.state.isTyping === false){ + console.log('setting state for updated HTML'); + self.setState({ + // formData: formData, + renderedHTML: body, + }); + } + }, 200); + } + } + // store.dispatch(updateComponentExplorerPreview(body)); + } + } + + render() { + console.log('render'); + const { initialLayout } = this.props; + const linkTags = ``; + const bodyScripts = ``; + const isHorizontal = initialLayout === 'horizontal'; + + const schema = prepSchema(this.props.schema); + + const iFrameProps = { + linkTags, + headScripts: '', + bodyScripts, + html: this.state.renderedHTML, + }; + + return ( +
+ {this.addStyles([styles, globalStyles])} +
+
+
+ - {/*
*/} -
-
- - -
-
-
this.requestRender(data.formData)} - onError={data => console.error('Error in Schema Form', data)} - /> -
- -
-
- ); - } -} - -SchemaForm.defaultProps = { - initialData: {}, - layout: 'horizontal', -}; - -SchemaForm.propTypes = { - schema: PropTypes.object.isRequired, - initialData: PropTypes.object, - demoTemplate: PropTypes.string.isRequired, - layout: PropTypes.oneOf(['vertical', 'horizontal']), -}; diff --git a/docs-site/src/components/schema-form/utils.js b/docs-site/src/components/schema-form/utils.js new file mode 100644 index 0000000000..4158c3866f --- /dev/null +++ b/docs-site/src/components/schema-form/utils.js @@ -0,0 +1,38 @@ +/** + * Prepare schema by removing properties + merging $ref data that the Form can't handle normally + * @param {object} schema - original schema to be cleaned up + * @returns {object} newSchema + */ +export function prepSchema(schema) { + // Can't use `Object.assign` + const newSchema = schema; + if (newSchema.not) { + delete newSchema.not; + } + + for (let property in newSchema.properties) { + if (newSchema.properties[property].title === 'DEPRECATED') { + delete newSchema.properties[property]; + } + + try { + if (newSchema.properties[property].ref) { + delete newSchema.properties[property].ref; + } + } catch (error) { + console.log('ref does not exist!'); + } + + try { + if (newSchema.properties[property]['$ref']) { + if (newSchema.properties[property].properties) { + delete newSchema.properties[property].properties; + } + } + } catch (error) { + console.log('$ref does not exist!'); + } + } + + return newSchema; +} diff --git a/docs-site/src/index.js b/docs-site/src/index.js index a848745d6d..8d69a4798d 100644 --- a/docs-site/src/index.js +++ b/docs-site/src/index.js @@ -1,4 +1,5 @@ import './components/version-selector/version-selector'; +import './components/schema-form'; import './components/handle-iframe-height'; import './pages/pattern-lab/_patterns/02-components/card/__tests__'; // import './pages/pattern-lab/_patterns/01-styleguide/100-rendering-performance/bolt-preact-test'; diff --git a/docs-site/src/index.scss b/docs-site/src/index.scss index 1d0f88497a..aaa1a7a339 100644 --- a/docs-site/src/index.scss +++ b/docs-site/src/index.scss @@ -7,6 +7,7 @@ @import './components/pattern-lab-hacks/pattern-lab-hacks.scss'; @import './components/pattern-lab-demos/pattern-lab-demos.scss'; @import './components/schema-table/schema-table.scss'; +@import './components/schema-form/component-explorer.scss'; @import './pages/pattern-lab/_patterns/01-visual-styles/00-color-palette/_color-swatch'; @import './pages/pattern-lab/_patterns/01-visual-styles/15-breakpoints/_breakpoints'; @import './components/pattern-lab-utils/_sassdoc.scss'; diff --git a/docs-site/src/pages/pattern-lab/_patterns/02-components/button/00-button-docs.twig b/docs-site/src/pages/pattern-lab/_patterns/02-components/button/00-button-docs.twig index 9252fbcad8..2c10080b55 100644 --- a/docs-site/src/pages/pattern-lab/_patterns/02-components/button/00-button-docs.twig +++ b/docs-site/src/pages/pattern-lab/_patterns/02-components/button/00-button-docs.twig @@ -6,5 +6,16 @@ {% include "@utils/docs.twig" with { componentName: "button", - usage: usage + usage: usage, + schemaForm: { + template: '@bolt-components-button/button.twig', + layout: "horizontal", + initialData: { + text: "Button w/ Icon `after` Text", + icon: { + name: "chevron-right", + position: "after" + } + }, + }, } only %} diff --git a/docs-site/src/pages/pattern-lab/_patterns/02-components/navbar/00-navbar-docs.twig b/docs-site/src/pages/pattern-lab/_patterns/02-components/navbar/00-navbar-docs.twig index 94d429893a..97c1a29324 100644 --- a/docs-site/src/pages/pattern-lab/_patterns/02-components/navbar/00-navbar-docs.twig +++ b/docs-site/src/pages/pattern-lab/_patterns/02-components/navbar/00-navbar-docs.twig @@ -28,28 +28,33 @@ componentName: "navbar", usage: usage, schemaForm: { - demoTemplate: '@bolt-components-navbar/navbar.twig', + template: '@bolt-components-navbar/navbar.twig', layout: "horizontal", initialData: { - title: { - tag: "h2", - text: "Title Text", - icon: { - name: "brand-operations" + width: "auto", + "title": { + "tag": "h2", + "text": "Title Text", + "icon": { + "name": "marketing-gray" } }, - links: [ + "links": [ { - text: "Link 1 Text", - url: "link-1-url" + "text": "Features", + "url": "#!" }, { - text: "Link 2 Text", - url: "link-2-url" + "text": "Resources", + "url": "#!" }, { - text: "Link 3 Text", - url: "link-3-url" + "text": "Industry Apps", + "url": "#!" + }, + { + "text": "Customer Success", + "url": "#!" } ] }, diff --git a/now.json b/now.json deleted file mode 100644 index 1e481ee515..0000000000 --- a/now.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": 1, - "static": { - "trailingSlash": true, - "cleanUrls": false, - "directoryListing": false, - "rewrites": [ - { - "source": "/", - "destination": "/index.html" - }, - { - "source": "/pattern-lab", - "destination": "/pattern-lab/index.html" - }, - { - "source": "/pattern-lab/splash-screen/", - "destination": "/pattern-lab/splash-screen.html" - }, - { - "source": "/docs", - "destination": "/docs/getting-started/index.html" - }, - { - "source": "/docs/", - "destination": "/docs/getting-started/index.html" - }, - { - "source": "/docs/index.html", - "destination": "/docs/getting-started/index.html" - }, - { - "source": "/favicon.ico", - "destination": "/pattern-lab/favicons/favicon.ico" - } - ] - } -} diff --git a/package.json b/package.json index 14ec550ab0..544a3fb3fd 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "setup:full": "yarn --force", "deploy": "./scripts/deploy.js", + "deploy:docker": "sh ./scripts/docker.sh", "clean": "npx npm-run-all --parallel clean:*", "clean:empty": "find . -empty -type d -delete", @@ -43,11 +44,14 @@ "test": "npm-run-all --parallel test:*", "test:js": "NODE_ENV='test' jest", "test:php": "npx lerna exec --scope @bolt/core-php -- composer run test", + + "serve": "node server.js", "postbootstrap": "node scripts/monorepo-tests.js", "prepublishOnly": "node scripts/monorepo-tests.js" }, "dependencies": { + "ci-utils": "^0.5.0", "sassdoc": "^2.5.0", "npm-run-all": "^4.1.5", "jest": "^23.6.0", @@ -60,18 +64,20 @@ }, "devDependencies": { "editorconfig-checker": "^1.3.3", + "express": "^4.16.3", "git-semver-tags": "^2.0.0", "husky": "^1.3.1", "lerna": "^3.8.0", "nightwatch": "^1.0.8", "node-fetch": "^2.2.0", - "now": "^12.1.0", + "now": "latest", "url-exists": "^1.0.3", "yaml-lint": "^1.2.4" }, "workspaces": { "packages": [ "docs-site", + "docs-site/src/components/*", "packages/uikit-workshop", "packages/build-tools", "packages/build-tools/plugins/*", diff --git a/packages/build-tools/api/__snapshots__/index.test.js.snap b/packages/build-tools/api/__snapshots__/index.test.js.snap index 927cf373f2..552045bf3e 100644 --- a/packages/build-tools/api/__snapshots__/index.test.js.snap +++ b/packages/build-tools/api/__snapshots__/index.test.js.snap @@ -1,67 +1,71 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Test the Bolt TwigRender API renders attributes on the button component correctly 1`] = ` -" - - -" -`; - -exports[`Test the Bolt TwigRender API renders the button component correctly 1`] = ` -" - - -" `; -exports[`Test the Bolt TwigRender API renders the secondary button correctly 1`] = ` -" - - + - +`; +exports[`Test the Bolt Twig Renderer API renders the secondary button correctly 1`] = ` + + + -" + + `; diff --git a/packages/build-tools/api/index.js b/packages/build-tools/api/index.js index d1daedc3b9..174a7a707e 100644 --- a/packages/build-tools/api/index.js +++ b/packages/build-tools/api/index.js @@ -1,5 +1,5 @@ const url = require('url'); -const fetch = require('node-fetch'); +const { render } = require('@bolt/twig-renderer'); const { getConfig } = require('../utils/config-store'); const log = require('../utils/log'); @@ -31,40 +31,33 @@ async function handleRequest(req, res, next) { const config = await getConfig(); const { method } = req; const { pathname, query, search } = url.parse(req.url, true); - // let body; - // if (method === 'POST') { - const body = await getBody(req); - // } // @todo test with `GET` requests // @todo test with empty body + // console.log(`api request received at ${pathname}`); // remove once we're sure this works on server switch (pathname) { - case '/render-twig': + case '/': + res.json({ + ok: true, + message: `Welcome to the Bolt Design System API! Have a nice day!`, + }); + break; + case '/render': try { - /** @var renderResponse {Response} */ - const renderResponse = await fetch( - `http://127.0.0.1:${config.renderingServicePort}${search}`, - { - method, - body: method === 'POST' ? JSON.stringify(body) : null, - }, - ); - const data = await renderResponse.text(); - // console.log(renderResponse); + if (!query.template) { + log.error('The template paramater is missing!'); + } + const body = await getBody(req); + const { ok, html, message } = await render(query.template, body); - const { status } = renderResponse; - const warning = renderResponse.headers.get('Warning'); - res.setHeader( - 'Content-Type', - renderResponse.headers.get('Content-Type'), - ); - res.statusCode = status; - if (warning) { - res.statusMessage = warning; - res.setHeader('Warning', warning); + if (!ok) { + log.error(message); } - res.end(data); + res.end(html); } catch (error) { - log.errorAndExit('Error connecting to phpServer api endpoint', error); + log.errorAndExit( + 'Error rendering Twig using the Twig rendering service...', + error, + ); } break; default: @@ -72,7 +65,7 @@ async function handleRequest(req, res, next) { res.end( JSON.stringify({ ok: false, - message: `Not api route found at: ${url}`, + message: `No api route found at: ${pathname}`, }), 'utf8', () => diff --git a/packages/build-tools/api/index.test.js b/packages/build-tools/api/index.test.js index c02a409a22..c235bf243e 100644 --- a/packages/build-tools/api/index.test.js +++ b/packages/build-tools/api/index.test.js @@ -1,24 +1,24 @@ -// const { twigRenderer } = require('./twig-renderer'); +const { render } = require('@bolt/twig-renderer'); const buttonTemplate = '@bolt-components-button/button.twig'; -describe.skip('Test the Bolt TwigRender API', () => { +describe('Test the Bolt Twig Renderer API', () => { test('renders a basic button', async () => { const buttonData = { text: 'Hello world!', }; - const result = await twigRenderer(buttonTemplate, buttonData); + const result = await render(buttonTemplate, buttonData); expect(result.ok).toEqual(true); }); test('handles missing data', async () => { - const result = await twigRenderer(buttonTemplate); + const result = await render(buttonTemplate); expect(result.ok).toEqual(true); }); test('handles non-existent template path', async () => { - const result = await twigRenderer('@bolt-components-button/button2.twig', { + const result = await render('@bolt-components-button/button2.twig', { text: 'Hello world 2!', }); @@ -26,7 +26,7 @@ describe.skip('Test the Bolt TwigRender API', () => { }); test('renders the button component correctly', async () => { - const result = await twigRenderer(buttonTemplate, { + const result = await render(buttonTemplate, { text: 'Hello world!2', }); @@ -34,7 +34,7 @@ describe.skip('Test the Bolt TwigRender API', () => { }); test('renders attributes on the button component correctly', async () => { - const result = await twigRenderer(buttonTemplate, { + const result = await render(buttonTemplate, { text: 'Hello world!', attributes: { class: ['u-bolt-margin-bottom-large'], @@ -45,7 +45,7 @@ describe.skip('Test the Bolt TwigRender API', () => { }); test('renders the secondary button correctly', async () => { - const result = await twigRenderer(buttonTemplate, { + const result = await render(buttonTemplate, { text: 'Secondary Button!', style: 'secondary', }); diff --git a/packages/build-tools/api/twig-renderer.js b/packages/build-tools/api/twig-renderer.js deleted file mode 100644 index 09308531b6..0000000000 --- a/packages/build-tools/api/twig-renderer.js +++ /dev/null @@ -1,30 +0,0 @@ -const qs = require('querystring'); -const fetch = require('node-fetch'); -const config = require('@bolt/build-tools/utils/config-store').getConfig(); -// const { getPort } = require('../utils/get-port'); - -async function twigRenderer(templatePath, body) { - const options = { - method: body ? 'POST' : 'GET', - }; - if (body) options.body = JSON.stringify(body); - const response = await fetch( - `http://127.0.0.1:${config.renderingServicePort}?${qs.stringify({ - templatePath, - })}`, - options, - ); - const html = await response.text(); - - const { status, statusText, ok } = response; - return { - status, - statusText, - ok, - html, - }; -} - -module.exports = { - twigRenderer, -}; diff --git a/packages/build-tools/create-webpack-config.js b/packages/build-tools/create-webpack-config.js index 3f11438a52..0a271541f6 100644 --- a/packages/build-tools/create-webpack-config.js +++ b/packages/build-tools/create-webpack-config.js @@ -474,6 +474,9 @@ async function createWebpackConfig(buildConfig) { hot: config.prod ? false : true, noInfo: true, // webpackTasks.watch handles output info related to success & failure publicPath, + headers: { + 'Access-Control-Allow-Origin': '*', + }, }; } diff --git a/packages/build-tools/tasks/task-collections.js b/packages/build-tools/tasks/task-collections.js index dd275a6bec..b023decad4 100644 --- a/packages/build-tools/tasks/task-collections.js +++ b/packages/build-tools/tasks/task-collections.js @@ -111,9 +111,9 @@ async function serve(buildTime = timer.start()) { try { const serverTasks = []; - if (config.renderingService) { - serverTasks.push(extraTasks.server.phpServer()); - } + // if (config.renderingService) { + // serverTasks.push(extraTasks.server.phpServer()); + // } if (config.wwwDir) { if (config.webpackDevServer && config.watch !== false) { serverTasks.push(webpackTasks.server()); diff --git a/packages/build-tools/tasks/webpack-tasks.js b/packages/build-tools/tasks/webpack-tasks.js index ff98fed9d7..30fb8107a1 100644 --- a/packages/build-tools/tasks/webpack-tasks.js +++ b/packages/build-tools/tasks/webpack-tasks.js @@ -5,6 +5,7 @@ const webpackHotMiddleware = require('webpack-hot-middleware'); const createWebpackConfig = require('../create-webpack-config'); const { getConfig } = require('../utils/config-store'); const { boltWebpackMessages } = require('../utils/webpack-helpers'); +const { handleRequest } = require('../api'); let boltBuildConfig; @@ -55,13 +56,13 @@ async function server(customWebpackConfig) { const compiler = boltWebpackMessages(webpack(webpackConfig)); // Add Hot Module Reloading scripts to Webpack entrypoint - if (webpackConfig[0].devServer.hot) { - webpackConfig[0].entry['bolt-global'].unshift( - `webpack-hot-middleware/client?noInfo=true&http://localhost:${ - webpackConfig[0].devServer.port - }/`, - ); - } + // if (webpackConfig[0].devServer.hot) { + // webpackConfig[0].entry['bolt-global'].unshift( + // `webpack-hot-middleware/client?noInfo=true&http://localhost:${ + // webpackConfig[0].devServer.port + // }/`, + // ); + // } /** * Run Browsersync and use middleware for Hot Module Replacement @@ -76,6 +77,12 @@ async function server(customWebpackConfig) { watchOptions: { ignoreInitial: true, }, + middleware: [ + { + route: '/api', + handle: handleRequest, + }, + ], server: { baseDir: boltBuildConfig.wwwDir, middleware: [ diff --git a/packages/build-tools/utils/config-store.js b/packages/build-tools/utils/config-store.js index b73b9f2ef9..439f7ce073 100644 --- a/packages/build-tools/utils/config-store.js +++ b/packages/build-tools/utils/config-store.js @@ -28,12 +28,10 @@ async function getDefaultConfig() { return Promise.all([ await getPort(configSchema.properties.port.default), await getPort(configSchema.properties.proxyPort.default), - await getPort(configSchema.properties.renderingServicePort.default), ]).then(function(ports) { return { port: ports[0], proxyPort: ports[1], - renderingServicePort: ports[2], ip, env: process.env.NODE_ENV, enableCache: configSchema.properties.enableCache.default, diff --git a/packages/components/bolt-button/button.schema.yml b/packages/components/bolt-button/button.schema.yml index 11073c090b..166be6574e 100644 --- a/packages/components/bolt-button/button.schema.yml +++ b/packages/components/bolt-button/button.schema.yml @@ -1,19 +1,10 @@ -$schema: 'http://json-schema.org/draft-04/schema#' title: 'Bolt Button' description: 'Buttons are the core of our action components.' type: object -required: - - text -not: - anyOf: - - required: - - itemAlignment - - required: - - rounded properties: - attributes: - type: object - description: A Drupal-style attributes object with extra attributes to append to this component. + # attributes: + # type: any + # description: A Drupal-style attributes object with extra attributes to append to this component. text: title: 'Button Text' description: 'The text displayed inside a button' @@ -30,6 +21,7 @@ properties: icon: type: object description: Icon data as expected by the icon component. Accepts an additional 'position' parameter that determines placement within the button. + ref: '@bolt-components-icon/icon.schema.yml' properties: position: type: string @@ -105,3 +97,11 @@ properties: default: false required: - icon +required: + - text +not: + anyOf: + - required: + - itemAlignment + - required: + - rounded \ No newline at end of file diff --git a/packages/components/bolt-navbar/navbar.schema.yml b/packages/components/bolt-navbar/navbar.schema.yml index f187685052..9e8b8b7fb5 100644 --- a/packages/components/bolt-navbar/navbar.schema.yml +++ b/packages/components/bolt-navbar/navbar.schema.yml @@ -18,6 +18,7 @@ properties: properties: tag: type: string + default: h2 enum: - h1 - h2 diff --git a/packages/core/actions.js b/packages/core/actions.js new file mode 100644 index 0000000000..cf492fdc91 --- /dev/null +++ b/packages/core/actions.js @@ -0,0 +1,23 @@ +export const UPDATE_EXPLORER_COMPONENT = 'UPDATE_EXPLORER_COMPONENT'; +export const UPDATE_EXPLORER_FORM = 'UPDATE_EXPLORER_FORM'; + +export const updateComponentExplorerPreview = renderedHTML => ( + dispatch, + getState, +) => { + if (getState().componentExplorer.renderedHTML !== renderedHTML) { + dispatch({ + type: UPDATE_EXPLORER_COMPONENT, + renderedHTML, + }); + } +}; + +export const updateComponentExplorerForm = formData => (dispatch, getState) => { + if (getState().componentExplorer.formData !== formData) { + dispatch({ + type: UPDATE_EXPLORER_FORM, + formData, + }); + } +}; diff --git a/packages/core/localstorage.js b/packages/core/localstorage.js new file mode 100644 index 0000000000..c8b92757a7 --- /dev/null +++ b/packages/core/localstorage.js @@ -0,0 +1,52 @@ +export const saveState = state => { + const json = localStorage.getItem('bolt') || '{}'; + const stringifiedNewState = JSON.stringify(state); + + if (stringifiedNewState !== json && stringifiedNewState !== '{}') { + localStorage.setItem('bolt', stringifiedNewState); + } +}; + +export const loadState = () => { + let json; + + // Temporarily don't load the cached state in debug mode. + if (window.location.hash === '#debug') { + json = '{}'; + // Alternatively, clear the localStorage redux state with a #reset hash + } else if (window.location.hash === '#reset') { + localStorage.removeItem('bolt'); + json = {}; + } else { + json = localStorage.getItem('bolt') || '{}'; + } + + const state = JSON.parse(json); + + if (state) { + // // Add default state data here (if necessary) + // if (state.app) { + // if (state.app.drawerHeight && !state.app.drawerOpened) { + // state.app.appHeight = window.innerHeight; + // } else if (state.app.drawerHeight && state.app.drawerOpened) { + // state.app.appHeight = window.innerHeight - state.app.drawerHeight; + // } + // } + + // if (state.app) { + // if (state.app.themeMode === undefined) { + // try { + // if (window.patternlab.config.theme.color !== undefined) { + // state.app.themeMode = window.patternlab.config.theme.color; + // } + // } catch (e) { + // state.app.themeMode = 'dark'; + // } + // } + // } + + return state; + } else { + return undefined; + } +}; diff --git a/packages/core/package.json b/packages/core/package.json index da890c0f78..bf9f0ba4a6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -7,6 +7,10 @@ "style": "styles/index.scss", "repository": "https://github.com/bolt-design-system/bolt/tree/master/packages/core", "dependencies": { + "lodash.merge": "^4.6.1", + "redux": "^4.0.1", + "redux-thunk": "^2.3.0", + "pwa-helpers": "^0.9.0", "@polymer/lit-element": "0.6.2", "@polymer/polymer": "^3.0.5", "@webcomponents/custom-elements": "^1.2.1", @@ -25,7 +29,7 @@ "html-element": "^2.2.0", "lit-html": "^0.12.0", "mdn-polyfills": "^5.12.0", - "preact": "8.3.1", + "preact": "8.4.2", "preact-compat": "^3.18.4", "raf": "^3.4.0", "sass-mq": "github:bolt-design-system/sass-mq#master", diff --git a/packages/core/reducers.js b/packages/core/reducers.js new file mode 100644 index 0000000000..ab0675341a --- /dev/null +++ b/packages/core/reducers.js @@ -0,0 +1,30 @@ +import { UPDATE_EXPLORER_FORM, UPDATE_EXPLORER_COMPONENT } from './actions.js'; + +export const app = (state = {}, action) => { + switch (action.type) { + // case UPDATE_COMPONENT_EXPLORER: + // return { + // ...state, + // renderedHTML: action.renderedHTML, + // }; + default: + return state; + } +}; + +export const componentExplorer = (state = {}, action) => { + switch (action.type) { + case UPDATE_EXPLORER_COMPONENT: + return { + ...state, + renderedHTML: action.renderedHTML, + }; + case UPDATE_EXPLORER_FORM: + return { + ...state, + formData: action.formData, + }; + default: + return state; + } +}; diff --git a/packages/core/renderers/bolt-base.js b/packages/core/renderers/bolt-base.js index 8f288d9af9..1ccac3d320 100644 --- a/packages/core/renderers/bolt-base.js +++ b/packages/core/renderers/bolt-base.js @@ -1,7 +1,15 @@ import Ajv from 'ajv'; +import isEqual from 'lodash.isequal'; import { withComponent, shadow, props } from 'skatejs'; -import { hasNativeShadowDomSupport } from '../utils/environment'; -import { findParentTag } from '../utils/find-parent-tag'; +import merge from 'lodash.merge'; +import { + deepEquals, + extend, + findParentTag, + hasNativeShadowDomSupport, +} from '../utils'; + +import { store } from '../store.js'; export function BoltBase(Base = HTMLElement) { return class extends Base { @@ -11,6 +19,66 @@ export function BoltBase(Base = HTMLElement) { return self; } + async shouldReRender(props, state, prevProps, prevState) { + return !deepEquals(props, prevProps) || !deepEquals(state, prevState); + } + + async shouldUpdate(prevProps, prevState) { + return this.shouldReRender(this, prevProps, prevState); + } + + connectedCallback() { + super.connectedCallback && super.connectedCallback; + this._storeUnsubscribe = store.subscribe(() => + this.stateChanged(store.getState()), + ); + this.stateChanged(store.getState()); + } + + stateChanged(state) {} + + disconnectedCallback() { + this._storeUnsubscribe(); + super.disconnectedCallback && super.disconnectedCallback(); + } + + /** + * Update component state and schedule a re-render. + * @param {object} state A dict of state properties to be shallowly merged + * into the current state, or a function that will produce such a dict. The + * function is called with the current state and props. + * @param {() => void} callback A function to be called once component state is + * updated + */ + async setState(state, callback) { + // @todo: review to see which merge we should be using here. + // this.state = merge(this.state || {}, state); + this.state = extend( + extend({}, this.state), + typeof state === 'function' ? state(this.state, this.props) : state, + ); + + if (!this._prevState) { + this._prevState = this.state; + } + + if ( + await this.shouldReRender( + this.props, + this.state, + this._prevProps, + this._prevState, + ) + ) { + await this.triggerUpdate(); + } + + if (callback) { + // await this._renderCallbacks.push(callback); // @todo: review to see if / how we can implement the normal setState callback + callback(); + } + } + setupSlots() { // Automatically adjust which inner element inside the custom element gets used as the base when evaluating slotted children. Necessary when including deeply nested slots in the initial HTML being rendered, which might include a few wrapping containers that get removed when the JavaScript kicks in. <-- this is how we get slotted buttons to work! const isShadowRootSelector = this.querySelector('[is="shadow-root"]'); @@ -107,11 +175,6 @@ export function BoltBase(Base = HTMLElement) { return slots; } - disconnectedCallback() { - this.disconnecting && this.disconnecting(); - this.disconnected && this.disconnected(); - } - rendered() { if (!this._wasInitiallyRendered) { this._wasInitiallyRendered = true; diff --git a/packages/core/store.js b/packages/core/store.js new file mode 100644 index 0000000000..47d6c0b544 --- /dev/null +++ b/packages/core/store.js @@ -0,0 +1,31 @@ +import { + createStore, + applyMiddleware, + compose as origCompose, + combineReducers, +} from 'redux'; +import thunk from 'redux-thunk'; +import { lazyReducerEnhancer } from 'pwa-helpers/lazy-reducer-enhancer.js'; +import { app, componentExplorer } from './reducers'; +import { loadState, saveState } from './localstorage.js'; + +const compose = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || origCompose; + +export const store = createStore( + (state, action) => state, + loadState(), // If there is local storage data, load it. + compose( + lazyReducerEnhancer(combineReducers), + applyMiddleware(thunk), + ), +); + +store.addReducers({ + app, + componentExplorer, +}); + +// This subscriber writes to local storage anytime the state updates. +store.subscribe(() => { + saveState(store.getState()); +}); diff --git a/packages/core/utils/deep-equals.js b/packages/core/utils/deep-equals.js new file mode 100644 index 0000000000..d4b68a8a04 --- /dev/null +++ b/packages/core/utils/deep-equals.js @@ -0,0 +1,80 @@ +function isArguments(object) { + return Object.prototype.toString.call(object) === '[object Arguments]'; +} + +export function deepEquals(a, b, ca = [], cb = []) { + // Partially extracted from node-deeper and adapted to exclude comparison + // checks for functions. + // https://github.com/othiym23/node-deeper + if (a === b) { + return true; + } else if (typeof a === 'function' || typeof b === 'function') { + // Assume all functions are equivalent + // see https://github.com/mozilla-services/react-jsonschema-form/issues/255 + return true; + } else if (typeof a !== 'object' || typeof b !== 'object') { + return false; + } else if (a === null || b === null) { + return false; + } else if (a instanceof Date && b instanceof Date) { + return a.getTime() === b.getTime(); + } else if (a instanceof RegExp && b instanceof RegExp) { + return ( + a.source === b.source && + a.global === b.global && + a.multiline === b.multiline && + a.lastIndex === b.lastIndex && + a.ignoreCase === b.ignoreCase + ); + } else if (isArguments(a) || isArguments(b)) { + if (!(isArguments(a) && isArguments(b))) { + return false; + } + let slice = Array.prototype.slice; + return deepEquals(slice.call(a), slice.call(b), ca, cb); + } else { + if (a.constructor !== b.constructor) { + return false; + } + + let ka = Object.keys(a); + let kb = Object.keys(b); + // don't bother with stack acrobatics if there's nothing there + if (ka.length === 0 && kb.length === 0) { + return true; + } + if (ka.length !== kb.length) { + return false; + } + + let cal = ca.length; + while (cal--) { + if (ca[cal] === a) { + return cb[cal] === b; + } + } + ca.push(a); + cb.push(b); + + ka.sort(); + kb.sort(); + for (var j = ka.length - 1; j >= 0; j--) { + if (ka[j] !== kb[j]) { + return false; + } + } + + let key; + for (let k = ka.length - 1; k >= 0; k--) { + key = ka[k]; + if (!deepEquals(a[key], b[key], ca, cb)) { + return false; + } + } + + ca.pop(); + cb.pop(); + + return true; + } +} diff --git a/packages/core/utils/extend.js b/packages/core/utils/extend.js new file mode 100644 index 0000000000..0c657e37d4 --- /dev/null +++ b/packages/core/utils/extend.js @@ -0,0 +1,6 @@ +export function extend(obj, props) { + for (const i in props) { + obj[i] = props[i]; + } + return obj; +} diff --git a/packages/core/utils/index.js b/packages/core/utils/index.js index 7200aae641..99a32ee6e1 100644 --- a/packages/core/utils/index.js +++ b/packages/core/utils/index.js @@ -15,6 +15,8 @@ export * from './wait-for-transition-end'; export * from './watch-for-component-mutations'; export * from './context/define-context'; export * from './context/with-context'; +export * from './extend'; +export { deepEquals } from './deep-equals'; // https://www.polymer-project.org/3.0/docs/devguide/custom-elements#defer-work export { diff --git a/packages/twig-renderer/twig-renderer.js b/packages/twig-renderer/twig-renderer.js index ba5be9aa2d..49e0a7cfdb 100644 --- a/packages/twig-renderer/twig-renderer.js +++ b/packages/twig-renderer/twig-renderer.js @@ -32,7 +32,7 @@ async function init() { debug: true, alterTwigEnv: config.alterTwigEnv, hasExtraInfoInResponses: false, // Will add `info` onto results with a lot of info about Twig Env - maxConcurrency: 1, + maxConcurrency: 10, keepAlive: false, // setting this to true will cause subsequent template / page recompiles to not regenerate when the source files have changed }); state = STATES.READY; diff --git a/scripts/deploy.js b/scripts/deploy.js index 37caec3a78..c340dd4708 100755 --- a/scripts/deploy.js +++ b/scripts/deploy.js @@ -1,6 +1,15 @@ #!/usr/bin/env node const url = require('url'); +const { resolve } = require('path'); const querystring = require('querystring'); +const { + setGitHubStatus, + createGitHubComment, + outputBanner, + runAndShow, + runAndReturn, + getGitSha, +} = require('ci-utils'); const fetch = require('node-fetch'); const { spawnSync } = require('child_process'); const { promisify } = require('util'); @@ -30,8 +39,13 @@ async function init() { TRAVIS_REPO_SLUG, // If the current build is for a git tag, this variable is set to the tag’s name TRAVIS_TAG, + TRAVIS_BUILD_WEB_URL, } = process.env; + // also made in `.travis.yml` during docker tag + const gitSha = getGitSha(true); + const gitShaLong = getGitSha(); + console.log({ TRAVIS, TRAVIS_BRANCH, @@ -39,6 +53,8 @@ async function init() { TRAVIS_PULL_REQUEST, TRAVIS_REPO_SLUG, TRAVIS_TAG, + TRAVIS_BUILD_WEB_URL, + gitSha, }); let branchName = 'detached-HEAD'; @@ -66,25 +82,47 @@ async function init() { const baseNowArgs = [ '--platform-version=1', '--team=boltdesignsystem', - '--local-config=../now.json', ]; if (NOW_TOKEN) baseNowArgs.push(`--token=${NOW_TOKEN}`); - console.log('Starting deploy...'); - const deployOutput = spawnSync( - 'now', - [ - 'deploy', - './www', - '--name=boltdesignsystem', - '--static', - ...baseNowArgs, - ], - { encoding: 'utf8' }, - ); + await setGitHubStatus({ + state: 'pending', + context: 'deploy/now.sh', + }); + + outputBanner('Starting deploy...'); + const deployOutput = spawnSync('now', [ + 'deploy', + '--force', + '--meta', + `TRAVIS_BUILD_WEB_URL="${TRAVIS_BUILD_WEB_URL}"`, + '--env', + `DOCKER_TAG=${gitSha}`, + '--build-env', + `DOCKER_TAG=${gitSha}`, + ...baseNowArgs, + ], { + encoding: 'utf8', + cwd: resolve(__dirname, '../deploys'), + }); + + // const deployOutput = spawnSync( + // 'now', + // [ + // 'deploy', + // './www', + // '--name=boltdesignsystem', + // '--static', + // ...baseNowArgs, + // ], + // { encoding: 'utf8' }, + // ); + if (deployOutput.status !== 0) { console.error('Error deploying:'); + console.log(deployOutput.stdout, deployOutput.stderr); + process.exit(1); } console.log(deployOutput.stdout, deployOutput.stderr); const deployedUrl = deployOutput.stdout.trim(); @@ -111,10 +149,23 @@ async function init() { if (aliasOutput.status !== 0) { console.error('Error aliasing:'); console.log(aliasOutput.stdout, aliasOutput.stderr); + + await setGitHubStatus({ + state: 'error', + context: 'deploy/now.sh', + description: `${aliasOutput.stdout} - ${aliasOutput.stderr}`, + }); process.exit(1); } console.log(aliasOutput.stdout, aliasOutput.stderr); + await setGitHubStatus({ + state: 'success', + context: 'deploy/now.sh', + url: deployedUrl, + description: `Alias set to ${aliasedUrl}`, + }); + // if this is a tagged release, then it should become the main site. we aliased above so we have a tagged version out as well i.e. `v1-2-3-boltdesignsystem.com` if (TRAVIS_TAG && TRAVIS_TAG === latestTag) { console.log('Is tag build, aliasing to main site.'); @@ -176,24 +227,11 @@ async function init() { `.trim(); // end GitHub comment template + const results = await createGitHubComment(githubCommentText, TRAVIS_PULL_REQUEST); + console.log(`GitHub comment made: ${results.html_url}`); - const githubCommentEndpoint = `https://api.github.com/repos/${TRAVIS_REPO_SLUG}/issues/${TRAVIS_PULL_REQUEST}/comments`; - - const response = await fetch(githubCommentEndpoint, { - method: 'POST', - body: JSON.stringify({ - body: githubCommentText, - }), - headers: { - Authorization: `Bearer ${GITHUB_TOKEN}`, - }, - }).then(res => res.json()); - console.log(response); - console.log('GitHub comment posted'); } else { - console.log( - 'This is not a Pull Request build, so will not try to comment on PR.', - ); + console.log('This is not a Pull Request build, so will not try to comment on PR.'); } // @todo Errors should be passed to `catch` } catch (error) { diff --git a/scripts/docker-build.sh b/scripts/docker-build.sh new file mode 100755 index 0000000000..b3936136a4 --- /dev/null +++ b/scripts/docker-build.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Start in this directory even if ran elsewhere +cd "$(dirname "$0")" +# Up to root of repo +cd .. +# Show all commands ran +set -x +# Exit this script if anything else exits +set -e + +# Error messages are redirected to stderr +function handle_error { + echo "$(basename $0): ERROR! An error was encountered executing line $1." 1>&2; + # cleanup + echo 'Exiting with error.' 1>&2; + exit 1 +} + +function handle_exit { + # cleanup + echo 'Exiting without error.' 1>&2; + exit +} + +# Exit the script with a helpful error message when any error is encountered +trap 'set +x; handle_error $LINENO $BASH_COMMAND' ERR + +# --oom-kill-disable -memory=2000m +time docker build --tag boltdesignsystem/bolt:latest . diff --git a/scripts/docker-run.sh b/scripts/docker-run.sh new file mode 100755 index 0000000000..dc645b6890 --- /dev/null +++ b/scripts/docker-run.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Start in this directory even if ran elsewhere +cd "$(dirname "$0")" +# Up to root of repo +cd .. + +# Show all commands ran +set -x +# Exit this script if anything else exits +set -e + +time docker run -p 3123:3123 --tty --rm boltdesignsystem/bolt:latest diff --git a/scripts/docker.sh b/scripts/docker.sh new file mode 100644 index 0000000000..97585be10e --- /dev/null +++ b/scripts/docker.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +docker --log-level error pull boltdesignsystem/bolt || true + +docker build --cache-from boltdesignsystem/bolt --tag boltdesignsystem/bolt . + +docker images + +echo $DOCKER_HUB_PASS | docker login --username $DOCKER_HUB_USER --password-stdin + +GIT_SHA="$(git rev-parse --short HEAD)" + +docker tag boltdesignsystem/bolt boltdesignsystem/bolt:latest + +docker tag boltdesignsystem/bolt boltdesignsystem/bolt:${GIT_SHA} + +docker --log-level error push boltdesignsystem/bolt:latest + +docker --log-level error push boltdesignsystem/bolt:${GIT_SHA} diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000000..0291e3acf2 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,19 @@ +{ + "name": "@bolt/scripts", + "version": "2.2.0", + "description": "Deps for CI scripts", + "main": "deploy.js", + "scripts": { + "deploy": "./deploy.js", + "nightwatch": "nightwatch --config ../nightwatch.js --env chrome,ie11" + }, + "dependencies": { + "ci-utils": "^0.5.0", + "git-semver-tags": "^2.0.2", + "nightwatch": "0.9.20", + "node-fetch": "^2.3.0", + "now": "latest" + }, + "author": "", + "license": "ISC" +} diff --git a/serve.json b/serve.json index da8303074e..a9ddc47646 100644 --- a/serve.json +++ b/serve.json @@ -42,6 +42,19 @@ "value": "application/schema+json" } ] + }, + { + "source": "**/*", + "headers": [ + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "Access-Control-Allow-Headers", + "value": "Origin, X-Requested-With, Content-Type, Accept, Range" + } + ] } ] } diff --git a/server.js b/server.js new file mode 100644 index 0000000000..eff96a7eea --- /dev/null +++ b/server.js @@ -0,0 +1,28 @@ +const express = require('express'); +const { join } = require('path'); +const { handleRequest } = require('@bolt/build-tools/api'); +const app = express(); + +const port = process.env.PORT || 3123; + +app.get(['/docs', '/docs/', '/docs/index.html'], (req, res) => { + res.redirect('/docs/getting-started/index.html'); +}); + +app.get('/pattern-lab/splash-screen', (req, res) => { + res.redirect('/pattern-lab'); +}); + +app.get('/favicon.ico', (req, res) => { + res.redirect('/pattern-lab/favicons/favicon.ico'); +}); + +app.use('/api', handleRequest); + +app.use(express.static(join(__dirname, './www'))); + +app.listen(port, () => { + console.log(`Express listening on http://localhost:${port}`); +}); + +app.redirect; diff --git a/yarn.lock b/yarn.lock index 55b6da7d99..f9b262ef66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1744,6 +1744,16 @@ ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" +ajv@^5.2.3: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + ajv@^6.0.1, ajv@^6.1.0, ajv@^6.5.2, ajv@^6.5.3, ajv@^6.5.4, ajv@^6.5.5: version "6.5.5" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.5.tgz#cf97cdade71c6399a92c6d6c4177381291b781a1" @@ -3148,6 +3158,13 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-utils@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/ci-utils/-/ci-utils-0.5.0.tgz#dcb768d26407e609f38673b899f62407d9b96f34" + integrity sha512-kZfXNlbeJ9JnvIDsJKLKhKZpyMdlEFF8O8UZA6aaE67EvSp489fDDM4lWwPYINREOPhgwwl5fZ9XI7WaHAyjvA== + dependencies: + commander "^2.19.0" + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -3174,7 +3191,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.2.5, classnames@^2.2.6: +classnames@2.x, classnames@^2.2.5, classnames@^2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" @@ -3670,6 +3687,11 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7: version "2.5.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" +core-js@^2.4.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.1.tgz#87416ae817de957a3f249b3b5ca475d4aaed6042" + integrity sha512-L72mmmEayPJBejKIWe2pYtGis5r0tQ5NaJekdhyXgeMQTpJoBsH0NL4ElY2LfSoV15xeQWKQ+XTTOZdyero5Xg== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -3738,6 +3760,15 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-react-class@^15.6.0: + version "15.6.3" + resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" + integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== + dependencies: + fbjs "^0.8.9" + loose-envify "^1.3.1" + object-assign "^4.1.1" + cross-spawn@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" @@ -3928,7 +3959,7 @@ css-what@2.1, css-what@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" -css@^2.0.0: +css@2.2.4, css@^2.0.0: version "2.2.4" resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" dependencies: @@ -4419,6 +4450,13 @@ domhandler@2.1: dependencies: domelementtype "1" +domhandler@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" + integrity sha1-LeWaCCLVAn+r/28DLCsloqir5zg= + dependencies: + domelementtype "1" + domhandler@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" @@ -5059,7 +5097,7 @@ export-files@^2.0.1: dependencies: lazy-cache "^1.0.3" -express@^4.16.4: +express@^4.16.3, express@^4.16.4: version "4.16.4" resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== @@ -5151,6 +5189,11 @@ extsprintf@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" +fast-deep-equal@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= + fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" @@ -5188,7 +5231,26 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -fbjs@^0.8.1: +fbjs-css-vars@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== + +fbjs@*: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a" + integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA== + dependencies: + core-js "^2.4.1" + fbjs-css-vars "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + +fbjs@^0.8.1, fbjs@^0.8.9: version "0.8.17" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= @@ -6284,6 +6346,14 @@ html-comment-regex@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" +html-dom-parser@0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-0.1.3.tgz#fe22aa84206a46484069138849f29b154a5ee884" + integrity sha512-kGhjJDkfiA2/3y0gc2Bi+rseJWJSKz4CioS4EM+vN80fw863f1hn3G+7EaP0/benxceky4a8TzEeW6+dDjUh7A== + dependencies: + domhandler "2.3.0" + htmlparser2 "3.9.1" + html-element@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/html-element/-/html-element-2.2.0.tgz#c3c1ff88c261db74d0af6391eef90c346f900730" @@ -6323,6 +6393,15 @@ html-minifier@^3.3.1, html-minifier@^3.5.20, html-minifier@^3.5.8: relateurl "0.2.x" uglify-js "3.4.x" +html-react-parser@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-0.6.0.tgz#cc5baf75b454ea7e336362860b0584dd1fa8e256" + integrity sha512-pbGxk7WnEbITjmr/6QHVzYx7ZkITiVkM6r+Cm3aW4378zTfxW+NHc4uGGoW4U6T/PJr3xRkGKw41Kqy+i4unBw== + dependencies: + html-dom-parser "0.1.3" + react-dom-core "0.0.3" + style-to-object "0.2.2" + html-tags@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" @@ -6338,6 +6417,18 @@ html-tags@^2.0.0: tapable "^1.1.0" util.promisify "1.0.0" +htmlparser2@3.9.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.1.tgz#621b7a58bc9acd003f7af0a2c9a00aa67c8505d2" + integrity sha1-Yht6WLyazQA/evCiyaAKpnyFBdI= + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + htmlparser2@^3.9.0, htmlparser2@^3.9.1, htmlparser2@^3.9.2: version "3.10.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.0.tgz#5f5e422dcf6119c0d983ed36260ce9ded0bee464" @@ -7608,6 +7699,11 @@ json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-bet version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -8375,6 +8471,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "~3.0.0" +lodash.topath@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" + integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -9389,9 +9490,10 @@ normalize-url@^3.0.0, normalize-url@^3.1.0: version "3.3.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" -now@^12.1.0: - version "12.1.6" - resolved "https://registry.yarnpkg.com/now/-/now-12.1.6.tgz#70a2a46d22feac85eb4c763223f9498d02f9e2f6" +now@latest: + version "12.1.14" + resolved "https://registry.yarnpkg.com/now/-/now-12.1.14.tgz#3f56bce02df505eb668343baf2cecf57ca1cc10c" + integrity sha512-DsEvELqGbxwKMHI3Xpf+CoAeIsvj0bKOCoOXmaf8F6cAAh9N20DhTFl1wFGylpjPgd3lj8SGfQuiSbpUV5CTjQ== npm-bundled@^1.0.1: version "1.0.5" @@ -9523,6 +9625,10 @@ oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" +object-assign@*, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + object-assign@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" @@ -9531,10 +9637,6 @@ object-assign@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" -object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - object-component@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" @@ -10756,15 +10858,15 @@ preact-transition-group@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/preact-transition-group/-/preact-transition-group-1.1.1.tgz#f0a49327ea515ece34ea2be864c4a7d29e5d6e10" -preact@8.3.1, preact@^8.2.7: - version "8.3.1" - resolved "https://registry.yarnpkg.com/preact/-/preact-8.3.1.tgz#ed34f79d09edc5efd32a378a3416ef5dc531e3ac" - -preact@^8.3.1: +preact@8.4.2, preact@^8.3.1: version "8.4.2" resolved "https://registry.yarnpkg.com/preact/-/preact-8.4.2.tgz#1263b974a17d1ea80b66590e41ef786ced5d6a23" integrity sha512-TsINETWiisfB6RTk0wh3/mvxbGRvx+ljeBccZ4Z6MPFKgu/KFGyf2Bmw3Z/jlXhL5JlNKY6QAbA9PVyzIy9//A== +preact@^8.2.7: + version "8.3.1" + resolved "https://registry.yarnpkg.com/preact/-/preact-8.3.1.tgz#ed34f79d09edc5efd32a378a3416ef5dc531e3ac" + prebuild-install@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.2.2.tgz#237888f21bfda441d0ee5f5612484390bccd4046" @@ -10910,7 +11012,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2: +prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2: version "15.6.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: @@ -11028,6 +11130,11 @@ pwa-helpers@^0.8.3: resolved "https://registry.yarnpkg.com/pwa-helpers/-/pwa-helpers-0.8.4.tgz#c6fa3836ac05e7c38ec021668f4914037b877d99" integrity sha512-PluAQZo8a6GqcdIa0a61JabSHiKjmF1zuiGtAznDaUotD5dGA9OK4MbH++2cxig70vHg0sZemyeS4kzeVIK+0g== +pwa-helpers@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/pwa-helpers/-/pwa-helpers-0.9.0.tgz#88bc7c60842c7179ab643d337ab20bb252e5bf9d" + integrity sha512-lFj0udSHHqEJdf+SmAp8Y9yRJ8s1ieQIbvt4mWXn6NXUOO9ntLXNLBi8PMJGBPkc32cG4nJ6yuJrHzOxC5ZTXw== + q@1.*, q@^1.1.2, q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -11139,6 +11246,35 @@ react-autowhatever@^10.1.2: react-themeable "^1.1.0" section-iterator "^2.0.0" +react-dom-core@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/react-dom-core/-/react-dom-core-0.0.3.tgz#422d8efac874bed36731c3128281d33b346dd2af" + integrity sha512-pMMNOU4Hhe/LmkGWMVOpQmPnKQo+bGaWY3Y0+OjXmsQVB5LJ3sr4hVCM/7MpcRGeEMcfaRaoHyiLhufzk/BL4w== + dependencies: + react "15" + optionalDependencies: + fbjs "*" + object-assign "*" + +react-draggable@3.x, "react-draggable@^2.2.6 || ^3.0.3": + version "3.1.1" + resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-3.1.1.tgz#ed1db43e09b146805f6d158296e58eae4f768d36" + integrity sha512-tqIgDUm4XPSFbxelYpcsnayPU79P26ChnszDl5/RDFKfMuHnRxypS+OFfEyAEO1CtqaB3lrecQ2dyNIE2G0TlQ== + dependencies: + classnames "^2.2.5" + prop-types "^15.6.0" + +react-grid-layout@^0.16.6: + version "0.16.6" + resolved "https://registry.yarnpkg.com/react-grid-layout/-/react-grid-layout-0.16.6.tgz#9b2407a2b946c2260ebaf66f13b556e1da4efeb2" + integrity sha512-h2EsYgsqcESLJeevQSJsEKp8hhh+phOlXDJoMhlV2e7T3VWQL+S6iCF3iD/LK19r4oyRyOMDEir0KV+eLXrAyw== + dependencies: + classnames "2.x" + lodash.isequal "^4.0.0" + prop-types "15.x" + react-draggable "3.x" + react-resizable "1.x" + react-html-parser@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/react-html-parser/-/react-html-parser-2.0.2.tgz#6dbe1ddd2cebc1b34ca15215158021db5fc5685e" @@ -11146,6 +11282,25 @@ react-html-parser@^2.0.2: dependencies: htmlparser2 "^3.9.0" +react-jsonschema-form@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/react-jsonschema-form/-/react-jsonschema-form-1.0.6.tgz#ceef7c2c386e46a149ec94203547dd9111913e36" + integrity sha512-F6441MjApWHiFU/98T+fM19kBP9Ib0b3GMOB5DNyXnfMYC35CLwaANeZsTHug0HAmXGxgG+caPZSxgJSAyPz1Q== + dependencies: + ajv "^5.2.3" + babel-runtime "^6.26.0" + core-js "^2.5.7" + lodash.topath "^4.5.2" + prop-types "^15.5.8" + +react-resizable@1.x: + version "1.7.5" + resolved "https://registry.yarnpkg.com/react-resizable/-/react-resizable-1.7.5.tgz#83eb75bb3684da6989bbbf4f826e1470f0af902e" + integrity sha512-lauPcBsLqmxMHXHpTeOBpYenGalbSikYr8hK+lwtNYMQX1pGd2iYE+pDvZEV97nCnzuCtWM9htp7OpsBIY2Sjw== + dependencies: + prop-types "15.x" + react-draggable "^2.2.6 || ^3.0.3" + react-themeable@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/react-themeable/-/react-themeable-1.1.0.tgz#7d4466dd9b2b5fa75058727825e9f152ba379a0e" @@ -11153,6 +11308,17 @@ react-themeable@^1.1.0: dependencies: object-assign "^3.0.0" +react@15: + version "15.6.2" + resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72" + integrity sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI= + dependencies: + create-react-class "^15.6.0" + fbjs "^0.8.9" + loose-envify "^1.1.0" + object-assign "^4.1.0" + prop-types "^15.5.10" + read-cmd-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz#2d5d157786a37c055d22077c32c53f8329e91c7b" @@ -11363,7 +11529,7 @@ redux@^3.3.1: loose-envify "^1.1.0" symbol-observable "^1.0.3" -redux@^4.0.0: +redux@^4.0.0, redux@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5" integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg== @@ -12750,6 +12916,13 @@ style-search@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" +style-to-object@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.2.2.tgz#3ea3b276bd3fa9da1195fcdcdd03bc52aa2aae01" + integrity sha512-GcbtvfsqyKmIPpHeOHZ5Rmwsx2MDJct4W9apmTGcbPTbpA2FcgTFl2Z43Hm4Qb61MWGPNK8Chki7ITiY7lLOow== + dependencies: + css "2.2.4" + stylehacks@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-2.3.2.tgz#64c83e0438a68c9edf449e8c552a7d9ab6009b0b"