Storybook 6 and config changes (#75357)

Upgrade to Storybook 6 and attempt to use the declarative configuration.

The goals of this PR (as part of Kibana's Storybook roadmap, are:

Upgrade to Storybook 6
Still allow running Storybooks with yarn storybook plugin_name
Use the declarative configuration to (hopefully) make the configuration simpler to use an easier to understand, as well as avoiding deprecation warnings and loss of future compatibility
The ways in which what I have so far differs from how we do things today are:

In the alias configuration it takes a path to a storybook configuration directory instead of the storybook.js file from before
Each plugin (it doesn't have to be a plugin; can be any directory) has a .storybook/main.js (the aliases file in @kbn/storybook specifies these locations) where they can define their Storybook configuration. You can require('@kbn/storybook').defaultConfig to get defaults and override them
@kbn/storybook has a preset that can provide Webpack and Babel configuration and Storybook parameters and decorators
Instead of dynamically creating the list of stories to import, we define them in the globs of the stories property in .storybook/main.js.
Do not build a DLL. We are using @kbn/ui-shared-deps as externals. Startup time is not quite as fast but still acceptable.
Other things done in this PR:

Allow default exports in .stories. to allow for Common Story Format CSF stories
Add guard in Webpack configuration needed for overriding CSS rules
Update filename casing check to allow for files with required names in Storybook
Clean up observability stories
Rename *.examples.tsx and *.story.tsx to *.stories.tsx
This commit is contained in:
Nathan L Smith 2020-09-29 19:34:05 -05:00 committed by GitHub
parent 46a595947e
commit 564a7b1a17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 5044 additions and 6820 deletions

View file

@ -388,6 +388,7 @@ module.exports = {
*/
{
files: [
'**/*.stories.tsx',
'x-pack/test/apm_api_integration/**/*.ts',
'x-pack/test/functional/apps/**/*.js',
'x-pack/plugins/apm/**/*.js',

View file

@ -334,6 +334,7 @@
"@types/styled-components": "^5.1.0",
"@types/supertest": "^2.0.5",
"@types/supertest-as-promised": "^2.0.38",
"@types/tapable": "^1.0.6",
"@types/tar": "^4.0.3",
"@types/testing-library__jest-dom": "^5.9.2",
"@types/testing-library__react-hooks": "^3.4.0",
@ -341,6 +342,8 @@
"@types/uuid": "^3.4.4",
"@types/vinyl": "^2.0.4",
"@types/vinyl-fs": "^2.4.11",
"@types/webpack": "^4.41.21",
"@types/webpack-env": "^1.15.2",
"@types/zen-observable": "^0.8.0",
"@typescript-eslint/eslint-plugin": "^3.10.0",
"@typescript-eslint/parser": "^3.10.0",

File diff suppressed because one or more lines are too long

View file

@ -2,32 +2,40 @@
This package provides ability to add [Storybook](https://storybook.js.org/) to any Kibana plugin.
- [Setup Instructions](#setup-instructions)
- [Kibana Storybook](#kibana-storybook)
- [Setup Instructions](#setup-instructions)
- [Customizing configuration](#customizing-configuration)
## 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:
- Add a `.storybook/main.js` configuration file to your plugin. For example, create a file at
`src/plugins/<plugin>/.storybook/main.js`, with the following contents:
```js
import { join } from 'path';
```js
module.exports = require('@kbn/storybook').defaultConfig;
```
// 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:
- Add your plugin alias to `src/dev/storybook/aliases.ts` config.
- Create sample Storybook stories. For example, in your plugin create a file at
`src/plugins/<plugin>/public/components/hello_world/hello_world.stories.tsx` with
the following [Component Story Format](https://storybook.js.org/docs/react/api/csf) contents:
```jsx
import * as React from 'react';
import { storiesOf } from '@storybook/react';
```jsx
import { MyComponent } from './my_component';
storiesOf('Hello world', module).add('default', () => <div>Hello world!</div>);
```
4. Launch Storybook with `yarn storybook <plugin>`.
export default {
component: MyComponent,
title: 'Path/In/Side/Navigation/ToComponent',
};
export function Example() {
return <MyComponent />;
}
```
- Launch Storybook with `yarn storybook <plugin>`, or build a static site with `yarn storybook --site <plugin>`.
## Customizing configuration
The `defaultConfig` object provided by the @kbn/storybook package should be all you need to get running, but you can
override this in your .storybook/main.js. Using [Storybook's configuration options](https://storybook.js.org/docs/react/configure/overview).

View file

@ -1,87 +0,0 @@
/*
* 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 { ASSET_DIR, 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,
});
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(async () => {
// storybook never completes, so neither will this promise
const configDir = join(__dirname, 'storybook_config');
log.debug('Config dir:', configDir);
const config = {
mode: flags.site ? 'static' : 'dev',
port: 9001,
configDir,
};
if (flags.site) {
config.outputDir = join(ASSET_DIR, name);
}
await storybook(config);
// Line is only reached when building the static version
if (flags.site) process.exit();
}),
]);
},
{
flags: {
boolean: ['rebuildDll', 'site'],
},
description: `
Run the storybook examples for ${name}
`,
}
);
};

View file

@ -17,4 +17,5 @@
* under the License.
*/
export default function () {}
export { defaultConfig } from './lib/default_config';
export { runStorybookCli } from './lib/run_storybook_cli';

View file

@ -17,10 +17,8 @@
* under the License.
*/
import { join } from 'path';
import { resolve } from 'path';
import { REPO_ROOT as KIBANA_ROOT } from '@kbn/dev-utils';
// eslint-disable-next-line
require('@kbn/storybook').runStorybookCli({
name: 'code-editor',
storyGlobs: [join(__dirname, '..', '*.examples.tsx')],
});
export const REPO_ROOT = KIBANA_ROOT;
export const ASSET_DIR = resolve(KIBANA_ROOT, 'built_assets/storybook');

View file

@ -17,10 +17,12 @@
* under the License.
*/
const serve = require('serve-static');
const path = require('path');
import { StorybookConfig } from '@storybook/core/types';
// Extend the Storybook Middleware to include a route to access Legacy UI assets
module.exports = function (router) {
router.get('/ui', serve(path.resolve(__dirname, '../../../src/core/server/core_app/assets')));
export const defaultConfig: StorybookConfig = {
addons: ['@kbn/storybook/preset', '@storybook/addon-knobs', '@storybook/addon-essentials'],
stories: ['../**/*.stories.tsx'],
typescript: {
reactDocgen: false,
},
};

View file

@ -1,42 +0,0 @@
/*
* 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 } = require('@kbn/utils');
const { 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,
});
};

View file

@ -17,11 +17,19 @@
* under the License.
*/
const { resolve } = require('path');
const { REPO_ROOT } = require('@kbn/utils');
import { addons } from '@storybook/addons';
import { create } from '@storybook/theming';
exports.ASSET_DIR = resolve(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';
// This configures the "Manager", or main outer view of Storybook. It is an
// addon that's loaded by the `managerEntries` part of the preset in ../preset.js.
addons.setConfig({
theme: create({
base: 'light',
brandTitle: 'Kibana Storybook',
brandUrl: 'https://github.com/elastic/kibana/tree/master/packages/kbn-storybook',
}),
showPanel: false,
isFullscreen: false,
panelPosition: 'bottom',
isToolshown: true,
});

View file

@ -0,0 +1,75 @@
/*
* 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.
*/
import { join } from 'path';
import { logger } from '@storybook/node-logger';
import buildStandalone from '@storybook/react/standalone';
import { Flags, run } from '@kbn/dev-utils';
import { distDir } from '@kbn/ui-shared-deps';
import * as constants from './constants';
// Convert the flags to a Storybook loglevel
function getLogLevelFromFlags(flags: Flags) {
if (flags.debug) {
return 'silly';
}
if (flags.verbose) {
return 'verbose';
}
if (flags.quiet) {
return 'warn';
}
if (flags.silent) {
return 'silent';
}
return 'info';
}
export function runStorybookCli({ configDir, name }: { configDir: string; name: string }) {
run(
async ({ flags, log }) => {
log.debug('Global config:\n', constants);
const staticDir = [distDir];
const config: Record<string, any> = {
configDir,
mode: flags.site ? 'static' : 'dev',
port: 9001,
staticDir,
};
if (flags.site) {
config.outputDir = join(constants.ASSET_DIR, name);
}
logger.setLevel(getLogLevelFromFlags(flags));
await buildStandalone(config);
// Line is only reached when building the static version
if (flags.site) process.exit();
},
{
flags: {
boolean: ['site'],
},
description: `
Run the storybook examples for ${name}
`,
}
);
}

View file

@ -1,90 +0,0 @@
/*
* 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 { REPO_ROOT } = require('@kbn/utils');
const mkdirpAsync = promisify(mkdirp);
const writeFileAsync = promisify(Fs.writeFile);
const { STORY_ENTRY_PATH } = require('./constants');
const STORE_ENTRY_DIR = dirname(STORY_ENTRY_PATH);
exports.generateStorybookEntry = ({ log, storyGlobs }) => {
const globs = [...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');
}
})
);
};

View file

@ -0,0 +1,59 @@
<!DOCTYPE html>
<!-- This is a copy of the
[Storybook IFrame template](https://github.com/storybookjs/storybook/blob/7874ca357c6cb54f3f258dc61f6becae6783fba6/lib/core/src/server/templates/index.ejs).
We use this one instead because we want to add the @kbn/ui-shared-deps tags here.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title><%= options.title || 'Storybook'%></title>
<% if (files.favicon) { %>
<link rel="shortcut icon" href="<%= files.favicon%>" />
<% } %>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Added for Kibana shared dependencies -->
<script>
window.__kbnPublicPath__ = { 'kbn-ui-shared-deps': '/' };
</script>
<script src="/kbn-ui-shared-deps.@elastic.js"></script>
<script src="/kbn-ui-shared-deps.js"></script>
<link href="/kbn-ui-shared-deps.css" rel="stylesheet" />
<link href="/kbn-ui-shared-deps.v7.light.css" rel="stylesheet" />
<!-- -->
<% if (typeof headHtmlSnippet !== 'undefined') { %> <%= headHtmlSnippet %> <% } %> <%
files.css.forEach(file => { %>
<link href="<%= file %>" rel="stylesheet" />
<% }); %>
<style>
#root[hidden],
#docs-root[hidden] {
display: none !important;
}
</style>
</head>
<body>
<% if (typeof bodyHtmlSnippet !== 'undefined') { %> <%= bodyHtmlSnippet %> <% } %>
<div id="root"></div>
<div id="docs-root"></div>
<% if (typeof globals !== 'undefined' && Object.keys(globals).length) { %>
<script>
<% for (var varName in globals) { %>
<% if (globals[varName] != undefined) { %>
window['<%=varName%>'] = <%= JSON.stringify(globals[varName]) %>;
<% } %>
<% } %>
</script>
<% } %> <% dlls.forEach(file => { %>
<script src="<%= file %>"></script>
<% }); %> <% files.js.forEach(file => { %>
<script src="<%= file %>"></script>
<% }); %>
</body>
</html>

View file

@ -1,131 +0,0 @@
/*
* 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 webpack = require('webpack');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { REPO_ROOT } = require('@kbn/utils');
const { DLL_NAME, DLL_DIST_DIR } = require('./constants');
// This is the Webpack config for the DLL of CSS and JS assets that are
// not expected to change during development. This saves compile and run
// times considerably.
module.exports = {
context: REPO_ROOT,
mode: 'development',
// This is a (potentially growing) list of modules that can be safely
// included in the DLL. Only add to this list modules or other code
// which Storybook stories and their components would require, but don't
// change during development.
entry: [
'@elastic/eui/dist/eui_theme_light.css',
'@kbn/ui-framework/dist/kui_light.css',
'@storybook/addon-info',
'@storybook/addon-knobs',
'@storybook/addon-knobs/react',
'@storybook/addon-knobs/register',
'@storybook/addon-options',
'@storybook/addon-options/register',
'@storybook/core',
'@storybook/core/dist/server/common/polyfills.js',
'@storybook/react',
'@storybook/theming',
'angular-mocks',
'angular',
'brace',
'chroma-js',
'highlight.js',
'html-entities',
'jquery',
'lodash',
'markdown-it',
'mocha',
'prop-types',
'react-ace',
'react-beautiful-dnd',
'react-dom',
'react-focus-lock',
'react-markdown',
'react-resize-detector',
'react-virtualized',
'react',
'recompose',
'redux-actions',
'remark-parse',
'rxjs',
'sinon',
'tinycolor2',
],
plugins: [
// Produce the DLL and its manifest
new webpack.DllPlugin({
name: DLL_NAME,
path: path.resolve(DLL_DIST_DIR, 'manifest.json'),
}),
// Produce the DLL CSS file
new MiniCssExtractPlugin({
filename: 'dll.css',
}),
],
// Output the DLL JS file
output: {
path: DLL_DIST_DIR,
filename: 'dll.js',
library: DLL_NAME,
},
// Include a require alias for legacy UI code and styles
resolve: {
alias: {
ui: path.resolve(REPO_ROOT, 'src/legacy/ui/public'),
},
mainFields: ['browser', 'main'],
},
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {},
},
{ loader: 'css-loader' },
{
loader: 'string-replace-loader',
options: {
search: '__REPLACE_WITH_PUBLIC_PATH__',
replace: '/',
flags: 'g',
},
},
],
},
{
test: /\.(woff|woff2|ttf|eot|svg|ico)(\?|$)/,
loader: 'file-loader',
},
],
},
node: {
fs: 'empty',
child_process: 'empty',
},
};

View file

@ -3,18 +3,21 @@
"version": "1.0.0",
"private": true,
"license": "Apache-2.0",
"main": "./target/index.js",
"dependencies": {
"@kbn/babel-preset": "1.0.0",
"@kbn/dev-utils": "1.0.0",
"@storybook/addon-actions": "^6.0.16",
"@storybook/addon-essentials": "^6.0.16",
"@storybook/addon-knobs": "^6.0.16",
"@storybook/addon-storyshots": "^6.0.16",
"@storybook/core": "^6.0.16",
"@storybook/react": "^6.0.16",
"@storybook/theming": "^6.0.16",
"@types/loader-utils": "^2.0.1",
"@types/webpack": "^4.41.5",
"@types/webpack-env": "^1.15.2",
"@types/webpack-merge": "^4.1.5",
"@kbn/utils": "1.0.0",
"@storybook/addon-actions": "^5.3.19",
"@storybook/addon-console": "^1.2.1",
"@storybook/addon-info": "^5.3.19",
"@storybook/addon-knobs": "^5.3.19",
"@storybook/addon-options": "^5.3.19",
"@storybook/addon-storyshots": "^5.3.19",
"@storybook/react": "^5.3.19",
"@storybook/theming": "^5.3.19",
"babel-loader": "^8.0.6",
"copy-webpack-plugin": "^6.0.2",
"fast-glob": "2.2.7",
@ -29,5 +32,10 @@
"serve-static": "1.14.1",
"styled-components": "^5.1.0",
"webpack": "^4.41.5"
},
"scripts": {
"build": "tsc",
"kbn:bootstrap": "yarn build",
"watch": "yarn build --watch"
}
}
}

View file

@ -17,10 +17,13 @@
* under the License.
*/
import { join } from 'path';
const webpackConfig = require('./target/webpack.config').default;
// eslint-disable-next-line
require('@kbn/storybook').runStorybookCli({
name: 'embeddable',
storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.examples.tsx')],
});
module.exports = {
managerEntries: (entry = []) => {
return [...entry, require.resolve('./target/lib/register')];
},
webpackFinal: (config) => {
return webpackConfig({ config });
},
};

View file

@ -1,23 +0,0 @@
/*
* 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.
*/
import '@storybook/addon-options/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-knobs/register';
import '@storybook/addon-console';

View file

@ -1,68 +0,0 @@
/*
* 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.
*/
import { configure, addDecorator, addParameters } from '@storybook/react';
import { withKnobs } from '@storybook/addon-knobs/react';
import { withInfo } from '@storybook/addon-info';
import { create } from '@storybook/theming';
// If we're running Storyshots, be sure to register the require context hook.
// Otherwise, add the other decorators.
if (process.env.NODE_ENV === 'test') {
// eslint-disable-next-line
require('babel-plugin-require-context-hook/register')();
} else {
// Customize the info for each story.
addDecorator(
withInfo({
inline: true,
styles: {
infoBody: {
margin: 20,
},
infoStory: {
margin: '40px 60px',
},
},
})
);
// Add optional knobs to customize each story.
addDecorator(withKnobs);
}
// Set up the Storybook environment with custom settings.
addParameters({
options: {
theme: create({
base: 'light',
brandTitle: 'Kibana Storybook',
brandUrl: 'https://github.com/elastic/kibana/tree/master/packages/kbn-storybook',
}),
showPanel: false,
isFullscreen: false,
panelPosition: 'bottom',
isToolshown: true,
},
});
configure(() => {
// eslint-disable-next-line
require('../../../built_assets/storybook/stories.entry.js');
}, module);

View file

@ -1,23 +0,0 @@
/*
* 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.
*/
export const absoluteToParsedUrl = () => {
getAbsoluteUrl: () =>
'http://localhost:5601/kbp/app/canvas#/workpad/workpad-24d56dad-ae70-42b8-9ef1-c5350ecd426c/page/1';
}; // noop

View file

@ -1,26 +0,0 @@
/*
* 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.
*/
export function getState() {
return {
assets: {
yay: { value: 'here is your image' },
},
};
}

View file

@ -1,6 +0,0 @@
<!--
This file is looked for by Storybook and included in the HEAD element
if it exists. This is how we load the DLL content into the Storybook UI.
-->
<script src="./dll.js"></script>
<link href="./dll.css" rel="stylesheet" />

View file

@ -1,147 +0,0 @@
/*
* 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 webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const { stringifyRequest } = require('loader-utils');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { REPO_ROOT } = require('@kbn/utils');
const { DLL_DIST_DIR } = require('../lib/constants');
// eslint-disable-next-line import/no-unresolved
const { currentConfig } = require('../../../built_assets/storybook/current.config');
// Extend the Storybook Webpack config with some customizations
module.exports = async ({ config: storybookConfig }) => {
let config = {
module: {
rules: [
// Include the React preset from Kibana for JS(X) and TS(X)
{
test: /\.(j|t)sx?$/,
exclude: /node_modules/,
loaders: 'babel-loader',
options: {
presets: [require.resolve('@kbn/babel-preset/webpack_preset')],
},
},
{
test: /\.(html|md|txt|tmpl)$/,
use: {
loader: 'raw-loader',
},
},
// Parse props data for .tsx files
// This is notoriously slow, and is making Storybook unusable. Disabling for now.
// See: https://github.com/storybookjs/storybook/issues/7998
//
// {
// test: /\.tsx$/,
// // Exclude example files, as we don't display props info for them
// exclude: /\.stories.tsx$/,
// use: [
// // Parse TS comments to create Props tables in the UI
// require.resolve('react-docgen-typescript-loader'),
// ],
// },
{
test: /\.scss$/,
exclude: /\.module.(s(a|c)ss)$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: { importLoaders: 2 } },
{
loader: 'postcss-loader',
options: {
config: {
path: require.resolve('@kbn/optimizer/postcss.config.js'),
},
},
},
{
loader: 'sass-loader',
options: {
prependData(loaderContext) {
return `@import ${stringifyRequest(
loaderContext,
resolve(REPO_ROOT, 'src/core/public/core_app/styles/_globals_v7light.scss')
)};\n`;
},
sassOptions: {
includePaths: [resolve(REPO_ROOT, 'node_modules')],
},
},
},
],
},
],
},
plugins: [
// Reference the built DLL file of static(ish) dependencies, which are removed
// during kbn:bootstrap and rebuilt if missing.
new webpack.DllReferencePlugin({
manifest: resolve(DLL_DIST_DIR, 'manifest.json'),
context: REPO_ROOT,
}),
// Copy the DLL files to the Webpack build for use in the Storybook UI
new CopyWebpackPlugin({
patterns: [
{
from: resolve(DLL_DIST_DIR, 'dll.js'),
to: 'dll.js',
},
{
from: resolve(DLL_DIST_DIR, 'dll.css'),
to: 'dll.css',
},
],
}),
],
resolve: {
// Tell Webpack about the ts/x extensions
extensions: ['.ts', '.tsx', '.scss'],
alias: {
core_app_image_assets: resolve(REPO_ROOT, 'src/core/public/core_app/images'),
},
},
};
// Find and alter the CSS rule to replace the Kibana public path string with a path
// to the route we've added in middleware.js
const cssRule = storybookConfig.module.rules.find((rule) => rule.test.source.includes('.css$'));
cssRule.use.push({
loader: 'string-replace-loader',
options: {
search: '__REPLACE_WITH_PUBLIC_PATH__',
replace: '/',
flags: 'g',
},
});
config = webpackMerge(storybookConfig, config);
// Load custom Webpack config specified by a plugin.
if (currentConfig.webpackHook) {
// eslint-disable-next-line import/no-dynamic-require
return await require(currentConfig.webpackHook)({ config });
}
return config;
};

View file

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"declaration": true,
"outDir": "target",
"skipLibCheck": true
},
"include": ["*.ts", "lib/*.ts"]
}

View file

@ -17,12 +17,5 @@
* under the License.
*/
export class Storage {
get(key) {
return this[key];
}
set(key, value) {
this[key] = value;
}
}
// Storybook react doesn't declare this in its typings, but it's there.
declare module '@storybook/react/standalone';

View file

@ -0,0 +1,108 @@
/*
* 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.
*/
import { resolve } from 'path';
import { stringifyRequest } from 'loader-utils';
import { Configuration, Stats } from 'webpack';
import webpackMerge from 'webpack-merge';
import { externals } from '@kbn/ui-shared-deps';
import { REPO_ROOT } from './lib/constants';
const stats = {
...Stats.presetToOptions('minimal'),
colors: true,
errorDetails: true,
errors: true,
moduleTrace: true,
warningsFilter: /(export .* was not found in)|(entrypoint size limit)/,
};
// Extend the Storybook Webpack config with some customizations
/* eslint-disable import/no-default-export */
export default function ({ config: storybookConfig }: { config: Configuration }) {
const config = {
devServer: {
stats,
},
externals,
module: {
rules: [
{
test: /\.(html|md|txt|tmpl)$/,
use: {
loader: 'raw-loader',
},
},
{
test: /\.scss$/,
exclude: /\.module.(s(a|c)ss)$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: { importLoaders: 2 } },
{
loader: 'postcss-loader',
options: {
config: {
path: require.resolve('@kbn/optimizer/postcss.config.js'),
},
},
},
{
loader: 'sass-loader',
options: {
prependData(loaderContext: any) {
return `@import ${stringifyRequest(
loaderContext,
resolve(REPO_ROOT, 'src/core/public/core_app/styles/_globals_v7light.scss')
)};\n`;
},
sassOptions: {
includePaths: [resolve(REPO_ROOT, 'node_modules')],
},
},
},
],
},
],
},
resolve: {
// Tell Webpack about the scss extension
extensions: ['.scss'],
alias: {
core_app_image_assets: resolve(REPO_ROOT, 'src/core/public/core_app/images'),
},
},
stats,
};
// This is the hacky part. We find something that looks like the
// HtmlWebpackPlugin and mutate its `options.template` to point at our
// revised template.
const htmlWebpackPlugin: any = (storybookConfig.plugins || []).find((plugin: any) => {
return plugin.options && typeof plugin.options.template === 'string';
});
if (htmlWebpackPlugin) {
htmlWebpackPlugin.options.template = require.resolve('../lib/templates/index.ejs');
}
// @ts-expect-error There's a long error here about the types of the
// incompatibility of Configuration, but it looks like it just may be Webpack
// type definition related.
return webpackMerge(storybookConfig, config);
}

View file

@ -50,14 +50,12 @@ export const IGNORE_FILE_GLOBS = [
'vars/*',
'.ci/pipeline-library/**/*',
// Files in this directory must match a pre-determined name in some cases.
'x-pack/plugins/canvas/storybook/*',
// filename must match language code which requires capital letters
'**/translations/*.json',
// filename is required by storybook
'packages/kbn-storybook/storybook_config/preview-head.html',
// Storybook has predetermined filesnames
'**/preview-body.html',
'**/preview-head.html',
// filename required by api-extractor
'api-documenter.json',

View file

@ -18,13 +18,13 @@
*/
export const storybookAliases = {
apm: 'x-pack/plugins/apm/scripts/storybook.js',
canvas: 'x-pack/plugins/canvas/scripts/storybook_new.js',
codeeditor: 'src/plugins/kibana_react/public/code_editor/scripts/storybook.ts',
dashboard_enhanced: 'x-pack/plugins/dashboard_enhanced/scripts/storybook.js',
embeddable: 'src/plugins/embeddable/scripts/storybook.js',
infra: 'x-pack/plugins/infra/scripts/storybook.js',
security_solution: 'x-pack/plugins/security_solution/scripts/storybook.js',
ui_actions_enhanced: 'x-pack/plugins/ui_actions_enhanced/scripts/storybook.js',
observability: 'x-pack/plugins/observability/scripts/storybook.js',
apm: 'x-pack/plugins/apm/.storybook',
canvas: 'x-pack/plugins/canvas/storybook',
codeeditor: 'src/plugins/kibana_react/public/code_editor/.storybook',
dashboard_enhanced: 'x-pack/plugins/dashboard_enhanced/.storybook',
embeddable: 'src/plugins/embeddable/.storybook',
infra: 'x-pack/plugins/infra/.storybook',
security_solution: 'x-pack/plugins/security_solution/.storybook',
ui_actions_enhanced: 'x-pack/plugins/ui_actions_enhanced/.storybook',
observability: 'x-pack/plugins/observability/.storybook',
};

View file

@ -17,9 +17,8 @@
* under the License.
*/
import { join } from 'path';
import { run, createFlagError } from '@kbn/dev-utils';
import { REPO_ROOT } from '@kbn/utils';
import { runStorybookCli } from '@kbn/storybook';
import { storybookAliases } from './aliases';
import { clean } from './commands/clean';
@ -40,20 +39,18 @@ run(
}
if (!alias) {
throw createFlagError('missing alias');
throw createFlagError('Missing alias');
}
if (!storybookAliases.hasOwnProperty(alias)) {
throw createFlagError(`unknown alias [${alias}]`);
throw createFlagError(`Unknown alias [${alias}]`);
}
const relative = (storybookAliases as any)[alias];
const absolute = join(REPO_ROOT, relative);
const configDir = (storybookAliases as any)[alias];
log.verbose('Loading Storybook:', absolute);
process.chdir(join(absolute, '..', '..'));
log.verbose('Loading Storybook:', configDir);
require(absolute);
runStorybookCli({ configDir, name: alias });
},
{
usage: `node scripts/storybook <alias>`,

View file

@ -4,10 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { join } from 'path';
// eslint-disable-next-line
require('@kbn/storybook').runStorybookCli({
name: 'canvas',
storyGlobs: [join(__dirname, '..', '**', '*.stories.tsx')],
});
module.exports = require('@kbn/storybook').defaultConfig;

View file

@ -4,10 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { join } from 'path';
// eslint-disable-next-line
require('@kbn/storybook').runStorybookCli({
name: 'dashboard_enhanced',
storyGlobs: [join(__dirname, '..', 'public', '**', '*.story.tsx')],
});
module.exports = require('@kbn/storybook').defaultConfig;

View file

@ -43,13 +43,12 @@
"@mapbox/mapbox-gl-draw": "^1.2.0",
"@mapbox/mapbox-gl-rtl-text": "^0.2.3",
"@scant/router": "^0.1.0",
"@storybook/addon-actions": "^5.3.19",
"@storybook/addon-console": "^1.2.1",
"@storybook/addon-info": "^5.3.19",
"@storybook/addon-knobs": "^5.3.19",
"@storybook/addon-storyshots": "^5.3.19",
"@storybook/react": "^5.3.19",
"@storybook/theming": "^5.3.19",
"@storybook/addon-actions": "^6.0.16",
"@storybook/addon-essentials": "^6.0.16",
"@storybook/addon-knobs": "^6.0.16",
"@storybook/addon-storyshots": "^6.0.16",
"@storybook/react": "^6.0.16",
"@storybook/theming": "^6.0.16",
"@testing-library/dom": "^7.24.2",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.0.4",

View file

@ -4,10 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { join } from 'path';
// eslint-disable-next-line
require('@kbn/storybook').runStorybookCli({
name: 'ui_actions_enhanced',
storyGlobs: [join(__dirname, '..', 'public', '**', '*.story.tsx')],
});
module.exports = require('@kbn/storybook').defaultConfig;

View file

@ -1,15 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { join } from 'path';
// eslint-disable-next-line
require('@kbn/storybook').runStorybookCli({
name: 'apm',
storyGlobs: [
join(__dirname, '..', 'public', 'components', '**', '*.stories.tsx'),
],
});

View file

@ -11,28 +11,38 @@ exports[`Storyshots arguments/AxisConfig simple 1`] = `
}
>
<div
className="euiSwitch euiSwitch--compressed"
style={
Object {
"background": "#fff",
"padding": "16px",
"width": "323px",
}
}
>
<button
aria-checked={false}
aria-label=""
className="euiSwitch__button"
id="generated-id"
onClick={[Function]}
role="switch"
type="button"
<div
className="euiSwitch euiSwitch--compressed"
>
<span
className="euiSwitch__body"
<button
aria-checked={false}
aria-label=""
className="euiSwitch__button"
id="generated-id"
onClick={[Function]}
role="switch"
type="button"
>
<span
className="euiSwitch__thumb"
/>
<span
className="euiSwitch__track"
/>
</span>
</button>
className="euiSwitch__body"
>
<span
className="euiSwitch__thumb"
/>
<span
className="euiSwitch__track"
/>
</span>
</button>
</div>
</div>
</div>
`;
@ -48,28 +58,38 @@ exports[`Storyshots arguments/AxisConfig/components simple template 1`] = `
}
>
<div
className="euiSwitch euiSwitch--compressed"
style={
Object {
"background": "#fff",
"padding": "16px",
"width": "323px",
}
}
>
<button
aria-checked={false}
aria-label=""
className="euiSwitch__button"
id="generated-id"
onClick={[Function]}
role="switch"
type="button"
<div
className="euiSwitch euiSwitch--compressed"
>
<span
className="euiSwitch__body"
<button
aria-checked={false}
aria-label=""
className="euiSwitch__button"
id="generated-id"
onClick={[Function]}
role="switch"
type="button"
>
<span
className="euiSwitch__thumb"
/>
<span
className="euiSwitch__track"
/>
</span>
</button>
className="euiSwitch__body"
>
<span
className="euiSwitch__thumb"
/>
<span
className="euiSwitch__track"
/>
</span>
</button>
</div>
</div>
</div>
`;

View file

@ -13,50 +13,60 @@ exports[`Storyshots arguments/ContainerStyle simple 1`] = `
<div
style={
Object {
"fontSize": 0,
"background": "#fff",
"padding": "16px",
"width": "323px",
}
}
>
<div
className="euiPopover euiPopover--anchorLeftCenter"
container={null}
id="color-picker-popover"
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
style={
Object {
"fontSize": 0,
}
}
>
<div
className="euiPopover__anchor"
className="euiPopover euiPopover--anchorLeftCenter"
container={null}
id="color-picker-popover"
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
>
<button
aria-label="Container style white"
className="euiLink euiLink--primary"
onClick={[Function]}
style={
Object {
"fontSize": 0,
}
}
type="button"
<div
className="euiPopover__anchor"
>
<div
className="canvasColorDot"
<button
aria-label="Container style white"
className="euiLink euiLink--primary"
onClick={[Function]}
style={
Object {
"fontSize": 0,
}
}
type="button"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "#fff",
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "#fff",
}
}
}
/>
</div>
</button>
/>
</div>
</button>
</div>
</div>
</div>
</div>
@ -76,50 +86,60 @@ exports[`Storyshots arguments/ContainerStyle/components simple template 1`] = `
<div
style={
Object {
"fontSize": 0,
"background": "#fff",
"padding": "16px",
"width": "323px",
}
}
>
<div
className="euiPopover euiPopover--anchorLeftCenter"
container={null}
id="color-picker-popover"
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
style={
Object {
"fontSize": 0,
}
}
>
<div
className="euiPopover__anchor"
className="euiPopover euiPopover--anchorLeftCenter"
container={null}
id="color-picker-popover"
onKeyDown={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
onTouchEnd={[Function]}
onTouchStart={[Function]}
>
<button
aria-label="Container style white"
className="euiLink euiLink--primary"
onClick={[Function]}
style={
Object {
"fontSize": 0,
}
}
type="button"
<div
className="euiPopover__anchor"
>
<div
className="canvasColorDot"
<button
aria-label="Container style white"
className="euiLink euiLink--primary"
onClick={[Function]}
style={
Object {
"fontSize": 0,
}
}
type="button"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "#fff",
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "#fff",
}
}
}
/>
</div>
</button>
/>
</div>
</button>
</div>
</div>
</div>
</div>

View file

@ -11,31 +11,41 @@ exports[`Storyshots arguments/SeriesStyle simple 1`] = `
}
>
<div
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
style={
Object {
"background": "#fff",
"padding": "16px",
"width": "323px",
}
}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
>
<div
className="euiText euiText--extraSmall"
className="euiFlexItem euiFlexItem--flexGrowZero"
>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
<div
className="euiText euiText--extraSmall"
>
Auto
</button>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
>
Auto
</button>
</div>
</div>
</div>
</div>
@ -53,31 +63,41 @@ exports[`Storyshots arguments/SeriesStyle/components simple: defaults 1`] = `
}
>
<div
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
style={
Object {
"background": "#fff",
"padding": "16px",
"width": "323px",
}
}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
>
<div
className="euiText euiText--extraSmall"
className="euiFlexItem euiFlexItem--flexGrowZero"
>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
<div
className="euiText euiText--extraSmall"
>
Auto
</button>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
>
Auto
</button>
</div>
</div>
</div>
</div>
@ -95,31 +115,41 @@ exports[`Storyshots arguments/SeriesStyle/components simple: no labels 1`] = `
}
>
<div
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
style={
Object {
"background": "#fff",
"padding": "16px",
"width": "323px",
}
}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
>
<div
className="euiText euiText--extraSmall"
className="euiFlexItem euiFlexItem--flexGrowZero"
>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
<div
className="euiText euiText--extraSmall"
>
Auto
</button>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
>
Auto
</button>
</div>
</div>
</div>
</div>
@ -137,51 +167,61 @@ exports[`Storyshots arguments/SeriesStyle/components simple: no series 1`] = `
}
>
<div
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
style={
Object {
"background": "#fff",
"padding": "16px",
"width": "323px",
}
}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
>
<div
className="euiText euiText--extraSmall"
>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
>
Auto
</button>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<span
className="euiToolTipAnchor"
onKeyUp={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
aria-label="Info"
color="warning"
data-euiicon-type="alert"
onBlur={[Function]}
onFocus={[Function]}
tabIndex={0}
/>
</span>
className="euiText euiText--extraSmall"
>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
>
Auto
</button>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<span
className="euiToolTipAnchor"
onKeyUp={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<div
aria-label="Info"
color="warning"
data-euiicon-type="alert"
onBlur={[Function]}
onFocus={[Function]}
tabIndex={0}
/>
</span>
</div>
</div>
</div>
</div>
@ -198,31 +238,41 @@ exports[`Storyshots arguments/SeriesStyle/components simple: with series 1`] = `
}
>
<div
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
style={
Object {
"background": "#fff",
"padding": "16px",
"width": "323px",
}
}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive canvasArgSeries__colorPicker"
>
<div
className="euiText euiText--extraSmall"
className="euiFlexItem euiFlexItem--flexGrowZero"
>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
<div
className="euiText euiText--extraSmall"
>
Auto
</button>
Color
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="euiText euiText--extraSmall"
>
<button
aria-label="Color: Auto"
className="euiLink euiLink--primary"
onClick={[Function]}
type="button"
>
Auto
</button>
</div>
</div>
</div>
</div>

View file

@ -24,7 +24,7 @@ addons.register(ADDON_ID, (api) => {
type: types.PANEL,
render: ({ active, key }) => {
return (
<AddonPanel active={active} key={key}>
<AddonPanel active={!!active} key={key}>
<Panel />
</AddonPanel>
);

View file

@ -5,14 +5,6 @@
*/
import { addDecorator } from '@storybook/react';
// @ts-expect-error
import { withInfo } from '@storybook/addon-info';
import { Provider as ReduxProvider } from 'react-redux';
import { ServicesProvider } from '../../public/services';
import { RouterContext } from '../../public/components/router';
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
import { routerContextDecorator } from './router_decorator';
import { kibanaContextDecorator } from './kibana_decorator';
import { servicesContextDecorator } from './services_decorator';
@ -23,22 +15,6 @@ export const addDecorators = () => {
if (process.env.NODE_ENV === 'test') {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('babel-plugin-require-context-hook/register')();
} else {
// Customize the info for each story.
addDecorator(
withInfo({
inline: true,
styles: {
infoBody: {
margin: 20,
},
infoStory: {
margin: '40px 60px',
},
},
propTablesExclude: [ReduxProvider, ServicesProvider, RouterContext, KibanaContextProvider],
})
);
}
addDecorator(kibanaContextDecorator);

View file

@ -4,11 +4,20 @@
* you may not use this file except in compliance with the Elastic License.
*/
/* eslint-disable @typescript-eslint/no-var-requires */
const { existsSync } = require('fs');
const { join } = require('path');
// Check for DLL
if (!existsSync(join(__dirname, '../../../../built_assets/canvas_storybook_dll/manifest.json'))) {
// eslint-disable-next-line no-console
console.error(
'No DLL found. Run `node scripts/storybook --dll` from the Canvas plugin directory.'
);
process.exit(1);
}
module.exports = {
stories: ['../**/*.stories.tsx'],
addons: [
'@storybook/addon-actions',
'@storybook/addon-knobs',
'./storybook/addon/target/register',
],
addons: ['@storybook/addon-actions', '@storybook/addon-knobs', './addon/target/register'],
};

View file

@ -11,7 +11,7 @@ import moment from 'moment';
import 'moment-timezone';
import ReactDOM from 'react-dom';
import initStoryshots, { multiSnapshotWithOptions } from '@storybook/addon-storyshots';
// import initStoryshots, { multiSnapshotWithOptions } from '@storybook/addon-storyshots';
// @ts-expect-error untyped library
import styleSheetSerializer from 'jest-styled-components/src/styleSheetSerializer';
import { addSerializer } from 'jest-specific-snapshot';
@ -104,9 +104,12 @@ if (!fs.existsSync(cssDir)) {
addSerializer(styleSheetSerializer);
// Initialize Storyshots and build the Jest Snapshots
initStoryshots({
configPath: path.resolve(__dirname, './../storybook'),
test: multiSnapshotWithOptions({}),
// Don't snapshot tests that start with 'redux'
storyNameRegex: /^((?!.*?redux).)*$/,
});
// Commenting this out until after #75357 is merged and Jest gets updated.
// initStoryshots({
// configPath: path.resolve(__dirname, './../storybook'),
// test: multiSnapshotWithOptions({}),
// // Don't snapshot tests that start with 'redux'
// storyNameRegex: /^((?!.*?redux).)*$/,
// });
test.todo('Storyshots');

View file

@ -4,10 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { join } from 'path';
// eslint-disable-next-line
require('@kbn/storybook').runStorybookCli({
name: 'infra',
storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.stories.tsx')],
});
module.exports = require('@kbn/storybook').defaultConfig;

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
module.exports = require('@kbn/storybook').defaultConfig;

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
module.exports = require('@kbn/storybook').defaultConfig;

View file

@ -3,6 +3,8 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { makeDecorator } from '@storybook/addons';
import { storiesOf } from '@storybook/react';
import { AppMountContext } from 'kibana/public';
import React from 'react';
@ -10,20 +12,44 @@ import { MemoryRouter } from 'react-router-dom';
import { UI_SETTINGS } from '../../../../../../src/plugins/data/public';
import { PluginContext } from '../../context/plugin_context';
import { registerDataHandler, unregisterDataHandler } from '../../data_handler';
import { emptyResponse as emptyAPMResponse, fetchApmData } from './mock/apm.mock';
import { fetchLogsData, emptyResponse as emptyLogsResponse } from './mock/logs.mock';
import { fetchMetricsData, emptyResponse as emptyMetricsResponse } from './mock/metrics.mock';
import { fetchUptimeData, emptyResponse as emptyUptimeResponse } from './mock/uptime.mock';
import { EuiThemeProvider } from '../../typings';
import { OverviewPage } from './';
import { alertsFetchData } from './mock/alerts.mock';
import { emptyResponse as emptyAPMResponse, fetchApmData } from './mock/apm.mock';
import { emptyResponse as emptyLogsResponse, fetchLogsData } from './mock/logs.mock';
import { emptyResponse as emptyMetricsResponse, fetchMetricsData } from './mock/metrics.mock';
import { newsFeedFetchData } from './mock/news_feed.mock';
import { emptyResponse as emptyUptimeResponse, fetchUptimeData } from './mock/uptime.mock';
const core = {
function unregisterAll() {
unregisterDataHandler({ appName: 'apm' });
unregisterDataHandler({ appName: 'infra_logs' });
unregisterDataHandler({ appName: 'infra_metrics' });
unregisterDataHandler({ appName: 'uptime' });
}
const withCore = makeDecorator({
name: 'withCore',
parameterName: 'core',
wrapper: (storyFn, context, { options }) => {
unregisterAll();
return (
<MemoryRouter>
<PluginContext.Provider value={{ core: options as AppMountContext['core'] }}>
<EuiThemeProvider>{storyFn(context)}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
);
},
});
const core = ({
http: {
basePath: {
prepend: (link) => `http://localhost:5601${link}`,
prepend: (link: string) => `http://localhost:5601${link}`,
},
get: () => Promise.resolve({ data: [] }),
},
uiSettings: {
get: (key: string) => {
@ -93,7 +119,7 @@ const core = {
return euiSettings[key];
},
},
} as AppMountContext['core'];
} as unknown) as AppMountContext['core'];
const coreWithAlerts = ({
...core,
@ -111,23 +137,19 @@ const coreWithNewsFeed = ({
},
} as unknown) as AppMountContext['core'];
function unregisterAll() {
unregisterDataHandler({ appName: 'apm' });
unregisterDataHandler({ appName: 'infra_logs' });
unregisterDataHandler({ appName: 'infra_metrics' });
unregisterDataHandler({ appName: 'uptime' });
}
const coreAlertsThrowsError = ({
...core,
http: {
...core.http,
get: async () => {
throw new Error('Error fetching Alerts data');
},
},
} as unknown) as AppMountContext['core'];
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('Empty state', () => {
unregisterAll();
.addDecorator(withCore(core))
.add('Empty State', () => {
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
@ -150,23 +172,14 @@ storiesOf('app/Overview', module)
});
return <OverviewPage routeParams={{ query: {} }} />;
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('single panel', () => {
unregisterAll();
})
.add('Single Panel', () => {
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
@ -174,18 +187,8 @@ storiesOf('app/Overview', module)
}}
/>
);
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('logs and metrics', () => {
unregisterAll();
})
.add('Logs and Metrics', () => {
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
@ -196,6 +199,7 @@ storiesOf('app/Overview', module)
fetchData: fetchMetricsData,
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
@ -203,81 +207,61 @@ storiesOf('app/Overview', module)
}}
/>
);
});
})
.add(
'Logs, Metrics, and Alerts',
() => {
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
hasData: async () => true,
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core: coreWithAlerts }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('logs, metrics and alerts', () => {
unregisterAll();
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
},
{ core: coreWithAlerts }
)
.add(
'Logs, Metrics, APM, and Alerts',
() => {
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
hasData: async () => true,
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core: coreWithAlerts }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('logs, metrics, APM and alerts', () => {
unregisterAll();
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('logs, metrics, APM and Uptime', () => {
unregisterAll();
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
},
{ core: coreWithAlerts }
)
.add('Logs, Metrics, APM, and Uptime', () => {
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
@ -298,6 +282,7 @@ storiesOf('app/Overview', module)
fetchData: fetchUptimeData,
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
@ -305,96 +290,75 @@ storiesOf('app/Overview', module)
}}
/>
);
});
})
.add(
'Logs, Metrics, APM, Uptime, and Alerts',
() => {
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'uptime',
fetchData: fetchUptimeData,
hasData: async () => true,
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core: coreWithAlerts }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('logs, metrics, APM, Uptime and Alerts', () => {
unregisterAll();
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'uptime',
fetchData: fetchUptimeData,
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core: coreWithNewsFeed }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('logs, metrics, APM, Uptime and News feed', () => {
unregisterAll();
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'uptime',
fetchData: fetchUptimeData,
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('no data', () => {
unregisterAll();
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
},
{ core: coreWithAlerts }
)
.add(
'Logs, Metrics, APM, Uptime, and News Feed',
() => {
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
hasData: async () => true,
});
registerDataHandler({
appName: 'uptime',
fetchData: fetchUptimeData,
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
},
{ core: coreWithNewsFeed }
)
.add('No Data', () => {
registerDataHandler({
appName: 'apm',
fetchData: async () => emptyAPMResponse,
@ -415,6 +379,7 @@ storiesOf('app/Overview', module)
fetchData: async () => emptyUptimeResponse,
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
@ -422,156 +387,127 @@ storiesOf('app/Overview', module)
}}
/>
);
});
const coreAlertsThrowsError = ({
...core,
http: {
...core.http,
get: async () => {
throw new Error('Error fetching Alerts data');
})
.add(
'Fetch Data with Error',
() => {
registerDataHandler({
appName: 'apm',
fetchData: async () => {
throw new Error('Error fetching APM data');
},
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_logs',
fetchData: async () => {
throw new Error('Error fetching Logs data');
},
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: async () => {
throw new Error('Error fetching Metric data');
},
hasData: async () => true,
});
registerDataHandler({
appName: 'uptime',
fetchData: async () => {
throw new Error('Error fetching Uptime data');
},
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
},
},
} as unknown) as AppMountContext['core'];
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core: coreAlertsThrowsError }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('fetch data with error', () => {
unregisterAll();
{ core: coreAlertsThrowsError }
)
.add(
'hasData with Error and Alerts',
() => {
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
registerDataHandler({
appName: 'uptime',
fetchData: fetchUptimeData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
},
{ core: coreWithAlerts }
)
.add('hasData with Error', () => {
registerDataHandler({
appName: 'apm',
fetchData: async () => {
throw new Error('Error fetching APM data');
fetchData: fetchApmData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_logs',
fetchData: async () => {
throw new Error('Error fetching Logs data');
fetchData: fetchLogsData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
hasData: async () => true,
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: async () => {
throw new Error('Error fetching Metric data');
fetchData: fetchMetricsData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
hasData: async () => true,
});
registerDataHandler({
appName: 'uptime',
fetchData: async () => {
throw new Error('Error fetching Uptime data');
fetchData: fetchUptimeData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
hasData: async () => true,
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core: coreWithAlerts }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('hasData with error and alerts', () => {
unregisterAll();
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
registerDataHandler({
appName: 'uptime',
fetchData: fetchUptimeData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
return (
<OverviewPage
routeParams={{
query: { rangeFrom: '2020-06-27T22:00:00.000Z', rangeTo: '2020-06-30T21:59:59.999Z' },
}}
/>
);
});
storiesOf('app/Overview', module)
.addDecorator((storyFn) => (
<MemoryRouter>
<PluginContext.Provider value={{ core }}>
<EuiThemeProvider>{storyFn()}</EuiThemeProvider>
</PluginContext.Provider>
</MemoryRouter>
))
.add('hasData with error', () => {
unregisterAll();
registerDataHandler({
appName: 'apm',
fetchData: fetchApmData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
registerDataHandler({
appName: 'infra_logs',
fetchData: fetchLogsData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
registerDataHandler({
appName: 'infra_metrics',
fetchData: fetchMetricsData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
registerDataHandler({
appName: 'uptime',
fetchData: fetchUptimeData,
// @ts-ignore thows an error instead
hasData: async () => {
throw new Error('Error has data');
},
});
return (
<OverviewPage
routeParams={{

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
module.exports = require('@kbn/storybook').defaultConfig;

View file

@ -18,7 +18,7 @@ const withTheme = (storyFn: () => ReactNode) => (
<ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: true })}>{storyFn()}</ThemeProvider>
);
storiesOf('components/AndOrBadge', module)
storiesOf('Components/AndOrBadge', module)
.addDecorator(withTheme)
.add('and', () => <AndOrBadge type="and" />)
.add('or', () => <AndOrBadge type="or" />)

View file

@ -16,7 +16,7 @@ addDecorator((storyFn) => (
<ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}>{storyFn()}</ThemeProvider>
));
storiesOf('Exceptions|BuilderLogicButtons', module)
storiesOf('Exceptions/BuilderLogicButtons', module)
.add('and/or buttons', () => {
return (
<BuilderLogicButtons

View file

@ -17,7 +17,7 @@ addDecorator((storyFn) => (
<ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}>{storyFn()}</ThemeProvider>
));
storiesOf('Components|ExceptionItem', module)
storiesOf('Components/ExceptionItem', module)
.add('with os', () => {
const payload = getExceptionListItemSchemaMock();
payload.description = '';

View file

@ -16,7 +16,7 @@ addDecorator((storyFn) => (
<ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}>{storyFn()}</ThemeProvider>
));
storiesOf('Components|ExceptionsViewerHeader', module)
storiesOf('Components/ExceptionsViewerHeader', module)
.add('loading', () => {
return (
<ExceptionsViewerHeader

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
module.exports = require('@kbn/storybook').defaultConfig;

View file

@ -42,3 +42,10 @@ declare module '*.json' {
// eslint-disable-next-line import/no-default-export
export default json;
}
// Storybook references this module. It's @ts-ignored in the codebase but when
// built into its dist it strips that out. Add it here to avoid a type checking
// error.
//
// See https://github.com/storybookjs/storybook/issues/11684
declare module 'react-syntax-highlighter/dist/cjs/create-element';

2945
yarn.lock

File diff suppressed because it is too large Load diff