Skip to content

Commit

Permalink
πŸ“• Storybook for everyone (take 2) (elastic#43529)
Browse files Browse the repository at this point in the history
* chore: πŸ€– add Infra sample Storybook story

* chore: πŸ€– add @kbn/storybook package

* chore: πŸ€– add sample SIEM stories

* chore: πŸ€– add Canvas new Storybook config

* fix: πŸ› fix TypeScript errors

* chore: πŸ€– add @kbn/babel-preset to package.json

* chore: πŸ€– move dependencies to devDependencies

* chore: πŸ€– make Storybook dependencies non-dev

* chore: πŸ€– upgrade Storybook dependencies

* chore: πŸ€– add packages to webpack

* fix: πŸ› fix TypeScript type check

* chore: πŸ€– disable ESLint warnings

* chore: πŸ€– remove Storybook info plugin

* chore: πŸ€– upate yarn.lock

* chore: πŸ€– add Storbook to Embeddables

* feat: 🎸 add --clean flag to Storybook CLI

* coalesce yarn.lock versions

* update kbn/pm dist

* This reverts commit 97d8ff9 and 49b07cd.

* chore: πŸ€– use fs instead of mkdirp

* chore: πŸ€– use debug for message logging

* chore: πŸ€– update yarn.lock

* feat: 🎸 add link to kbn-storybook package

* docs: ✏️ add Storybook readme

* chore: πŸ€– remove packages that failed DLL build

* style: πŸ’„ add ESLint comma

* chore: πŸ€– apply changes from elastic#52209

* fix: πŸ› make Canvas Storybook build again

* chore: πŸ€– move Canvas stories to global Storybook

* chore: πŸ€– move more Canvas components to global Storybook

* chore: πŸ€– move more Canvas stories to global Storybook

* chore: πŸ€– move <ItemGrid> and <KeyboardShortcutsDoc> to NP SB

* chore: πŸ€– move shape picker Canvas stories to global Storybook

* chore: πŸ€– move Canvas sidebar stories to global Storybook

* fix: πŸ› split imports to not import path.resolve in Storybook

* chore: πŸ€– move tag and PDF panel Canvas stories to global SB

* chore: πŸ€– move Canvas share website flyout stories to global SB

* fix: πŸ› clean up <ShareWebsiteFlyout> imports

* chore: πŸ€– add back Canvas withInfo decorator

* chore: πŸ€– look for Canvas stories everywhere in /canvas folder

* test: πŸ’ mock correct files in Storyshots

* test: πŸ’ update Canvas Storyshot snapshots

* chore: πŸ€– move more Canvas components to global Storybook

* chore: πŸ€– move more Canvas components to global Storybook

* test: πŸ’ update Canvas Storyshots

* chore: πŸ€– rebuild @kbn/pm

* chore: πŸ€– refresh @kbn/pm dist/index.js artifact

* chore: πŸ€– update yarn.lock

* chore: πŸ€– update @kbn/pm artifact

* feat: 🎸 address review comments

* fix: πŸ› remove circular import

* chore: πŸ€– update yarn.lock

* test: πŸ’ disable a test suite

* test: πŸ’ update Canvas storyshots

* chore: πŸ€– remvoe build step from @kbn/storybook

* chore: πŸ€– enable disabled functional test suite

Co-authored-by: Spencer <[email protected]>
Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
3 people authored and jkelastic committed Jan 17, 2020
1 parent 69dd017 commit f9e4921
Show file tree
Hide file tree
Showing 140 changed files with 2,478 additions and 851 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"kbn:bootstrap": "yarn build:types && node scripts/register_git_hook",
"spec_to_console": "node scripts/spec_to_console",
"backport-skip-ci": "backport --prDescription \"[skip-ci]\"",
"storybook": "node scripts/storybook",
"cover:report": "nyc report --temp-dir target/kibana-coverage/functional --report-dir target/coverage/report --reporter=lcov && open ./target/coverage/report/lcov-report/index.html"
},
"repository": {
Expand Down Expand Up @@ -460,10 +461,13 @@
"postcss-url": "^8.0.0",
"prettier": "^1.19.1",
"proxyquire": "1.8.0",
"react-popper-tooltip": "^2.10.1",
"react-textarea-autosize": "^7.1.2",
"regenerate": "^1.4.0",
"sass-lint": "^1.12.1",
"selenium-webdriver": "^4.0.0-alpha.5",
"simple-git": "1.116.0",
"simplebar-react": "^2.1.0",
"sinon": "^7.4.2",
"strip-ansi": "^3.0.1",
"supertest": "^3.1.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/kbn-dev-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

export { withProcRunner } from './proc_runner';
export { withProcRunner, ProcRunner } from './proc_runner';
export {
ToolingLog,
ToolingLogTextWriter,
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-dev-utils/src/proc_runner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
*/

export { withProcRunner } from './with_proc_runner';
export { ProcRunner } from './proc_runner';
13 changes: 9 additions & 4 deletions packages/kbn-dev-utils/src/run/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ import exitHook from 'exit-hook';
import { pickLevelFromFlags, ToolingLog } from '../tooling_log';
import { createFlagError, isFailError } from './fail';
import { Flags, getFlags, getHelp } from './flags';
import { ProcRunner, withProcRunner } from '../proc_runner';

type CleanupTask = () => void;
type RunFn = (args: {
log: ToolingLog;
flags: Flags;
procRunner: ProcRunner;
addCleanupTask: (task: CleanupTask) => void;
}) => Promise<void> | void;

Expand Down Expand Up @@ -102,10 +104,13 @@ export async function run(fn: RunFn, options: Options = {}) {
}

try {
await fn({
log,
flags,
addCleanupTask: (task: CleanupTask) => cleanupTasks.push(task),
await withProcRunner(log, async procRunner => {
await fn({
log,
flags,
procRunner,
addCleanupTask: (task: CleanupTask) => cleanupTasks.push(task),
});
});
} finally {
doCleanup();
Expand Down
15 changes: 11 additions & 4 deletions packages/kbn-pm/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4489,6 +4489,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = __webpack_require__(36);
var proc_runner_1 = __webpack_require__(37);
exports.withProcRunner = proc_runner_1.withProcRunner;
exports.ProcRunner = proc_runner_1.ProcRunner;
var tooling_log_1 = __webpack_require__(415);
exports.ToolingLog = tooling_log_1.ToolingLog;
exports.ToolingLogTextWriter = tooling_log_1.ToolingLogTextWriter;
Expand Down Expand Up @@ -4761,6 +4762,8 @@ function __importDefault(mod) {
Object.defineProperty(exports, "__esModule", { value: true });
var with_proc_runner_1 = __webpack_require__(38);
exports.withProcRunner = with_proc_runner_1.withProcRunner;
var proc_runner_1 = __webpack_require__(39);
exports.ProcRunner = proc_runner_1.ProcRunner;


/***/ }),
Expand Down Expand Up @@ -37069,6 +37072,7 @@ const exit_hook_1 = tslib_1.__importDefault(__webpack_require__(348));
const tooling_log_1 = __webpack_require__(415);
const fail_1 = __webpack_require__(425);
const flags_1 = __webpack_require__(426);
const proc_runner_1 = __webpack_require__(37);
async function run(fn, options = {}) {
var _a;
const flags = flags_1.getFlags(process.argv.slice(2), options);
Expand Down Expand Up @@ -37118,10 +37122,13 @@ async function run(fn, options = {}) {
throw fail_1.createFlagError(`Unknown flag(s) "${flags.unexpected.join('", "')}"`);
}
try {
await fn({
log,
flags,
addCleanupTask: (task) => cleanupTasks.push(task),
await proc_runner_1.withProcRunner(log, async (procRunner) => {
await fn({
log,
flags,
procRunner,
addCleanupTask: (task) => cleanupTasks.push(task),
});
});
}
finally {
Expand Down
33 changes: 33 additions & 0 deletions packages/kbn-storybook/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Kibana Storybook

This package provides ability to add [Storybook](https://storybook.js.org/) to any Kibana plugin.

- [Setup Instructions](#setup-instructions)


## Setup Instructions

1. Add `storybook.js` launcher file to your plugin. For example, create a file at
`src/plugins/<plugin>/scripts/storybook.js`, with the following contents:

```js
import { join } from 'path';

// eslint-disable-next-line
require('@kbn/storybook').runStorybookCli({
name: '<plugin>',
storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.examples.tsx')],
});
```
2. Add your plugin alias to `src/dev/storybook/aliases.ts` config.
3. Create sample Storybook stories. For example, in your plugin create create a file at
`src/plugins/<plugin>/public/components/hello_world/__examples__/hello_world.examples.tsx` with
the following contents:

```jsx
import * as React from 'react';
import { storiesOf } from '@storybook/react';

storiesOf('Hello world', module).add('default', () => <div>Hello world!</div>);
```
4. Launch Storybook with `yarn storybook <plugin>`.
86 changes: 86 additions & 0 deletions packages/kbn-storybook/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

const fs = require('fs');
const { join } = require('path');
const Rx = require('rxjs');
const { first } = require('rxjs/operators');
const storybook = require('@storybook/react/standalone');
const { run } = require('@kbn/dev-utils');
const { generateStorybookEntry } = require('./lib/storybook_entry');
const { REPO_ROOT, CURRENT_CONFIG } = require('./lib/constants');
const { buildDll } = require('./lib/dll');

exports.runStorybookCli = config => {
const { name, storyGlobs } = config;
run(
async ({ flags, log, procRunner }) => {
log.debug('Global config:\n', require('./lib/constants'));

const currentConfig = JSON.stringify(config, null, 2);
const currentConfigDir = join(CURRENT_CONFIG, '..');
await fs.promises.mkdir(currentConfigDir, { recursive: true });
log.debug('Writing currentConfig:\n', CURRENT_CONFIG + '\n', currentConfig);
await fs.promises.writeFile(CURRENT_CONFIG, `exports.currentConfig = ${currentConfig};`);

await buildDll({
rebuildDll: flags.rebuildDll,
log,
procRunner,
});

// Build sass and continue when initial build complete
await procRunner.run('watch sass', {
cmd: process.execPath,
args: ['scripts/build_sass', '--watch'],
cwd: REPO_ROOT,
wait: /scss bundles created/,
});

const subj = new Rx.ReplaySubject(1);
generateStorybookEntry({ log, storyGlobs }).subscribe(subj);

await subj.pipe(first()).toPromise();

await Promise.all([
// route errors
subj.toPromise(),

new Promise(() => {
// storybook never completes, so neither will this promise
const configDir = join(__dirname, 'storybook_config');
log.debug('Config dir:', configDir);
storybook({
mode: 'dev',
port: 9001,
configDir,
});
}),
]);
},
{
flags: {
boolean: ['rebuildDll'],
},
description: `
Run the storybook examples for ${name}
`,
}
);
};
27 changes: 27 additions & 0 deletions packages/kbn-storybook/lib/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

const { resolve, dirname } = require('path');

exports.REPO_ROOT = dirname(require.resolve('../../../package.json'));
exports.ASSET_DIR = resolve(exports.REPO_ROOT, 'built_assets/storybook');
exports.CURRENT_CONFIG = resolve(exports.ASSET_DIR, 'current.config.js');
exports.STORY_ENTRY_PATH = resolve(exports.ASSET_DIR, 'stories.entry.js');
exports.DLL_DIST_DIR = resolve(exports.ASSET_DIR, 'dll');
exports.DLL_NAME = 'storybook_dll';
41 changes: 41 additions & 0 deletions packages/kbn-storybook/lib/dll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

const { resolve } = require('path');
const { existsSync } = require('fs');

const { REPO_ROOT, DLL_DIST_DIR } = require('./constants');

exports.buildDll = async ({ rebuildDll, log, procRunner }) => {
if (rebuildDll) {
log.info('rebuilding dll');
} else if (!existsSync(resolve(DLL_DIST_DIR, 'dll.js'))) {
log.info('dll missing, rebuilding');
} else {
log.info('dll exists');
return;
}

await procRunner.run('build dll ', {
cmd: require.resolve('webpack/bin/webpack'),
args: ['--config', require.resolve('./webpack.dll.config.js')],
cwd: REPO_ROOT,
wait: true,
});
};
89 changes: 89 additions & 0 deletions packages/kbn-storybook/lib/storybook_entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

const { resolve, relative, dirname } = require('path');
const Fs = require('fs');
const Rx = require('rxjs');
const { mergeMap, map, debounceTime } = require('rxjs/operators');
const normalize = require('normalize-path');
const { promisify } = require('util');

const watch = require('glob-watcher');
const mkdirp = require('mkdirp'); // eslint-disable-line
const glob = require('fast-glob');

const mkdirpAsync = promisify(mkdirp);
const writeFileAsync = promisify(Fs.writeFile);

const { REPO_ROOT, STORY_ENTRY_PATH } = require('./constants');
const STORE_ENTRY_DIR = dirname(STORY_ENTRY_PATH);

exports.generateStorybookEntry = ({ log, storyGlobs }) => {
const globs = ['built_assets/css/**/*.light.css', ...storyGlobs];
log.info('Storybook globs:\n', globs);
const norm = p => normalize(relative(STORE_ENTRY_DIR, p));

return Rx.defer(() =>
glob(globs, {
absolute: true,
cwd: REPO_ROOT,
onlyFiles: true,
})
).pipe(
map(paths => {
log.info('Discovered Storybook entry points:\n', paths);
return new Set(paths.map(norm));
}),
mergeMap(
paths =>
new Rx.Observable(observer => {
observer.next(paths);

const chokidar = watch(globs, { cwd: REPO_ROOT })
.on('add', path => {
observer.next(paths.add(norm(resolve(REPO_ROOT, path))));
})
.on('unlink', path => {
observer.next(paths.delete(norm(resolve(REPO_ROOT, path))));
});

return () => {
chokidar.close();
};
})
),
debounceTime(200),
mergeMap(async (paths, i) => {
await mkdirpAsync(STORE_ENTRY_DIR);

let content = '';
for (const path of paths) {
content += `require('${path}');\n`;
}

await writeFileAsync(STORY_ENTRY_PATH, content);

if (i === 0) {
log.info('%d paths written to entry file', paths.size);
} else {
log.info('entry file updated');
}
})
);
};
Loading

0 comments on commit f9e4921

Please sign in to comment.