[9.0] Upgrade to Storybook 8 (#195148) (#214680)

# Backport

This will backport the following commits from `main` to `9.0`:
- [Upgrade to Storybook 8
(#195148)](https://github.com/elastic/kibana/pull/195148)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Brad
White","email":"Ikuni17@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-03-14T22:41:03Z","message":"Upgrade
to Storybook 8 (#195148)\n\n## Summary\n\nDepends on #191106\nCloses
#171591\n\nThis PR migrates Storybook from `6.x` to `8.x`. Please see
the\n[migration\nguide](https://storybook.js.org/docs/migration-guide/from-older-version)\nfor
an overview of the changes because there are many breaking
changes\nwhich effect Kibana. The TODO list below is not inclusive of
all the\nchanges.\n\n## Reviewers\n### Each commit contains all files
changed for a specific codeowner,\nplease find your respective commit to
make review easier.\n\nA **first step before code review** should be
checking the
[`Storybooks\nPreview`](https://ci-artifacts.kibana.dev/storybooks/pr-195148/index.html)\nfrom
CI for any runtime or style issues which were missed. The preview\ncan
be compared to a build from
`main`\n[here](https://ci-artifacts.kibana.dev/storybooks/pr-212585/index.html).\nIt
is worth noting that some stories have runtime issues which
existed\nbefore this migration.\n\nMost stories appear to have been
migrated properly, but the Operations\nteam does not have prior
knowledge into every story. Some of the\nmigration was able to be
automated through Storybook provided scripts.\nIt is possible this
wasn't entirely correct due to the structure of some\nstories.
Additionally, part of this migration is moving Storybook to\nWebpack 5
which changed how styles are being loaded.\n\n#### TODO\n- [x] Migrate
`stories.mdx`\n- [x] storyshots\n- [x]
[Migrate\npackages](https://storybook.js.org/docs/migration-guide/from-older-version#package-structure-changes)\nwhich
were removed in `8.0`\n- [x] `react-doc-gen` resolution\n- [x]
[Migrate\nblocks](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#addon-docs-removed-deprecated-blocksjs-entry)\n-
[x] Migrate deprecated `addon-knobs`
to\n[addon-controls](https://www.npmjs.com/package/@storybook/addon-controls)\n-
[x] React Fast Refresh support\n- [x] Watch flag callback\n- [x]
`canvas` webpack\n- [x] Rerun CSF migrations for new stories\n- [x]
Handle ESM import for `addon-docs`\n- [x] `'@storybook/addon-actions'
should be listed in the project's\ndependencies. Run 'npm i -S
@storybook/addon-actions' to
add\niteslint[import/no-extraneous-dependencies](https://github.com/import-js/eslint-plugin-import/blob/v2.28.0/docs/rules/no-extraneous-dependencies.md)`\n-
[x] `addDecorator` migration \n- [x] `addParameter` migration\n- [x]
static build\n- [ ] determine if #176500 is solved or push to followup
PR\n - This will need to be fixed separately\n- [x] revert
`.buildkite/pipelines/pull_request/base.yml`
&\n`.buildkite/scripts/pipelines/pull_request/pipeline.ts` to
`main`\n\n---------\n\nCo-authored-by: Tiago Costa
<tiago.costa@elastic.co>\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\nCo-authored-by: Jacek
Kolezynski <jacek.kolezynski@elastic.co>\nCo-authored-by: Stratoula
Kalafateli <efstratia.kalafateli@elastic.co>\nCo-authored-by: Clint
Andrew Hall
<clint@clintandrewhall.com>","sha":"403b5f23636d61809d23851f856ff0e26235b22f","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Operations","release_note:skip","Team:Fleet","v9.0.0","ci:build-storybooks","Team:Obs
AI Assistant","backport:version","v9.1.0","v8.19.0"],"title":"Upgrade to
Storybook
8","number":195148,"url":"https://github.com/elastic/kibana/pull/195148","mergeCommit":{"message":"Upgrade
to Storybook 8 (#195148)\n\n## Summary\n\nDepends on #191106\nCloses
#171591\n\nThis PR migrates Storybook from `6.x` to `8.x`. Please see
the\n[migration\nguide](https://storybook.js.org/docs/migration-guide/from-older-version)\nfor
an overview of the changes because there are many breaking
changes\nwhich effect Kibana. The TODO list below is not inclusive of
all the\nchanges.\n\n## Reviewers\n### Each commit contains all files
changed for a specific codeowner,\nplease find your respective commit to
make review easier.\n\nA **first step before code review** should be
checking the
[`Storybooks\nPreview`](https://ci-artifacts.kibana.dev/storybooks/pr-195148/index.html)\nfrom
CI for any runtime or style issues which were missed. The preview\ncan
be compared to a build from
`main`\n[here](https://ci-artifacts.kibana.dev/storybooks/pr-212585/index.html).\nIt
is worth noting that some stories have runtime issues which
existed\nbefore this migration.\n\nMost stories appear to have been
migrated properly, but the Operations\nteam does not have prior
knowledge into every story. Some of the\nmigration was able to be
automated through Storybook provided scripts.\nIt is possible this
wasn't entirely correct due to the structure of some\nstories.
Additionally, part of this migration is moving Storybook to\nWebpack 5
which changed how styles are being loaded.\n\n#### TODO\n- [x] Migrate
`stories.mdx`\n- [x] storyshots\n- [x]
[Migrate\npackages](https://storybook.js.org/docs/migration-guide/from-older-version#package-structure-changes)\nwhich
were removed in `8.0`\n- [x] `react-doc-gen` resolution\n- [x]
[Migrate\nblocks](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#addon-docs-removed-deprecated-blocksjs-entry)\n-
[x] Migrate deprecated `addon-knobs`
to\n[addon-controls](https://www.npmjs.com/package/@storybook/addon-controls)\n-
[x] React Fast Refresh support\n- [x] Watch flag callback\n- [x]
`canvas` webpack\n- [x] Rerun CSF migrations for new stories\n- [x]
Handle ESM import for `addon-docs`\n- [x] `'@storybook/addon-actions'
should be listed in the project's\ndependencies. Run 'npm i -S
@storybook/addon-actions' to
add\niteslint[import/no-extraneous-dependencies](https://github.com/import-js/eslint-plugin-import/blob/v2.28.0/docs/rules/no-extraneous-dependencies.md)`\n-
[x] `addDecorator` migration \n- [x] `addParameter` migration\n- [x]
static build\n- [ ] determine if #176500 is solved or push to followup
PR\n - This will need to be fixed separately\n- [x] revert
`.buildkite/pipelines/pull_request/base.yml`
&\n`.buildkite/scripts/pipelines/pull_request/pipeline.ts` to
`main`\n\n---------\n\nCo-authored-by: Tiago Costa
<tiago.costa@elastic.co>\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\nCo-authored-by: Jacek
Kolezynski <jacek.kolezynski@elastic.co>\nCo-authored-by: Stratoula
Kalafateli <efstratia.kalafateli@elastic.co>\nCo-authored-by: Clint
Andrew Hall
<clint@clintandrewhall.com>","sha":"403b5f23636d61809d23851f856ff0e26235b22f"}},"sourceBranch":"main","suggestedTargetBranches":["9.0","8.x"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/195148","number":195148,"mergeCommit":{"message":"Upgrade
to Storybook 8 (#195148)\n\n## Summary\n\nDepends on #191106\nCloses
#171591\n\nThis PR migrates Storybook from `6.x` to `8.x`. Please see
the\n[migration\nguide](https://storybook.js.org/docs/migration-guide/from-older-version)\nfor
an overview of the changes because there are many breaking
changes\nwhich effect Kibana. The TODO list below is not inclusive of
all the\nchanges.\n\n## Reviewers\n### Each commit contains all files
changed for a specific codeowner,\nplease find your respective commit to
make review easier.\n\nA **first step before code review** should be
checking the
[`Storybooks\nPreview`](https://ci-artifacts.kibana.dev/storybooks/pr-195148/index.html)\nfrom
CI for any runtime or style issues which were missed. The preview\ncan
be compared to a build from
`main`\n[here](https://ci-artifacts.kibana.dev/storybooks/pr-212585/index.html).\nIt
is worth noting that some stories have runtime issues which
existed\nbefore this migration.\n\nMost stories appear to have been
migrated properly, but the Operations\nteam does not have prior
knowledge into every story. Some of the\nmigration was able to be
automated through Storybook provided scripts.\nIt is possible this
wasn't entirely correct due to the structure of some\nstories.
Additionally, part of this migration is moving Storybook to\nWebpack 5
which changed how styles are being loaded.\n\n#### TODO\n- [x] Migrate
`stories.mdx`\n- [x] storyshots\n- [x]
[Migrate\npackages](https://storybook.js.org/docs/migration-guide/from-older-version#package-structure-changes)\nwhich
were removed in `8.0`\n- [x] `react-doc-gen` resolution\n- [x]
[Migrate\nblocks](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#addon-docs-removed-deprecated-blocksjs-entry)\n-
[x] Migrate deprecated `addon-knobs`
to\n[addon-controls](https://www.npmjs.com/package/@storybook/addon-controls)\n-
[x] React Fast Refresh support\n- [x] Watch flag callback\n- [x]
`canvas` webpack\n- [x] Rerun CSF migrations for new stories\n- [x]
Handle ESM import for `addon-docs`\n- [x] `'@storybook/addon-actions'
should be listed in the project's\ndependencies. Run 'npm i -S
@storybook/addon-actions' to
add\niteslint[import/no-extraneous-dependencies](https://github.com/import-js/eslint-plugin-import/blob/v2.28.0/docs/rules/no-extraneous-dependencies.md)`\n-
[x] `addDecorator` migration \n- [x] `addParameter` migration\n- [x]
static build\n- [ ] determine if #176500 is solved or push to followup
PR\n - This will need to be fixed separately\n- [x] revert
`.buildkite/pipelines/pull_request/base.yml`
&\n`.buildkite/scripts/pipelines/pull_request/pipeline.ts` to
`main`\n\n---------\n\nCo-authored-by: Tiago Costa
<tiago.costa@elastic.co>\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\nCo-authored-by: Jacek
Kolezynski <jacek.kolezynski@elastic.co>\nCo-authored-by: Stratoula
Kalafateli <efstratia.kalafateli@elastic.co>\nCo-authored-by: Clint
Andrew Hall
<clint@clintandrewhall.com>","sha":"403b5f23636d61809d23851f856ff0e26235b22f"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
This commit is contained in:
Brad White 2025-03-19 12:55:47 -06:00 committed by GitHub
parent 5acd953a80
commit 1c41d899ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
516 changed files with 13184 additions and 13840 deletions

View file

@ -73,4 +73,4 @@ RUN echo "source ${KBN_DIR}/.devcontainer/scripts/env.sh" >> ${HOME}/.bashrc &&
echo "source ${KBN_DIR}/.devcontainer/scripts/env.sh" >> ${HOME}/.zshrc echo "source ${KBN_DIR}/.devcontainer/scripts/env.sh" >> ${HOME}/.zshrc
# This is for documentation. Ports are exposed via devcontainer.json # This is for documentation. Ports are exposed via devcontainer.json
EXPOSE 9200 5601 9229 9230 9231 EXPOSE 9200 5601 9229 9230 9231 9001

View file

@ -24,7 +24,8 @@
5601, 5601,
9229, 9229,
9230, 9230,
9231 9231,
9001
], ],
"postStartCommand": "${containerWorkspaceFolder}/.devcontainer/scripts/post_start.sh", "postStartCommand": "${containerWorkspaceFolder}/.devcontainer/scripts/post_start.sh",
"remoteUser": "vscode", "remoteUser": "vscode",

1
.github/CODEOWNERS vendored
View file

@ -1590,6 +1590,7 @@ packages/kbn-monaco/src/esql @elastic/kibana-esql
/.devcontainer/ @elastic/kibana-operations /.devcontainer/ @elastic/kibana-operations
/.eslintrc.js @elastic/kibana-operations /.eslintrc.js @elastic/kibana-operations
/.eslintignore @elastic/kibana-operations /.eslintignore @elastic/kibana-operations
/.ci/.storybook @elastic/kibana-operations
# QA - Appex QA # QA - Appex QA
/x-pack/platform/plugins/shared/maps/ui_tests @elastic/appex-qa # temporarily /x-pack/platform/plugins/shared/maps/ui_tests @elastic/appex-qa # temporarily

View file

@ -60,7 +60,7 @@
"serverless-security": "node scripts/kibana --dev --serverless=security", "serverless-security": "node scripts/kibana --dev --serverless=security",
"spec_to_console": "node scripts/spec_to_console", "spec_to_console": "node scripts/spec_to_console",
"start": "node scripts/kibana --dev", "start": "node scripts/kibana --dev",
"storybook": "node --no-deprecation scripts/storybook", "storybook": "node scripts/storybook",
"test:ftr": "node scripts/functional_tests", "test:ftr": "node scripts/functional_tests",
"test:ftr:runner": "node scripts/functional_test_runner", "test:ftr:runner": "node scripts/functional_test_runner",
"test:ftr:server": "node scripts/functional_tests_server", "test:ftr:server": "node scripts/functional_tests_server",
@ -77,6 +77,7 @@
"yarn": "^1.22.19" "yarn": "^1.22.19"
}, },
"resolutions": { "resolutions": {
"**/@babel/parser": "7.24.7",
"**/@bazel/typescript/protobufjs": "6.11.4", "**/@bazel/typescript/protobufjs": "6.11.4",
"**/@hello-pangea/dnd": "16.6.0", "**/@hello-pangea/dnd": "16.6.0",
"**/@langchain/core": "^0.3.40", "**/@langchain/core": "^0.3.40",
@ -92,6 +93,7 @@
"**/remark-parse/trim": "1.0.1", "**/remark-parse/trim": "1.0.1",
"**/sharp": "0.32.6", "**/sharp": "0.32.6",
"**/typescript": "5.1.6", "**/typescript": "5.1.6",
"**/util": "^0.11.1",
"@aws-sdk/client-bedrock-agent-runtime": "^3.744.0", "@aws-sdk/client-bedrock-agent-runtime": "^3.744.0",
"@aws-sdk/client-bedrock-runtime": "^3.744.0", "@aws-sdk/client-bedrock-runtime": "^3.744.0",
"@aws-sdk/client-kendra": "3.744.0", "@aws-sdk/client-kendra": "3.744.0",
@ -1537,28 +1539,27 @@
"@octokit/rest": "^21.1.1", "@octokit/rest": "^21.1.1",
"@parcel/watcher": "^2.1.0", "@parcel/watcher": "^2.1.0",
"@playwright/test": "1.49.0", "@playwright/test": "1.49.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
"@redocly/cli": "^1.29.0", "@redocly/cli": "^1.29.0",
"@statoscope/webpack-plugin": "^5.28.2", "@statoscope/webpack-plugin": "^5.28.2",
"@storybook/addon-a11y": "^6.5.16", "@storybook/addon-a11y": "^8.6.3",
"@storybook/addon-actions": "^6.5.16", "@storybook/addon-actions": "^8.6.3",
"@storybook/addon-docs": "^6.5.16", "@storybook/addon-essentials": "^8.6.3",
"@storybook/addon-essentials": "^6.5.16", "@storybook/addon-jest": "8.6.3",
"@storybook/addon-knobs": "^6.4.0", "@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-storyshots": "^6.5.16", "@storybook/addon-webpack5-compiler-babel": "^3.0.5",
"@storybook/addons": "^6.5.16", "@storybook/blocks": "^8.6.3",
"@storybook/api": "^6.5.16", "@storybook/components": "^8.6.3",
"@storybook/builder-webpack5": "^6.5.16", "@storybook/core-events": "^8.6.3",
"@storybook/client-api": "^6.5.16", "@storybook/core-server": "^8.6.3",
"@storybook/components": "^6.5.16", "@storybook/icons": "^1.3.2",
"@storybook/core": "^6.5.16", "@storybook/manager-api": "^8.6.3",
"@storybook/core-common": "^6.5.16", "@storybook/preview-api": "^8.6.3",
"@storybook/core-events": "^6.5.16", "@storybook/react": "^8.6.3",
"@storybook/manager-webpack5": "^6.5.16", "@storybook/react-webpack5": "^8.6.3",
"@storybook/node-logger": "^6.5.16", "@storybook/test": "^8.6.3",
"@storybook/preview-web": "^6.5.16", "@storybook/theming": "^8.6.3",
"@storybook/react": "^6.5.16", "@storybook/types": "^8.6.3",
"@storybook/testing-react": "^1.3.0",
"@storybook/theming": "^6.5.16",
"@testing-library/dom": "^10.4.0", "@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1", "@testing-library/react": "^16.0.1",
@ -1646,6 +1647,7 @@
"@types/picomatch": "^2.3.0", "@types/picomatch": "^2.3.0",
"@types/pixelmatch": "^5.2.4", "@types/pixelmatch": "^5.2.4",
"@types/pngjs": "^6.0.5", "@types/pngjs": "^6.0.5",
"@types/prettier": "^2.7.3",
"@types/prop-types": "^15.7.5", "@types/prop-types": "^15.7.5",
"@types/rbush": "^3.0.0", "@types/rbush": "^3.0.0",
"@types/react": "~18.2.0", "@types/react": "~18.2.0",
@ -1760,6 +1762,7 @@
"fetch-mock": "^10.1.0", "fetch-mock": "^10.1.0",
"file-loader": "^4.2.0", "file-loader": "^4.2.0",
"find-cypress-specs": "^1.41.4", "find-cypress-specs": "^1.41.4",
"fix-esm": "^1.0.1",
"form-data": "^4.0.1", "form-data": "^4.0.1",
"geckodriver": "^5.0.0", "geckodriver": "^5.0.0",
"gulp-brotli": "^3.0.0", "gulp-brotli": "^3.0.0",
@ -1826,6 +1829,7 @@
"prettier": "^2.8.8", "prettier": "^2.8.8",
"proxy": "^2.1.1", "proxy": "^2.1.1",
"react-is": "~18.2.0", "react-is": "~18.2.0",
"react-refresh": "^0.16.0",
"react-test-renderer": "^17.0.2", "react-test-renderer": "^17.0.2",
"recast": "^0.23.9", "recast": "^0.23.9",
"regenerate": "^1.4.0", "regenerate": "^1.4.0",
@ -1839,6 +1843,7 @@
"sinon": "^7.4.2", "sinon": "^7.4.2",
"sort-package-json": "^1.53.1", "sort-package-json": "^1.53.1",
"source-map": "^0.7.4", "source-map": "^0.7.4",
"storybook": "^8.6.3",
"string-replace-loader": "^3.1.0", "string-replace-loader": "^3.1.0",
"style-loader": "^4.0.0", "style-loader": "^4.0.0",
"stylelint": "^14.16.1", "stylelint": "^14.16.1",

View file

@ -42,10 +42,25 @@
}, },
{ {
"groupName": "webpack", "groupName": "webpack",
"matchDepNames": ["webpack", "@types/webpack", "webpack-cli", "webpack-dev-server", "webpack-merge"], "matchDepNames": [
"reviewers": ["team:kibana-operations"], "webpack",
"matchBaseBranches": ["main"], "@types/webpack",
"labels": ["Team:Operations", "backport:all-open", "release_note:skip", "ci:build-webpack-bundle-analyzer"], "webpack-cli",
"webpack-dev-server",
"webpack-merge"
],
"reviewers": [
"team:kibana-operations"
],
"matchBaseBranches": [
"main"
],
"labels": [
"Team:Operations",
"backport:all-open",
"release_note:skip",
"ci:build-webpack-bundle-analyzer"
],
"minimumReleaseAge": "60 days", "minimumReleaseAge": "60 days",
"enabled": true "enabled": true
}, },
@ -1392,7 +1407,8 @@
"matchDepNames": [ "matchDepNames": [
"prettier", "prettier",
"eslint-plugin-prettier", "eslint-plugin-prettier",
"eslint-config-prettier" "eslint-config-prettier",
"@types/prettier"
], ],
"reviewers": [ "reviewers": [
"team:kibana-operations" "team:kibana-operations"
@ -3359,28 +3375,7 @@
"enabled": true "enabled": true
}, },
{ {
"groupName": "@storybook", "groupName": "storybook",
"reviewers": [
"team:kibana-operations"
],
"matchBaseBranches": [
"main"
],
"matchDepPatterns": [
"^@storybook"
],
"labels": [
"Team:Operations",
"release_note:skip",
"ci:build-storybooks",
"backport:skip"
],
"minimumReleaseAge": "7 days",
"allowedVersions": "<7.0",
"enabled": true
},
{
"groupName": "@storybook/testing-react",
"reviewers": [ "reviewers": [
"team:kibana-operations" "team:kibana-operations"
], ],
@ -3388,16 +3383,20 @@
"main" "main"
], ],
"matchDepNames": [ "matchDepNames": [
"@storybook/testing-react" "@pmmmwh/react-refresh-webpack-plugin",
"fix-esm",
"react-refresh"
],
"matchDepPatterns": [
"storybook"
], ],
"labels": [ "labels": [
"Team:Operations", "Team:Operations",
"release_note:skip", "release_note:skip",
"ci:build-storybooks", "ci:build-storybooks",
"backport:skip" "backport:prev-minor"
], ],
"minimumReleaseAge": "7 days", "minimumReleaseAge": "7 days",
"allowedVersions": "<2.0",
"enabled": true "enabled": true
}, },
{ {

View file

@ -71,6 +71,7 @@ export const LICENSE_ALLOWED = [
'Python-2.0', 'Python-2.0',
'(Apache-2.0 AND MIT)', '(Apache-2.0 AND MIT)',
'BlueOak-1.0.0', 'BlueOak-1.0.0',
'WTFPL OR CC0-1.0',
]; ];
// The following list only applies to licenses that // The following list only applies to licenses that

View file

@ -7,23 +7,17 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
// Please also add new aliases to .buildkite/scripts/steps/storybooks/build_and_upload.ts
//
// If you wish for your Storybook to be built and included in CI, also add your
// alias to .buildkite/scripts/steps/storybooks/build_and_upload.ts
export const storybookAliases = { export const storybookAliases = {
ai_assistant: 'x-pack/platform/packages/shared/kbn-ai-assistant/.storybook', ai_assistant: 'x-pack/platform/packages/shared/kbn-ai-assistant/.storybook',
apm: 'x-pack/solutions/observability/plugins/apm/.storybook', apm: 'x-pack/solutions/observability/plugins/apm/.storybook',
canvas: 'x-pack/platform/plugins/private/canvas/storybook', canvas: 'x-pack/platform/plugins/private/canvas/storybook',
cases: 'src/platform/packages/shared/kbn-cases-components/.storybook', cases: 'src/platform/packages/shared/kbn-cases-components/.storybook',
cell_actions: 'src/platform/packages/shared/kbn-cell-actions/.storybook', cell_actions: 'src/platform/packages/shared/kbn-cell-actions/.storybook',
chart_icons: 'src/platform/packages/shared/kbn-chart-icons/.storybook',
cloud_security_posture_graph: cloud_security_posture_graph:
'x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/.storybook', 'x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/.storybook',
cloud: 'src/platform/packages/shared/cloud/.storybook', cloud: 'src/platform/packages/shared/cloud/.storybook',
coloring: 'src/platform/packages/shared/kbn-coloring/.storybook', coloring: 'src/platform/packages/shared/kbn-coloring/.storybook',
language_documentation_popover:
'src/platform/packages/private/kbn-language-documentation/.storybook',
chart_icons: 'src/platform/packages/shared/kbn-chart-icons/.storybook',
content_management_examples: 'examples/content_management_examples/.storybook', content_management_examples: 'examples/content_management_examples/.storybook',
custom_icons: 'src/platform/packages/shared/kbn-custom-icons/.storybook', custom_icons: 'src/platform/packages/shared/kbn-custom-icons/.storybook',
custom_integrations: 'src/platform/plugins/shared/custom_integrations/storybook', custom_integrations: 'src/platform/plugins/shared/custom_integrations/storybook',
@ -31,8 +25,10 @@ export const storybookAliases = {
dashboard: 'src/platform/plugins/shared/dashboard/.storybook', dashboard: 'src/platform/plugins/shared/dashboard/.storybook',
data: 'src/platform/plugins/shared/data/.storybook', data: 'src/platform/plugins/shared/data/.storybook',
discover: 'src/platform/plugins/shared/discover/.storybook', discover: 'src/platform/plugins/shared/discover/.storybook',
esql_ast_inspector: 'examples/esql_ast_inspector/.storybook',
es_ui_shared: 'src/platform/plugins/shared/es_ui_shared/.storybook', es_ui_shared: 'src/platform/plugins/shared/es_ui_shared/.storybook',
esql_ast_inspector: 'examples/esql_ast_inspector/.storybook',
esql_editor: 'src/platform/packages/private/kbn-esql-editor/.storybook',
event_stacktrace: 'x-pack/platform/packages/shared/kbn-event-stacktrace/.storybook',
expandable_flyout: 'x-pack/solutions/security/packages/expandable-flyout/.storybook', expandable_flyout: 'x-pack/solutions/security/packages/expandable-flyout/.storybook',
expression_error: 'src/platform/plugins/shared/expression_error/.storybook', expression_error: 'src/platform/plugins/shared/expression_error/.storybook',
expression_image: 'src/platform/plugins/shared/expression_image/.storybook', expression_image: 'src/platform/plugins/shared/expression_image/.storybook',
@ -53,20 +49,22 @@ export const storybookAliases = {
inventory: 'x-pack/solutions/observability/plugins/inventory/.storybook', inventory: 'x-pack/solutions/observability/plugins/inventory/.storybook',
investigate: 'x-pack/solutions/observability/plugins/investigate_app/.storybook', investigate: 'x-pack/solutions/observability/plugins/investigate_app/.storybook',
kibana_react: 'src/platform/plugins/shared/kibana_react/.storybook', kibana_react: 'src/platform/plugins/shared/kibana_react/.storybook',
language_documentation_popover:
'src/platform/packages/private/kbn-language-documentation/.storybook',
lists: 'x-pack/solutions/security/plugins/lists/.storybook', lists: 'x-pack/solutions/security/plugins/lists/.storybook',
logs_explorer: 'x-pack/solutions/observability/plugins/logs_explorer/.storybook', logs_explorer: 'x-pack/solutions/observability/plugins/logs_explorer/.storybook',
management: 'src/platform/packages/shared/kbn-management/storybook/config', management: 'src/platform/packages/shared/kbn-management/storybook/config',
observability: 'x-pack/solutions/observability/plugins/observability/.storybook',
observability_ai_assistant:
'x-pack/platform/plugins/shared/observability_ai_assistant/.storybook',
observability_ai_assistant_app: observability_ai_assistant_app:
'x-pack/solutions/observability/plugins/observability_ai_assistant_app/.storybook', 'x-pack/solutions/observability/plugins/observability_ai_assistant_app/.storybook',
observability_ai_assistant:
'x-pack/platform/plugins/shared/observability_ai_assistant/.storybook',
observability_inventory: 'x-pack/solutions/observability/plugins/inventory/.storybook', observability_inventory: 'x-pack/solutions/observability/plugins/inventory/.storybook',
observability_shared: 'x-pack/solutions/observability/plugins/observability_shared/.storybook', observability_shared: 'x-pack/solutions/observability/plugins/observability_shared/.storybook',
observability_slo: 'x-pack/solutions/observability/plugins/slo/.storybook', observability_slo: 'x-pack/solutions/observability/plugins/slo/.storybook',
observability: 'x-pack/solutions/observability/plugins/observability/.storybook',
presentation: 'src/platform/plugins/shared/presentation_util/storybook', presentation: 'src/platform/plugins/shared/presentation_util/storybook',
profiling: 'x-pack/solutions/observability/plugins/profiling/.storybook',
random_sampling: 'x-pack/platform/packages/private/kbn-random-sampling/.storybook', random_sampling: 'x-pack/platform/packages/private/kbn-random-sampling/.storybook',
esql_editor: 'src/platform/packages/private/kbn-esql-editor/.storybook',
// Skipped, please check and fix https://github.com/elastic/kibana/issues/207227 // Skipped, please check and fix https://github.com/elastic/kibana/issues/207227
// security_solution: 'x-pack/solutions/security/plugins/security_solution/.storybook', // security_solution: 'x-pack/solutions/security/plugins/security_solution/.storybook',
// security_solution_packages: 'x-pack/solutions/security/packages/storybook/config', // security_solution_packages: 'x-pack/solutions/security/packages/storybook/config',
@ -76,6 +74,4 @@ export const storybookAliases = {
triggers_actions_ui: 'x-pack/platform/plugins/shared/triggers_actions_ui/.storybook', triggers_actions_ui: 'x-pack/platform/plugins/shared/triggers_actions_ui/.storybook',
ui_actions_enhanced: 'src/platform/plugins/shared/ui_actions_enhanced/.storybook', ui_actions_enhanced: 'src/platform/plugins/shared/ui_actions_enhanced/.storybook',
unified_search: 'src/platform/plugins/shared/unified_search/.storybook', unified_search: 'src/platform/plugins/shared/unified_search/.storybook',
profiling: 'x-pack/solutions/observability/plugins/profiling/.storybook',
event_stacktrace: 'x-pack/platform/packages/shared/kbn-event-stacktrace/.storybook',
}; };

View file

@ -41,7 +41,6 @@ run(
log.verbose('Loading Storybook:', configDir); log.verbose('Loading Storybook:', configDir);
// TODO: once storybook is upgraded into a newer version, --no-deprecation flag could be removed when invoking it through the package.json script
runStorybookCli({ configDir, name: alias }); runStorybookCli({ configDir, name: alias });
}, },
{ {

View file

@ -11,7 +11,6 @@ const defaultConfig = require('@kbn/storybook').defaultConfig;
module.exports = { module.exports = {
...defaultConfig, ...defaultConfig,
stories: ['../**/*.stories.+(tsx|mdx)'],
typescript: { typescript: {
reactDocgen: 'react-docgen-typescript', reactDocgen: 'react-docgen-typescript',
}, },

View file

@ -0,0 +1,20 @@
import { Canvas, Meta, Story, Controls } from '@storybook/blocks';
import * as EsqlEditorStories from './esql_editor.stories';
<Meta of={EsqlEditorStories} />
# Overview
The ESQLEditor component is a reusable component and can be used to support text based languages in your application (SQL, ESQL):
<Canvas of={EsqlEditorStories.ExpandedMode} />
When there are errors to the query the UI displays the errors to the editor:
<Canvas of={EsqlEditorStories.WithErrors} />
## Component props
The component exposes the following properties:
<Controls />../esql\_editor

View file

@ -1,74 +0,0 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs/blocks';
import { I18nProvider } from '@kbn/i18n-react';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { ESQLEditor } from '../esql_editor';
export const Template = (args) =>
<I18nProvider>
<KibanaContextProvider
services={{
settings: { client: { get: () => {} } },
uiSettings: { get: () => {} },
}}
>
<ESQLEditor {...args} />
</KibanaContextProvider>
</I18nProvider>;
<Meta
title="Text based languages editor"
component={ESQLEditor}
/>
# Overview
The ESQLEditor component is a reusable component and can be used to support text based languages in your application (SQL, ESQL):
<Canvas>
<Story
name='expanded mode'
args={
{
query: { esql: 'from dataview | keep field1, field2' },
'data-test-subj':'test-id'
}
}
argTypes={
{ onTextLangQueryChange: { action: 'changed' }, onTextLangQuerySubmit: { action: 'submitted' }}
}
>
{Template.bind({})}
</Story>
</Canvas>
When there are errors to the query the UI displays the errors to the editor:
<Canvas>
<Story
name='with errors'
args={
{
query: { esql: 'from dataview | keep field1, field2' },
'data-test-subj':'test-id',
errors: [
new Error(
'[essql] > Unexpected error from Elasticsearch: verification_exception - Found 1 problem line 1:16: Unknown column [field10]'
),
]
}
}
argTypes={
{ onTextLangQueryChange: { action: 'changed' }, onTextLangQuerySubmit: { action: 'submitted' }}
}
>
{Template.bind({})}
</Story>
</Canvas>
## Component props
The component exposes the following properties:
<ArgsTable story="expanded mode"/>../esql_editor

View file

@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import React from 'react';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import type { StoryObj } from '@storybook/react';
import { ESQLEditor } from '../esql_editor';
import type { ESQLEditorProps } from '../types';
const Template = (args: ESQLEditorProps) => (
<KibanaContextProvider
services={{
settings: { client: { get: () => {} } },
uiSettings: { get: () => {} },
data: { query: { timefilter: { timefilter: { getTime: () => {} } } } },
}}
>
<ESQLEditor {...args} />
</KibanaContextProvider>
);
export default {
title: 'Text based languages editor',
component: ESQLEditor,
};
export const ExpandedMode: StoryObj<typeof ESQLEditor> = {
render: Template,
name: 'expanded mode',
args: {
query: {
esql: 'from dataview | keep field1, field2',
},
},
argTypes: {
onTextLangQueryChange: {
action: 'changed',
},
onTextLangQuerySubmit: {
action: 'submitted',
},
},
};
export const WithErrors: StoryObj<typeof ESQLEditor> = {
render: Template,
name: 'with errors',
args: {
query: {
esql: 'from dataview | keep field1, field2',
},
dataTestSubj: 'test-id',
errors: [
new Error(
'[essql] > Unexpected error from Elasticsearch: verification_exception - Found 1 problem line 1:16: Unknown column [field10]'
),
],
},
argTypes: {
onTextLangQueryChange: {
action: 'changed',
},
onTextLangQuerySubmit: {
action: 'submitted',
},
},
};

View file

@ -8,7 +8,6 @@
*/ */
import React from 'react'; import React from 'react';
import { storiesOf } from '@storybook/react';
import { LanguageDocumentationPopover } from '../components/as_popover'; import { LanguageDocumentationPopover } from '../components/as_popover';
const sections = { const sections = {
@ -62,12 +61,20 @@ const sections = {
), ),
}; };
storiesOf('Language documentation popover', module).add('default', () => ( export default {
<LanguageDocumentationPopover title: 'Language documentation popover',
language="Test" };
sections={sections}
buttonProps={{ color: 'text' }} export const Default = {
isHelpMenuOpen={true} render: () => (
onHelpMenuVisibilityChange={() => {}} <LanguageDocumentationPopover
/> language="Test"
)); sections={sections}
buttonProps={{ color: 'text' }}
isHelpMenuOpen={true}
onHelpMenuVisibilityChange={() => {}}
/>
),
name: 'default',
};

View file

@ -27,12 +27,14 @@ export default {
}, },
}; };
export const Analytics = (params: AnalyticsNoDataPageStorybookParams) => { export const Analytics = {
return ( render: (params: AnalyticsNoDataPageStorybookParams) => {
<AnalyticsNoDataPageProvider {...mock.getProps(params)} {...mock.getServices(params)}> return (
<Component {...mock.getProps(params)} /> <AnalyticsNoDataPageProvider {...mock.getServices(params)}>
</AnalyticsNoDataPageProvider> <Component {...mock.getProps()} />
); </AnalyticsNoDataPageProvider>
}; );
},
Analytics.argTypes = mock.getArgumentTypes(); argTypes: mock.getArgumentTypes(),
};

View file

@ -38,18 +38,18 @@ export class StorybookMock extends AbstractStorybookMock<
propArguments = { propArguments = {
// requires hasESData to be toggled to true // requires hasESData to be toggled to true
useCustomOnTryESQL: { useCustomOnTryESQL: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
}; };
serviceArguments = { serviceArguments = {
kibanaGuideDocLink: { kibanaGuideDocLink: {
control: 'text', control: { control: 'text' },
defaultValue: 'Kibana guide', defaultValue: 'Kibana guide',
}, },
customBranding: { customBranding: {
hasCustomBranding$: { hasCustomBranding$: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
}, },
@ -70,10 +70,10 @@ export class StorybookMock extends AbstractStorybookMock<
}; };
} }
getProps(params: Params) { getProps(params?: Params) {
return { return {
onDataViewCreated: action('onDataViewCreated'), onDataViewCreated: action('onDataViewCreated'),
onTryESQL: params.useCustomOnTryESQL ? action('onTryESQL-from-props') : undefined, onTryESQL: params?.useCustomOnTryESQL ? action('onTryESQL-from-props') : undefined,
}; };
} }
} }

View file

@ -29,30 +29,34 @@ export default {
const mock = new KibanaNoDataPageStorybookMock(); const mock = new KibanaNoDataPageStorybookMock();
export const Kibana = (params: KibanaNoDataPageStorybookParams) => { export const Kibana = {
return ( render: (params: KibanaNoDataPageStorybookParams) => {
<KibanaNoDataPageProvider {...mock.getServices(params)}> return (
<Component {...mock.getProps(params)} /> <KibanaNoDataPageProvider {...mock.getServices(params)}>
</KibanaNoDataPageProvider> <Component {...mock.getProps(params)} />
); </KibanaNoDataPageProvider>
);
},
argTypes: mock.getArgumentTypes(),
}; };
Kibana.argTypes = mock.getArgumentTypes(); export const LoadingState = {
render: (params: KibanaNoDataPageStorybookParams) => {
// Simulate loading with a Promise that doesn't resolve.
const dataCheck = () => new Promise<boolean>((resolve, reject) => {});
export const LoadingState = (params: KibanaNoDataPageStorybookParams) => { const services = {
// Simulate loading with a Promise that doesn't resolve. ...mock.getServices(params),
const dataCheck = () => new Promise<boolean>((resolve, reject) => {}); hasESData: dataCheck,
hasUserDataView: dataCheck,
hasDataView: dataCheck,
};
const services = { return (
...mock.getServices(params), <KibanaNoDataPageProvider {...services}>
hasESData: dataCheck, <Component {...mock.getProps(params)} />
hasUserDataView: dataCheck, </KibanaNoDataPageProvider>
hasDataView: dataCheck, );
}; },
return (
<KibanaNoDataPageProvider {...services}>
<Component {...mock.getProps(params)} />
</KibanaNoDataPageProvider>
);
}; };

View file

@ -44,11 +44,11 @@ export class StorybookMock extends AbstractStorybookMock<
> { > {
propArguments = { propArguments = {
solution: { solution: {
control: 'text', control: { control: 'text' },
defaultValue: 'Observability', defaultValue: 'Observability',
}, },
logo: { logo: {
control: { type: 'radio' }, control: { control: 'radio' },
options: ['logoElastic', 'logoKibana', 'logoCloud', undefined], options: ['logoElastic', 'logoKibana', 'logoCloud', undefined],
defaultValue: undefined, defaultValue: undefined,
}, },
@ -56,11 +56,11 @@ export class StorybookMock extends AbstractStorybookMock<
serviceArguments = { serviceArguments = {
hasESData: { hasESData: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
hasUserDataView: { hasUserDataView: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
}; };

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { addons } from '@storybook/addons'; import { addons } from '@storybook/manager-api';
import { create } from '@storybook/theming'; import { create } from '@storybook/theming';
import { PANEL_ID as selectedPanel } from '@storybook/addon-actions'; import { PANEL_ID as selectedPanel } from '@storybook/addon-actions';

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { ComponentMeta } from '@storybook/react'; import { Meta } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import type { SampleDataSet } from '@kbn/home-sample-data-types'; import type { SampleDataSet } from '@kbn/home-sample-data-types';
@ -27,24 +27,26 @@ export default {
}, },
}, },
decorators: [(Story) => <div style={{ width: '433px', padding: '25px' }}>{Story()}</div>], decorators: [(Story) => <div style={{ width: '433px', padding: '25px' }}>{Story()}</div>],
} as ComponentMeta<typeof Component>; } as Meta<typeof Component>;
const { description, ...argTypes } = getStoryArgTypes(); const { description, ...argTypes } = getStoryArgTypes();
export const CardFooter = (params: Params) => { export const CardFooter = {
const { includeAppLinks, status, ...rest } = params; render: (params: Params) => {
const sampleDataSet: SampleDataSet = { const { includeAppLinks, status, ...rest } = params;
...mockDataSet, const sampleDataSet: SampleDataSet = {
...rest, ...mockDataSet,
status, ...rest,
appLinks: includeAppLinks ? mockDataSet.appLinks : [], status,
}; appLinks: includeAppLinks ? mockDataSet.appLinks : [],
};
return ( return (
<SampleDataCardProvider {...getStoryServices(params)}> <SampleDataCardProvider {...getStoryServices(params)}>
<Component sampleDataSet={sampleDataSet} onAction={action('onAction')} /> <Component sampleDataSet={sampleDataSet} onAction={action('onAction')} />
</SampleDataCardProvider> </SampleDataCardProvider>
); );
},
argTypes,
}; };
CardFooter.argTypes = argTypes;

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { ComponentMeta } from '@storybook/react'; import { Meta } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import type { SampleDataSet } from '@kbn/home-sample-data-types'; import type { SampleDataSet } from '@kbn/home-sample-data-types';
@ -28,23 +28,25 @@ export default {
}, },
}, },
decorators: [(Story) => <div style={{ width: '433px', padding: '25px' }}>{Story()}</div>], decorators: [(Story) => <div style={{ width: '433px', padding: '25px' }}>{Story()}</div>],
} as ComponentMeta<typeof SampleDataCard>; } as Meta<typeof SampleDataCard>;
const argTypes = getStoryArgTypes(); const argTypes = getStoryArgTypes();
export const Card = (params: Params) => { export const Card = {
const { includeAppLinks, ...rest } = params; render: (params: Params) => {
const sampleDataSet: SampleDataSet = { const { includeAppLinks, ...rest } = params;
...mockDataSet, const sampleDataSet: SampleDataSet = {
...rest, ...mockDataSet,
appLinks: includeAppLinks ? mockDataSet.appLinks : [], ...rest,
}; appLinks: includeAppLinks ? mockDataSet.appLinks : [],
};
return ( return (
<SampleDataCardProvider {...getStoryServices(params)}> <SampleDataCardProvider {...getStoryServices(params)}>
<SampleDataCard sampleDataSet={sampleDataSet} onStatusChange={action('onStatusChange')} /> <SampleDataCard sampleDataSet={sampleDataSet} onStatusChange={action('onStatusChange')} />
</SampleDataCardProvider> </SampleDataCardProvider>
); );
},
argTypes,
}; };
Card.argTypes = argTypes;

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { ComponentMeta } from '@storybook/react'; import { Meta } from '@storybook/react';
import { DemoEnvironmentPanel } from './demo_env_panel'; import { DemoEnvironmentPanel } from './demo_env_panel';
@ -23,6 +23,6 @@ export default {
}, },
}, },
decorators: [(Story) => <div style={{ width: 1200 }}>{Story()}</div>], decorators: [(Story) => <div style={{ width: 1200 }}>{Story()}</div>],
} as ComponentMeta<typeof DemoEnvironmentPanel>; } as Meta<typeof DemoEnvironmentPanel>;
export const DemoPanel = () => <DemoEnvironmentPanel demoUrl="https://google.com" />; export const DemoPanel = () => <DemoEnvironmentPanel demoUrl="https://google.com" />;

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { ComponentMeta } from '@storybook/react'; import { Meta } from '@storybook/react';
import { SampleDataTab } from './sample_data_tab'; import { SampleDataTab } from './sample_data_tab';
@ -25,12 +25,14 @@ export default {
}, },
}, },
decorators: [(Story) => <div style={{ width: 1200 }}>{Story()}</div>], decorators: [(Story) => <div style={{ width: 1200 }}>{Story()}</div>],
} as ComponentMeta<typeof SampleDataTab>; } as Meta<typeof SampleDataTab>;
export const TabContent = (params: Params) => ( export const TabContent = {
<SampleDataTabProvider {...getStoryServices(params)}> render: (params: Params) => (
<SampleDataTab /> <SampleDataTabProvider {...getStoryServices(params)}>
</SampleDataTabProvider> <SampleDataTab />
); </SampleDataTabProvider>
),
TabContent.argTypes = getStoryArgTypes(); argTypes: getStoryArgTypes(),
};

View file

@ -9,7 +9,7 @@
import React from 'react'; import React from 'react';
import { I18nProvider } from '@kbn/i18n-react'; import { I18nProvider } from '@kbn/i18n-react';
import { ComponentStory, ComponentMeta } from '@storybook/react'; import { StoryObj, Meta } from '@storybook/react';
import { CaseStatuses } from '../status/types'; import { CaseStatuses } from '../status/types';
import { Tooltip } from '../tooltip/tooltip'; import { Tooltip } from '../tooltip/tooltip';
@ -59,46 +59,72 @@ const Template = (args: CaseTooltipProps) => (
export default { export default {
title: 'CaseTooltip', title: 'CaseTooltip',
component: Template, component: Template,
} as ComponentMeta<typeof Template>; } as Meta<typeof Template>;
export const Default: ComponentStory<typeof Template> = Template.bind({}); export const Default: StoryObj<typeof Template> = {
Default.args = { ...tooltipProps }; render: Template,
args: { ...tooltipProps },
export const LoadingState: ComponentStory<typeof Template> = Template.bind({});
LoadingState.args = { ...tooltipProps, loading: true };
export const LongTitle: ComponentStory<typeof Template> = Template.bind({});
LongTitle.args = { ...tooltipProps, content: { ...tooltipContent, title: longTitle } };
export const LongDescription: ComponentStory<typeof Template> = Template.bind({});
LongDescription.args = {
...tooltipProps,
content: { ...tooltipContent, description: longDescription },
}; };
export const InProgressStatus: ComponentStory<typeof Template> = Template.bind({}); export const LoadingState: StoryObj<typeof Template> = {
InProgressStatus.args = { render: Template,
...tooltipProps, args: { ...tooltipProps, loading: true },
content: { ...tooltipContent, status: CaseStatuses['in-progress'] },
}; };
export const ClosedStatus: ComponentStory<typeof Template> = Template.bind({}); export const LongTitle: StoryObj<typeof Template> = {
ClosedStatus.args = { render: Template,
...tooltipProps, args: { ...tooltipProps, content: { ...tooltipContent, title: longTitle } },
content: { ...tooltipContent, status: CaseStatuses.closed },
}; };
export const NoUserInfo: ComponentStory<typeof Template> = Template.bind({}); export const LongDescription: StoryObj<typeof Template> = {
NoUserInfo.args = { ...tooltipProps, content: { ...tooltipContent, createdBy: {} } }; render: Template,
export const FullName: ComponentStory<typeof Template> = Template.bind({}); args: {
FullName.args = { ...tooltipProps,
...tooltipProps, content: { ...tooltipContent, description: longDescription },
content: { ...tooltipContent, createdBy: { fullName: 'Elastic User' } }, },
}; };
export const LongUserName: ComponentStory<typeof Template> = Template.bind({}); export const InProgressStatus: StoryObj<typeof Template> = {
LongUserName.args = { render: Template,
...tooltipProps,
content: { ...tooltipContent, createdBy: { fullName: 'LoremIpsumElasticUser WithALongSurname' } }, args: {
...tooltipProps,
content: { ...tooltipContent, status: CaseStatuses['in-progress'] },
},
};
export const ClosedStatus: StoryObj<typeof Template> = {
render: Template,
args: {
...tooltipProps,
content: { ...tooltipContent, status: CaseStatuses.closed },
},
};
export const NoUserInfo: StoryObj<typeof Template> = {
render: Template,
args: { ...tooltipProps, content: { ...tooltipContent, createdBy: {} } },
};
export const FullName: StoryObj<typeof Template> = {
render: Template,
args: {
...tooltipProps,
content: { ...tooltipContent, createdBy: { fullName: 'Elastic User' } },
},
};
export const LongUserName: StoryObj<typeof Template> = {
render: Template,
args: {
...tooltipProps,
content: {
...tooltipContent,
createdBy: { fullName: 'LoremIpsumElasticUser WithALongSurname' },
},
},
}; };

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import type { ComponentStory } from '@storybook/react'; import type { StoryFn } from '@storybook/react';
import type { FieldSpec } from '@kbn/data-views-plugin/common'; import type { FieldSpec } from '@kbn/data-views-plugin/common';
import { CellActionsProvider } from '../context/cell_actions_context'; import { CellActionsProvider } from '../context/cell_actions_context';
import { makeAction } from '../mocks/helpers'; import { makeAction } from '../mocks/helpers';
@ -50,33 +50,35 @@ export default {
], ],
}; };
const CellActionsTemplate: ComponentStory<React.FC<CellActionsProps>> = (args) => ( const CellActionsTemplate: StoryFn<React.FC<CellActionsProps>> = (args) => (
<CellActions {...args}>{'Field value'}</CellActions> <CellActions {...args}>{'Field value'}</CellActions>
); );
export const DefaultWithControls = CellActionsTemplate.bind({}); export const DefaultWithControls = {
render: CellActionsTemplate,
DefaultWithControls.argTypes = { argTypes: {
mode: { mode: {
options: [CellActionsMode.HOVER_DOWN, CellActionsMode.INLINE], options: [CellActionsMode.HOVER_DOWN, CellActionsMode.INLINE],
defaultValue: CellActionsMode.HOVER_DOWN, defaultValue: CellActionsMode.HOVER_DOWN,
control: { control: {
type: 'radio', type: 'radio',
},
}, },
}, },
};
DefaultWithControls.args = { args: {
showActionTooltips: true, showActionTooltips: true,
mode: CellActionsMode.INLINE, mode: CellActionsMode.INLINE,
triggerId: TRIGGER_ID, triggerId: TRIGGER_ID,
data: [ data: [
{ {
field: FIELD, field: FIELD,
value: '', value: '',
}, },
], ],
visibleCellActions: 3, visibleCellActions: 3,
},
}; };
export const CellActionInline = () => ( export const CellActionInline = () => (

View file

@ -9,7 +9,7 @@
import React, { FC, ComponentType } from 'react'; import React, { FC, ComponentType } from 'react';
import { EuiFlexItem, EuiFlexGroup, EuiEmptyPrompt, EuiForm, IconType } from '@elastic/eui'; import { EuiFlexItem, EuiFlexGroup, EuiEmptyPrompt, EuiForm, IconType } from '@elastic/eui';
import { ComponentStory } from '@storybook/react'; import type { StoryFn } from '@storybook/react';
import { import {
IconCircle, IconCircle,
@ -224,10 +224,12 @@ function RootComponent(props: RootComponentProps) {
); );
} }
const Template: ComponentStory<FC<RootComponentProps>> = (args) => <RootComponent {...args} />; const Template: StoryFn<FC<RootComponentProps>> = (args) => <RootComponent {...args} />;
export const Default = Template.bind({}); export const Default = {
render: Template,
Default.args = { args: {
icons: IconsArray, icons: IconsArray,
},
}; };

View file

@ -10,7 +10,7 @@
import React, { FC, useState } from 'react'; import React, { FC, useState } from 'react';
import { getKbnPalettes } from '@kbn/palettes'; import { getKbnPalettes } from '@kbn/palettes';
import { EuiFlyout, EuiForm, EuiPage, isColorDark } from '@elastic/eui'; import { EuiFlyout, EuiForm, EuiPage, isColorDark } from '@elastic/eui';
import { ComponentStory } from '@storybook/react'; import type { StoryFn } from '@storybook/react';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
import { CategoricalColorMapping, ColorMappingProps } from '../categorical_color_mapping'; import { CategoricalColorMapping, ColorMappingProps } from '../categorical_color_mapping';
import { DEFAULT_COLOR_MAPPING_CONFIG } from '../config/default_color_mapping'; import { DEFAULT_COLOR_MAPPING_CONFIG } from '../config/default_color_mapping';
@ -25,7 +25,7 @@ export default {
decorators: [(story: Function) => story()], decorators: [(story: Function) => story()],
}; };
const Template: ComponentStory<FC<ColorMappingProps>> = (args) => { const Template: StoryFn<FC<ColorMappingProps>> = (args) => {
const [updatedModel, setUpdateModel] = useState<ColorMapping.Config>( const [updatedModel, setUpdateModel] = useState<ColorMapping.Config>(
DEFAULT_COLOR_MAPPING_CONFIG DEFAULT_COLOR_MAPPING_CONFIG
); );
@ -76,50 +76,53 @@ const Template: ComponentStory<FC<ColorMappingProps>> = (args) => {
</EuiPage> </EuiPage>
); );
}; };
export const Default = Template.bind({});
Default.args = { export const Default = {
model: { render: Template,
...DEFAULT_COLOR_MAPPING_CONFIG,
paletteId: 'eui_amsterdam',
colorMode: { args: {
type: 'categorical', model: {
}, ...DEFAULT_COLOR_MAPPING_CONFIG,
specialAssignments: [ paletteId: 'eui_amsterdam',
{
rule: { colorMode: {
type: 'other', type: 'categorical',
},
color: {
type: 'loop',
},
touched: false,
}, },
], specialAssignments: [
assignments: [], {
}, rule: {
isDarkMode: false, type: 'other',
data: { },
type: 'categories', color: {
categories: [ type: 'loop',
'US', },
'Mexico', touched: false,
'Brasil', },
'Canada', ],
'Italy', assignments: [],
'Germany', },
'France', isDarkMode: false,
'Spain', data: {
'UK', type: 'categories',
'Portugal', categories: [
'Greece', 'US',
'Sweden', 'Mexico',
'Finland', 'Brasil',
], 'Canada',
}, 'Italy',
'Germany',
'France',
'Spain',
'UK',
'Portugal',
'Greece',
'Sweden',
'Finland',
],
},
specialTokens: new Map(), specialTokens: new Map(),
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
onModelUpdate: (model) => console.log(model), onModelUpdate: (model: any) => console.log(model),
},
}; };

View file

@ -7,10 +7,9 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import React, { FC, PropsWithChildren } from 'react'; import React from 'react';
import { EuiForm } from '@elastic/eui'; import { EuiForm } from '@elastic/eui';
import { ComponentStory } from '@storybook/react'; import { CustomizablePalette } from '../palette_configuration';
import { CustomizablePalette, CustomizablePaletteProps } from '../palette_configuration';
import { getPaletteRegistry } from './palettes'; import { getPaletteRegistry } from './palettes';
export default { export default {
@ -19,29 +18,25 @@ export default {
decorators: [(story: Function) => <EuiForm>{story()}</EuiForm>], decorators: [(story: Function) => <EuiForm>{story()}</EuiForm>],
}; };
const Template: ComponentStory<FC<PropsWithChildren<CustomizablePaletteProps>>> = (args) => ( export const Default = {
<CustomizablePalette {...args} /> args: {
); palettes: getPaletteRegistry(),
activePalette: {
export const Default = Template.bind({}); type: 'palette',
name: 'default',
Default.args = { params: {
palettes: getPaletteRegistry(), steps: 1,
activePalette: { maxSteps: 10,
type: 'palette', continuity: 'none',
name: 'default', },
params: {
steps: 1,
maxSteps: 10,
continuity: 'none',
}, },
dataBounds: {
min: 0,
max: 100,
},
showExtraActions: true,
showRangeTypeSelector: true,
disableSwitchingContinuity: false,
setPalette: () => {},
}, },
dataBounds: {
min: 0,
max: 100,
},
showExtraActions: true,
showRangeTypeSelector: true,
disableSwitchingContinuity: false,
setPalette: () => {},
}; };

View file

@ -8,7 +8,7 @@
*/ */
import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiImage, EuiToolTip } from '@elastic/eui'; import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiImage, EuiToolTip } from '@elastic/eui';
import type { Story } from '@storybook/react'; import type { StoryFn } from '@storybook/react';
import React from 'react'; import React from 'react';
import { AGENT_NAMES } from '@kbn/elastic-agent-utils'; import { AGENT_NAMES } from '@kbn/elastic-agent-utils';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
@ -20,7 +20,7 @@ export default {
component: AgentIcon, component: AgentIcon,
}; };
export const List: Story = () => { export const List: StoryFn = () => {
return ( return (
<EuiThemeProvider darkMode={false}> <EuiThemeProvider darkMode={false}>
<EuiFlexGroup gutterSize="l" wrap={true}> <EuiFlexGroup gutterSize="l" wrap={true}>

View file

@ -8,7 +8,7 @@
*/ */
import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import type { Story } from '@storybook/react'; import type { StoryFn } from '@storybook/react';
import React from 'react'; import React from 'react';
import { CloudProviderIcon } from '.'; import { CloudProviderIcon } from '.';
import { CloudProvider } from './get_cloud_provider_icon'; import { CloudProvider } from './get_cloud_provider_icon';
@ -20,7 +20,7 @@ export default {
const providers: CloudProvider[] = ['gcp', 'aws', 'azure', 'unknownProvider']; const providers: CloudProvider[] = ['gcp', 'aws', 'azure', 'unknownProvider'];
export const List: Story = () => { export const List: StoryFn = () => {
return ( return (
<EuiFlexGroup gutterSize="l" wrap={true}> <EuiFlexGroup gutterSize="l" wrap={true}>
{providers.map((cloudProvider) => { {providers.map((cloudProvider) => {

View file

@ -61,7 +61,7 @@ export const esHitsMockWithSort = esHitsMock.map((hit) => ({
})); }));
const baseDate = new Date('2024-01-1').getTime(); const baseDate = new Date('2024-01-1').getTime();
const dateInc = 100_000_000; const dateInc = 100000000;
const generateFieldValue = (field: DataViewField, index: number) => { const generateFieldValue = (field: DataViewField, index: number) => {
switch (field.type) { switch (field.type) {

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import type { Story } from '@storybook/react'; import type { StoryFn } from '@storybook/react';
import { mockGroupingProps } from './grouping.mock'; import { mockGroupingProps } from './grouping.mock';
import { Grouping } from './grouping'; import { Grouping } from './grouping';
import readme from '../../README.mdx'; import readme from '../../README.mdx';
@ -24,6 +24,6 @@ export default {
}, },
}; };
export const Empty: Story<void> = () => { export const Empty: StoryFn = () => {
return <Grouping {...mockGroupingProps} />; return <Grouping {...mockGroupingProps} />;
}; };

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import type { ComponentMeta, Story } from '@storybook/react'; import type { StoryFn, Meta } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
@ -29,7 +29,7 @@ export default {
default: 'ghost', default: 'ghost',
}, },
}, },
} as ComponentMeta<typeof Component>; } as Meta<typeof Component>;
/** /**
* Props for a {@link SettinggApplication} Storybook story. * Props for a {@link SettinggApplication} Storybook story.
@ -69,12 +69,12 @@ const getSettingsApplicationStory = ({ hasGlobalSettings }: StoryProps) => (
</SettingsApplicationProvider> </SettingsApplicationProvider>
); );
export const SettingsApplicationWithGlobalSettings: Story = () => export const SettingsApplicationWithGlobalSettings: StoryFn = () =>
getSettingsApplicationStory({ getSettingsApplicationStory({
hasGlobalSettings: true, hasGlobalSettings: true,
}); });
export const SettingsApplicationWithoutGlobal: Story = () => export const SettingsApplicationWithoutGlobal: StoryFn = () =>
getSettingsApplicationStory({ getSettingsApplicationStory({
hasGlobalSettings: false, hasGlobalSettings: false,
}); });

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import type { ComponentMeta, Story } from '@storybook/react'; import type { StoryObj, Meta } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { FieldCategories as Component } from '../categories'; import { FieldCategories as Component } from '../categories';
import { Params, useCategoryStory } from './use_category_story'; import { Params, useCategoryStory } from './use_category_story';
@ -38,9 +38,9 @@ export default {
default: 'ghost', default: 'ghost',
}, },
}, },
} as ComponentMeta<typeof Component>; } as Meta<typeof Component>;
export const Categories: Story<Params> = (params) => { const CategoriesComponent = (params: Params) => {
const { const {
onClearQuery, onClearQuery,
isSavingEnabled, isSavingEnabled,
@ -75,3 +75,7 @@ export const Categories: Story<Params> = (params) => {
</FieldCategoryProvider> </FieldCategoryProvider>
); );
}; };
export const Categories: StoryObj<Params> = {
render: (params) => <CategoriesComponent {...params} />,
};

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import type { ComponentMeta } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { getSettingsMock } from '@kbn/management-settings-utilities/mocks/settings.mock'; import { getSettingsMock } from '@kbn/management-settings-utilities/mocks/settings.mock';
@ -51,11 +51,11 @@ export default {
}, },
}, },
}, },
} as ComponentMeta<typeof Component>; } as Meta<typeof Component>;
type FieldCategoryParams = Pick<ComponentProps, 'category'> & Params; type FieldCategoryParams = Pick<ComponentProps, 'category'> & Params;
export const Category = ({ isFiltered, category, isSavingEnabled }: FieldCategoryParams) => { const CategoryComponent = ({ isFiltered, category, isSavingEnabled }: FieldCategoryParams) => {
const { onClearQuery, onFieldChange, unsavedChanges } = useCategoryStory({ const { onClearQuery, onFieldChange, unsavedChanges } = useCategoryStory({
isFiltered, isFiltered,
isSavingEnabled, isSavingEnabled,
@ -89,3 +89,7 @@ export const Category = ({ isFiltered, category, isSavingEnabled }: FieldCategor
</FieldCategoryProvider> </FieldCategoryProvider>
); );
}; };
export const Category: StoryObj<FieldCategoryParams> = {
render: (params) => <CategoryComponent {...params} />,
};

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { useArgs } from '@storybook/client-api'; import { useArgs } from '@storybook/preview-api';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { getSettingsMock } from '@kbn/management-settings-utilities/mocks/settings.mock'; import { getSettingsMock } from '@kbn/management-settings-utilities/mocks/settings.mock';

View file

@ -9,5 +9,7 @@
import { getInputStory, getStory } from './common'; import { getInputStory, getStory } from './common';
export default getStory('Array Input', 'An input with an array value.'); const Story = getStory('Array Input', 'An input with an array value.');
export const ArrayInput = getInputStory('array' as const); export const ArrayInput = getInputStory('array' as const);
export default { ...Story };

View file

@ -9,5 +9,9 @@
import { getInputStory, getStory } from './common'; import { getInputStory, getStory } from './common';
export default getStory('Boolean Input', 'An input with a boolean value.'); const Story = getStory('Boolean Input', 'An input with a boolean value.');
export const BooleanInput = getInputStory('boolean' as const); export const BooleanInput = getInputStory('boolean' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getInputStory, getStory } from './common'; import { getInputStory, getStory } from './common';
export default getStory('Color Input', 'An input with a color value.'); const Story = getStory('Color Input', 'An input with a color value.');
export const ColorInput = getInputStory('color' as const); export const ColorInput = getInputStory('color' as const);
export default {
...Story,
};

View file

@ -8,7 +8,7 @@
*/ */
import React, { useState } from 'react'; import React, { useState } from 'react';
import type { ComponentMeta } from '@storybook/react'; import type { Meta } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { EuiPanel } from '@elastic/eui'; import { EuiPanel } from '@elastic/eui';
@ -23,7 +23,7 @@ import {
import { getFieldDefinition } from '@kbn/management-settings-field-definition'; import { getFieldDefinition } from '@kbn/management-settings-field-definition';
import { getDefaultValue, getUserValue } from '@kbn/management-settings-utilities/storybook'; import { getDefaultValue, getUserValue } from '@kbn/management-settings-utilities/storybook';
import { FieldInputProvider } from '../services'; import { FieldInputProvider } from '../services';
import { FieldInput as Component, FieldInput } from '../field_input'; import { FieldInput } from '../field_input';
import { InputProps } from '../types'; import { InputProps } from '../types';
/** /**
@ -92,7 +92,7 @@ export const getStory = (title: string, description: string) =>
</FieldInputProvider> </FieldInputProvider>
), ),
], ],
} as ComponentMeta<typeof Component>); } as Meta<typeof FieldInput>);
/** /**
* Utility function for returning a {@link FieldInput} Storybook story. * Utility function for returning a {@link FieldInput} Storybook story.

View file

@ -9,5 +9,9 @@
import { getInputStory, getStory } from './common'; import { getInputStory, getStory } from './common';
export default getStory('Image Input', 'An input with an image value.'); const Story = getStory('Image Input', 'An input with an image value.');
export const ImageInput = getInputStory('image' as const); export const ImageInput = getInputStory('image' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getInputStory, getStory } from './common'; import { getInputStory, getStory } from './common';
export default getStory('JSON Input', 'An input with a JSON value.'); const Story = getStory('JSON Input', 'An input with a JSON value.');
export const JSONInput = getInputStory('json' as const); export const JSONInput = getInputStory('json' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getInputStory, getStory } from './common'; import { getInputStory, getStory } from './common';
export default getStory('Markdown Input', 'An input with a markdown value.'); const Story = getStory('Markdown Input', 'An input with a markdown value.');
export const MarkdownInput = getInputStory('markdown' as const); export const MarkdownInput = getInputStory('markdown' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getInputStory, getStory } from './common'; import { getInputStory, getStory } from './common';
export default getStory('Number Input', 'An input with a number value.'); const Story = getStory('Number Input', 'An input with a number value.');
export const NumberInput = getInputStory('number' as const); export const NumberInput = getInputStory('number' as const);
export default {
...Story,
};

View file

@ -31,7 +31,7 @@ const settingFields = {
options: ['option1', 'option2', 'option3'], options: ['option1', 'option2', 'option3'],
}; };
export default getStory('Select Input', 'An input with multiple values.'); const Story = getStory('Select Input', 'An input with multiple values.');
export const SelectInput = getInputStory('select' as const, { argTypes, settingFields }); export const SelectInput = getInputStory('select' as const, { argTypes, settingFields });
SelectInput.args = { SelectInput.args = {
@ -39,3 +39,7 @@ SelectInput.args = {
value: 'option1', value: 'option1',
userValue: 'option2', userValue: 'option2',
}; };
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getInputStory, getStory } from './common'; import { getInputStory, getStory } from './common';
export default getStory('String Input', 'An input with a string value.'); const Story = getStory('String Input', 'An input with a string value.');
export const StringInput = getInputStory('string' as const); export const StringInput = getInputStory('string' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getFieldRowStory, getStory } from './common'; import { getFieldRowStory, getStory } from './common';
export default getStory('Array Row', 'A setting with an array of values.'); const Story = getStory('Array Row', 'A setting with an array of values.');
export const ArrayRow = getFieldRowStory('array' as const); export const ArrayRow = getFieldRowStory('array' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getStory, getFieldRowStory } from './common'; import { getStory, getFieldRowStory } from './common';
export default getStory('Boolean Row', 'A setting with a boolean value.'); const Story = getStory('Boolean Row', 'A setting with a boolean value.');
export const BooleanRow = getFieldRowStory('boolean' as const); export const BooleanRow = getFieldRowStory('boolean' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getFieldRowStory, getStory } from './common'; import { getFieldRowStory, getStory } from './common';
export default getStory('Color Row', 'A setting with an base64 image value.'); const Story = getStory('Color Row', 'A setting with an base64 image value.');
export const ColorRow = getFieldRowStory('color' as const); export const ColorRow = getFieldRowStory('color' as const);
export default {
...Story,
};

View file

@ -8,7 +8,7 @@
*/ */
import React, { useState } from 'react'; import React, { useState } from 'react';
import type { ComponentMeta } from '@storybook/react'; import type { Meta } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { EuiPanel } from '@elastic/eui'; import { EuiPanel } from '@elastic/eui';
import { SettingType, UnsavedFieldChange } from '@kbn/management-settings-types'; import { SettingType, UnsavedFieldChange } from '@kbn/management-settings-types';
@ -93,7 +93,7 @@ export const getStory = (
</FieldRowProvider> </FieldRowProvider>
), ),
], ],
} as ComponentMeta<typeof Component>); } as Meta<typeof Component>);
/** /**
* Default argument values for a {@link FieldInput} Storybook story. * Default argument values for a {@link FieldInput} Storybook story.

View file

@ -9,5 +9,9 @@
import { getFieldRowStory, getStory } from './common'; import { getFieldRowStory, getStory } from './common';
export default getStory('Image Row', 'A setting with an base64 image value.'); const Story = getStory('Image Row', 'A setting with an base64 image value.');
export const ImageRow = getFieldRowStory('image' as const); export const ImageRow = getFieldRowStory('image' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getFieldRowStory, getStory } from './common'; import { getFieldRowStory, getStory } from './common';
export default getStory('JSON Row', 'A setting with a JSON value.'); const Story = getStory('JSON Row', 'A setting with a JSON value.');
export const JSONRow = getFieldRowStory('json' as const); export const JSONRow = getFieldRowStory('json' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getFieldRowStory, getStory } from './common'; import { getFieldRowStory, getStory } from './common';
export default getStory('Markdown Row', 'A setting with a Markdown value.'); const Story = getStory('Markdown Row', 'A setting with a Markdown value.');
export const MarkdownRow = getFieldRowStory('markdown' as const); export const MarkdownRow = getFieldRowStory('markdown' as const);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getFieldRowStory, getStory } from './common'; import { getFieldRowStory, getStory } from './common';
export default getStory('Number Row', 'A setting with a numeric value.'); const Story = getStory('Number Row', 'A setting with a numeric value.');
export const NumberRow = getFieldRowStory('number' as const); export const NumberRow = getFieldRowStory('number' as const);
export default {
...Story,
};

View file

@ -24,5 +24,9 @@ const settingFields = {
options: ['option1', 'option2', 'option3'], options: ['option1', 'option2', 'option3'],
}; };
export default getStory('Select Row', 'A setting with a boolean value.', argTypes); const Story = getStory('Select Row', 'A setting with a boolean value.', argTypes);
export const SelectRow = getFieldRowStory('select' as const, settingFields); export const SelectRow = getFieldRowStory('select' as const, settingFields);
export default {
...Story,
};

View file

@ -9,5 +9,9 @@
import { getFieldRowStory, getStory } from './common'; import { getFieldRowStory, getStory } from './common';
export default getStory('String Row', 'A setting with a string value.'); const Story = getStory('String Row', 'A setting with a string value.');
export const StringRow = getFieldRowStory('string' as const); export const StringRow = getFieldRowStory('string' as const);
export default {
...Story,
};

View file

@ -9,7 +9,7 @@
import React from 'react'; import React from 'react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { ComponentMeta } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { FieldDefinition } from '@kbn/management-settings-types'; import { FieldDefinition } from '@kbn/management-settings-types';
import { getFieldDefinitions } from '@kbn/management-settings-field-definition'; import { getFieldDefinitions } from '@kbn/management-settings-field-definition';
import { getSettingsMock } from '@kbn/management-settings-utilities/mocks/settings.mock'; import { getSettingsMock } from '@kbn/management-settings-utilities/mocks/settings.mock';
@ -20,7 +20,7 @@ import { Form as Component } from '../form';
import { FormProvider } from '../services'; import { FormProvider } from '../services';
export default { export default {
title: `Settings/Form/Form`, title: 'Settings/Form/Form',
description: 'A form with field rows', description: 'A form with field rows',
argTypes: { argTypes: {
isSavingEnabled: { isSavingEnabled: {
@ -57,7 +57,7 @@ export default {
default: 'ghost', default: 'ghost',
}, },
}, },
} as ComponentMeta<typeof Component>; } as Meta<typeof Component>;
interface FormStoryProps { interface FormStoryProps {
/** True if saving settings is enabled, false otherwise. */ /** True if saving settings is enabled, false otherwise. */
@ -66,30 +66,34 @@ interface FormStoryProps {
requirePageReload: boolean; requirePageReload: boolean;
} }
export const Form = ({ isSavingEnabled, requirePageReload }: FormStoryProps) => { export const Form: StoryObj<FormStoryProps> = {
const fields: FieldDefinition[] = getFieldDefinitions( render: ({ isSavingEnabled, requirePageReload }) => {
getSettingsMock(requirePageReload), const fields: FieldDefinition[] = getFieldDefinitions(
uiSettingsClientMock getSettingsMock(requirePageReload),
); uiSettingsClientMock
);
const categorizedFields = categorizeFields(fields); const categorizedFields = categorizeFields(fields);
const categoryCounts = Object.keys(categorizedFields).reduce( const categoryCounts = Object.keys(categorizedFields).reduce(
(acc, category) => ({ (acc, category) => ({
...acc, ...acc,
[category]: categorizedFields[category].count, [category]: categorizedFields[category].count,
}), }),
{} {}
); );
const onClearQuery = () => {}; const onClearQuery = () => {};
return ( return (
<Component {...{ fields, isSavingEnabled, categoryCounts, onClearQuery, scope: 'namespace' }} /> <Component
); {...{ fields, isSavingEnabled, categoryCounts, onClearQuery, scope: 'namespace' }}
}; />
);
Form.args = { },
isSavingEnabled: true,
requirePageReload: false, args: {
isSavingEnabled: true,
requirePageReload: false,
},
}; };

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { addons } from '@storybook/addons'; import { addons } from '@storybook/manager-api';
import { create } from '@storybook/theming'; import { create } from '@storybook/theming';
import { PANEL_ID as selectedPanel } from '@storybook/addon-actions'; import { PANEL_ID as selectedPanel } from '@storybook/addon-actions';

View file

@ -7,13 +7,8 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { import { defaultConfig, StorybookConfig } from './src/lib/default_config';
defaultConfig, export { defaultConfig };
defaultConfigWebFinal,
mergeWebpackFinal,
StorybookConfig,
} from './src/lib/default_config';
export { defaultConfig, defaultConfigWebFinal, mergeWebpackFinal };
export type { StorybookConfig }; export type { StorybookConfig };
export { runStorybookCli } from './src/lib/run_storybook_cli'; export { runStorybookCli } from './src/lib/run_storybook_cli';
export { default as WebpackConfig } from './src/webpack.config'; export { default as WebpackConfig } from './src/webpack.config';

View file

@ -16,7 +16,7 @@ module.exports = {
webpackFinal: (config) => { webpackFinal: (config) => {
return webpackConfig({ config }); return webpackConfig({ config });
}, },
config: (entry) => { previewAnnotations: (entry) => {
return [...entry, require.resolve('./src/lib/decorators')]; return [...entry, require.resolve('./src/lib/preview')];
}, },
}; };

View file

@ -10,7 +10,7 @@
import { of, Subject } from 'rxjs'; import { of, Subject } from 'rxjs';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import type { DecoratorFn } from '@storybook/react'; import type { Decorator } from '@storybook/react';
import { I18nProvider } from '@kbn/i18n-react'; import { I18nProvider } from '@kbn/i18n-react';
import 'core_styles'; import 'core_styles';
@ -40,7 +40,7 @@ const analytics: AnalyticsServiceStart = {
* Storybook decorator using the `KibanaContextProvider`. Uses the value from * Storybook decorator using the `KibanaContextProvider`. Uses the value from
* `globals` provided by the Storybook theme switcher to set the `colorMode`. * `globals` provided by the Storybook theme switcher to set the `colorMode`.
*/ */
const KibanaContextDecorator: DecoratorFn = (storyFn, { globals }) => { const KibanaContextDecorator: Decorator = (storyFn, { globals }) => {
// TODO: Add a switcher to see components in other locales or pseudo locale // TODO: Add a switcher to see components in other locales or pseudo locale
i18n.init({ locale: 'en', messages: {} }); i18n.init({ locale: 'en', messages: {} });
const { darkMode, name } = getKibanaTheme(globals.euiTheme); const { darkMode, name } = getKibanaTheme(globals.euiTheme);

View file

@ -9,18 +9,28 @@
import * as path from 'path'; import * as path from 'path';
import fs from 'fs'; import fs from 'fs';
import type { StorybookConfig } from '@storybook/core-common'; import type { StorybookConfig as BaseStorybookConfig } from '@storybook/react-webpack5';
import webpack, { Configuration } from 'webpack'; import type { TypescriptOptions } from '@storybook/preset-react-webpack';
import { merge as webpackMerge } from 'webpack-merge'; import webpack from 'webpack';
import { resolve } from 'path';
import UiSharedDepsNpm from '@kbn/ui-shared-deps-npm';
import * as UiSharedDepsSrc from '@kbn/ui-shared-deps-src';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import { REPO_ROOT } from './constants'; import { REPO_ROOT } from './constants';
import { default as WebpackConfig } from '../webpack.config'; import { default as WebpackConfig } from '../webpack.config';
const MOCKS_DIRECTORY = '__storybook_mocks__'; const MOCKS_DIRECTORY = '__storybook_mocks__';
const EXTENSIONS = ['.ts', '.js']; const EXTENSIONS = ['.ts', '.js'];
export type { StorybookConfig }; /*
* false is a valid option for typescript.reactDocgen,
* but it is not in the type definition
*/
interface StorybookConfig extends BaseStorybookConfig {
typescript: Partial<TypescriptOptions>;
}
const toPath = (_path: string) => path.join(REPO_ROOT, _path); export type { StorybookConfig };
// This ignore pattern excludes all of node_modules EXCEPT for `@kbn`. This allows for // This ignore pattern excludes all of node_modules EXCEPT for `@kbn`. This allows for
// changes to packages to cause a refresh in Storybook. // changes to packages to cause a refresh in Storybook.
@ -32,31 +42,101 @@ const IGNORE_GLOBS = [
]; ];
export const defaultConfig: StorybookConfig = { export const defaultConfig: StorybookConfig = {
addons: ['@kbn/storybook/preset', '@storybook/addon-a11y', '@storybook/addon-essentials'], addons: [
core: { '@kbn/storybook/preset',
builder: 'webpack5', '@storybook/addon-a11y',
'@storybook/addon-webpack5-compiler-babel',
// https://storybook.js.org/docs/essentials
'@storybook/addon-essentials',
'@storybook/addon-jest',
{
/**
* This addon replaces rules in the default SB webpack config
* to avoid duplicate rule issues caused by directly using the rules
* in the custom webpack config.
*/
name: '@storybook/addon-styling-webpack',
options: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.scss$/,
exclude: /\.module.(s(a|c)ss)$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: { importLoaders: 2 } },
{
loader: 'postcss-loader',
options: {
postcssOptions: {
config: require.resolve('@kbn/optimizer/postcss.config'),
},
},
},
{
loader: 'sass-loader',
options: {
additionalData(content: string, loaderContext: any) {
const req = JSON.stringify(
loaderContext.utils.contextify(
loaderContext.context || loaderContext.rootContext,
resolve(REPO_ROOT, 'src/core/public/styles/core_app/_globals_v8light.scss')
)
);
return `@import ${req};\n${content}`;
},
implementation: require('sass-embedded'),
sassOptions: {
includePaths: [resolve(REPO_ROOT, 'node_modules')],
quietDeps: true,
},
},
},
],
},
],
},
},
],
stories: ['../**/*.stories.tsx', '../**/*.mdx'],
framework: {
name: '@storybook/react-webpack5',
options: {},
}, },
stories: ['../**/*.stories.tsx', '../**/*.stories.mdx'],
typescript: { typescript: {
reactDocgen: false, reactDocgen: false,
}, },
features: { core: {
postcss: false, disableTelemetry: true,
}, },
// @ts-expect-error StorybookConfig type is incomplete async babel(config: any, options: any) {
// https://storybook.js.org/docs/react/configure/babel#custom-configuration if (!config?.presets) {
babel: async (options) => { config.presets = [];
options.presets.push([ }
require.resolve('@emotion/babel-preset-css-prop'),
config.presets.push(
require.resolve('@kbn/babel-preset/common_preset'),
[
require.resolve('@emotion/babel-preset-css-prop'),
{
// There's an issue where emotion classnames may be duplicated,
// (e.g. `[hash]-[filename]--[local]_[filename]--[local]`)
// https://github.com/emotion-js/emotion/issues/2417
autoLabel: 'always',
labelFormat: '[filename]--[local]',
},
],
{ {
// There's an issue where emotion classnames may be duplicated, plugins: [
// (e.g. `[hash]-[filename]--[local]_[filename]--[local]`) process.env.NODE_ENV !== 'production' && require.resolve('react-refresh/babel'),
// https://github.com/emotion-js/emotion/issues/2417 ].filter(Boolean),
autoLabel: 'always', }
labelFormat: '[filename]--[local]', );
},
]); return config;
return options;
}, },
webpackFinal: (config, options) => { webpackFinal: (config, options) => {
if (process.env.CI) { if (process.env.CI) {
@ -64,6 +144,9 @@ export const defaultConfig: StorybookConfig = {
config.cache = true; config.cache = true;
} }
// required for react refresh
config.target = 'web';
// This will go over every component which is imported and check its import statements. // This will go over every component which is imported and check its import statements.
// For every import which starts with ./ it will do a check to see if a file with the same name // For every import which starts with ./ it will do a check to see if a file with the same name
// exists in the __storybook_mocks__ folder. If it does, use that import instead. // exists in the __storybook_mocks__ folder. If it does, use that import instead.
@ -106,6 +189,14 @@ export const defaultConfig: StorybookConfig = {
}) })
); );
if (process.env.NODE_ENV !== 'production') {
config.plugins?.push(
new ReactRefreshWebpackPlugin({
overlay: false,
})
);
}
config.resolve = { config.resolve = {
...config.resolve, ...config.resolve,
fallback: { fallback: {
@ -113,43 +204,40 @@ export const defaultConfig: StorybookConfig = {
fs: false, fs: false,
}, },
}; };
config.watch = true;
config.watchOptions = { config.watchOptions = {
...config.watchOptions, ...config.watchOptions,
ignored: IGNORE_GLOBS, ignored: IGNORE_GLOBS,
}; };
// Remove when @storybook has moved to @emotion v11
// https://github.com/storybookjs/storybook/issues/13145
const emotion11CompatibleConfig = {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve?.alias,
'@emotion/core': toPath('node_modules/@emotion/react'),
'@emotion/styled': toPath('node_modules/@emotion/styled'),
'emotion-theming': toPath('node_modules/@emotion/react'),
},
},
};
return emotion11CompatibleConfig;
},
};
// defaultConfigWebFinal and mergeWebpackFinal have been moved here because webpackFinal usage in
// storybook main.ts somehow is causing issues with newly added dependency of ts-node most likely
// an issue with storybook typescript setup see this issue for more details
// https://github.com/storybookjs/storybook/issues/9610
export const defaultConfigWebFinal: StorybookConfig = {
...defaultConfig,
webpackFinal: (config: Configuration) => {
return WebpackConfig({ config }); return WebpackConfig({ config });
}, },
}; previewHead: (head) => `
${head}
<meta name="eui-global" />
<meta name="emotion" />
<script>
window.__kbnPublicPath__ = { 'kbn-ui-shared-deps-npm': '', 'kbn-ui-shared-deps-src': '' };
window.__kbnHardenPrototypes__ = false;
</script>
<script src="kbn-ui-shared-deps-npm.dll.js"></script>
<script src="kbn-ui-shared-deps-src.js"></script>
<link href="kbn-ui-shared-deps-src.css" rel="stylesheet" />
export const mergeWebpackFinal = (extraConfig: Configuration) => { <link rel="preconnect" href="https://fonts.googleapis.com">
return { webpackFinal: (config: Configuration) => webpackMerge(config, extraConfig) }; <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300..700&family=Roboto+Mono:ital,wght@0,400..700;1,400..700&display=swap"
rel="stylesheet">
<meta name="eui-utilities" />
`,
staticDirs: [
UiSharedDepsNpm.distDir,
UiSharedDepsSrc.distDir,
{
from: `${REPO_ROOT}/src/platform/plugins/shared/kibana_react/public/assets`,
to: 'plugins/kibanaReact/assets',
},
],
}; };

View file

@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import type { Preview } from '@storybook/react';
import * as jest from 'jest-mock';
import { decorators } from './decorators';
// @ts-expect-error
window.jest = jest;
const preview: Preview = {
decorators,
initialGlobals: { euiTheme: 'v8.light' },
};
// eslint-disable-next-line import/no-default-export
export default preview;

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { addons } from '@storybook/addons'; import { addons } from '@storybook/manager-api';
import { create } from '@storybook/theming'; import { create } from '@storybook/theming';
import { registerThemeSwitcherAddon } from './register_theme_switcher_addon'; import { registerThemeSwitcherAddon } from './register_theme_switcher_addon';

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { addons, types } from '@storybook/addons'; import { addons, types } from '@storybook/manager-api';
import { ThemeSwitcher } from './theme_switcher'; import { ThemeSwitcher } from './theme_switcher';
export const THEME_SWITCHER_ADDON_ID = 'kibana/eui-theme-switcher'; export const THEME_SWITCHER_ADDON_ID = 'kibana/eui-theme-switcher';
@ -16,7 +16,7 @@ export function registerThemeSwitcherAddon() {
addons.register(THEME_SWITCHER_ADDON_ID, (api) => { addons.register(THEME_SWITCHER_ADDON_ID, (api) => {
const channel = api.getChannel(); const channel = api.getChannel();
channel.on('globalsUpdated', ({ globals }) => { channel?.on('globalsUpdated', ({ globals }) => {
const previewFrame = document.getElementById( const previewFrame = document.getElementById(
'storybook-preview-iframe' 'storybook-preview-iframe'
) as HTMLIFrameElement | null; ) as HTMLIFrameElement | null;

View file

@ -7,17 +7,15 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
/* eslint-disable @typescript-eslint/no-var-requires */
import { join } from 'path'; import { join } from 'path';
import { logger } from '@storybook/node-logger'; import { build } from '@storybook/core-server';
import buildStandalone from '@storybook/react/standalone'; import type { CLIOptions, BuilderOptions, LoadOptions } from '@storybook/types';
import { Flags, run } from '@kbn/dev-cli-runner'; import { Flags, run } from '@kbn/dev-cli-runner';
import UiSharedDepsNpm from '@kbn/ui-shared-deps-npm';
import * as UiSharedDepsSrc from '@kbn/ui-shared-deps-src';
// @ts-expect-error internal dep of storybook
import interpret from 'interpret'; // eslint-disable-line import/no-extraneous-dependencies
import * as constants from './constants'; import * as constants from './constants';
type StorybookCliOptions = CLIOptions & BuilderOptions & LoadOptions & { mode: 'dev' | 'static' };
// Convert the flags to a Storybook loglevel // Convert the flags to a Storybook loglevel
function getLogLevelFromFlags(flags: Flags) { function getLogLevelFromFlags(flags: Flags) {
if (flags.debug) { if (flags.debug) {
@ -40,29 +38,29 @@ export function runStorybookCli({ configDir, name }: { configDir: string; name:
async ({ flags, log }) => { async ({ flags, log }) => {
log.debug('Global config:\n', constants); log.debug('Global config:\n', constants);
const staticDir = [ const config: StorybookCliOptions = {
UiSharedDepsNpm.distDir,
UiSharedDepsSrc.distDir,
'src/platform/plugins/shared/kibana_react/public/assets:plugins/kibanaReact/assets',
];
const config: Record<string, any> = {
configDir, configDir,
mode: flags.site ? 'static' : 'dev', mode: flags.site ? 'static' : 'dev',
port: 9001, port: 9001,
staticDir, loglevel: getLogLevelFromFlags(flags),
}; };
if (flags.site) { if (flags.site) {
config.outputDir = join(constants.ASSET_DIR, name); config.outputDir = join(constants.ASSET_DIR, name);
process.env.NODE_ENV = 'production';
} else {
// required for react refresh
process.env.NODE_ENV = 'development';
} }
logger.setLevel(getLogLevelFromFlags(flags)); try {
// Some transitive deps of addon-docs are ESM and not loading properly
// force storybook to use our transpilation rather than ts-node or anything else // See: https://github.com/storybookjs/storybook/issues/29467
interpret.extensions['.ts'] = [require.resolve('@kbn/babel-register/install')]; require('fix-esm').require('react-docgen');
interpret.extensions['.tsx'] = [require.resolve('@kbn/babel-register/install')]; await build(config);
interpret.extensions['.jsx'] = [require.resolve('@kbn/babel-register/install')]; } finally {
require('fix-esm').unregister();
await buildStandalone(config); }
// Line is only reached when building the static version // Line is only reached when building the static version
if (flags.site) process.exit(); if (flags.site) process.exit();

View file

@ -8,8 +8,9 @@
*/ */
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect } from 'react';
import { Icons, IconButton, TooltipLinkList, WithTooltip } from '@storybook/components'; import { IconButton, TooltipLinkList, WithTooltip } from '@storybook/components';
import { useGlobals } from '@storybook/api'; import { useGlobals } from '@storybook/manager-api';
import { HeartIcon, HeartHollowIcon } from '@storybook/icons';
import { DEFAULT_THEME, THEMES, THEME_TITLES } from './themes'; import { DEFAULT_THEME, THEMES, THEME_TITLES } from './themes';
@ -37,7 +38,6 @@ export function ThemeSwitcher() {
<WithTooltip <WithTooltip
placement="top" placement="top"
trigger="click" trigger="click"
closeOnClick
tooltip={({ onHide }) => ( tooltip={({ onHide }) => (
<ThemeSwitcherTooltip <ThemeSwitcherTooltip
onHide={onHide} onHide={onHide}
@ -46,9 +46,8 @@ export function ThemeSwitcher() {
/> />
)} )}
> >
{/* @ts-ignore Remove when @storybook has moved to @emotion v11 */}
<IconButton key="eui-theme" title="Change the EUI theme"> <IconButton key="eui-theme" title="Change the EUI theme">
<Icons icon={selectedTheme?.includes('dark') ? 'heart' : 'hearthollow'} /> {selectedTheme?.includes('dark') ? <HeartIcon /> : <HeartHollowIcon />}
</IconButton> </IconButton>
</WithTooltip> </WithTooltip>
); );
@ -78,6 +77,6 @@ const ThemeSwitcherTooltip = React.memo(
}) })
); );
return <TooltipLinkList links={links} />; return <TooltipLinkList links={links.flat()} />;
} }
); );

View file

@ -10,64 +10,13 @@
/* eslint-disable import/no-default-export */ /* eslint-disable import/no-default-export */
import { externals } from '@kbn/ui-shared-deps-src'; import { externals } from '@kbn/ui-shared-deps-src';
import { resolve } from 'path'; import { resolve } from 'path';
import webpack, { Configuration } from 'webpack'; import { Configuration } from 'webpack';
import { merge as webpackMerge } from 'webpack-merge'; import { merge as webpackMerge } from 'webpack-merge';
import { NodeLibsBrowserPlugin } from '@kbn/node-libs-browser-webpack-plugin'; import { NodeLibsBrowserPlugin } from '@kbn/node-libs-browser-webpack-plugin';
import { REPO_ROOT } from './lib/constants'; import { REPO_ROOT } from './lib/constants';
import { IgnoreNotFoundExportPlugin } from './ignore_not_found_export_plugin'; import { IgnoreNotFoundExportPlugin } from './ignore_not_found_export_plugin';
import 'webpack-dev-server'; // Extends webpack configuration with `devServer` property import 'webpack-dev-server'; // Extends webpack configuration with `devServer` property
type Preset = string | [string, Record<string, unknown>] | Record<string, unknown>;
function isProgressPlugin(plugin: any) {
return 'handler' in plugin && plugin.showActiveModules && plugin.showModules;
}
function isHtmlPlugin(plugin: any): plugin is { options: { template: string } } {
return !!(typeof plugin.options?.template === 'string');
}
interface BabelLoaderRule extends webpack.RuleSetRule {
use: Array<{
loader: 'babel-loader';
[key: string]: unknown;
}>;
}
function isBabelLoaderRule(rule: webpack.RuleSetRule): rule is BabelLoaderRule {
return !!(
rule.use &&
Array.isArray(rule.use) &&
rule.use.some(
(l) =>
typeof l === 'object' && typeof l?.loader === 'string' && l?.loader.includes('babel-loader')
)
);
}
function getPresetPath(preset: Preset) {
if (typeof preset === 'string') return preset;
if (Array.isArray(preset)) return preset[0];
return undefined;
}
function getTsPreset(preset: Preset) {
if (getPresetPath(preset)?.includes('preset-typescript')) {
if (typeof preset === 'string') return [preset, {}];
if (Array.isArray(preset)) return preset;
throw new Error('unsupported preset-typescript format');
}
}
function isDesiredPreset(preset: Preset) {
return !getPresetPath(preset)?.includes('preset-flow');
}
// Extend the Storybook Webpack config with some customizations
/**
* @returns {import('webpack').Configuration}
*/
export default ({ config: storybookConfig }: { config: Configuration }) => { export default ({ config: storybookConfig }: { config: Configuration }) => {
const config: Configuration = { const config: Configuration = {
devServer: { devServer: {
@ -97,41 +46,6 @@ export default ({ config: storybookConfig }: { config: Configuration }) => {
loader: require.resolve('@kbn/peggy-loader'), loader: require.resolve('@kbn/peggy-loader'),
}, },
}, },
{
test: /\.scss$/,
exclude: /\.module.(s(a|c)ss)$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: { importLoaders: 2 } },
{
loader: 'postcss-loader',
options: {
postcssOptions: {
config: require.resolve('@kbn/optimizer/postcss.config'),
},
},
},
{
loader: 'sass-loader',
options: {
additionalData(content: string, loaderContext: any) {
const req = JSON.stringify(
loaderContext.utils.contextify(
loaderContext.context || loaderContext.rootContext,
resolve(REPO_ROOT, 'src/core/public/styles/core_app/_globals_v8light.scss')
)
);
return `@import ${req};\n${content}`;
},
implementation: require('sass-embedded'),
sassOptions: {
includePaths: [resolve(REPO_ROOT, 'node_modules')],
quietDeps: true,
},
},
},
],
},
], ],
}, },
plugins: [new NodeLibsBrowserPlugin(), new IgnoreNotFoundExportPlugin()], plugins: [new NodeLibsBrowserPlugin(), new IgnoreNotFoundExportPlugin()],
@ -147,74 +61,5 @@ export default ({ config: storybookConfig }: { config: Configuration }) => {
stats: 'errors-only', stats: 'errors-only',
}; };
// Override storybookConfig mainFields instead of merging with config return webpackMerge(storybookConfig, config);
delete storybookConfig.resolve?.mainFields;
const updatedModuleRules: webpack.RuleSetRule[] = [];
// clone and modify the module.rules config provided by storybook so that the default babel plugins run after the typescript preset
for (const originalRule of storybookConfig.module?.rules ?? []) {
const rule = typeof originalRule !== 'string' ? { ...originalRule } : {};
updatedModuleRules.push(rule);
if (isBabelLoaderRule(rule)) {
rule.use = [...rule.use];
const loader = (rule.use[0] = { ...rule.use[0] });
const options = (loader.options = { ...(loader.options as Record<string, any>) });
// capture the plugins defined at the root level
const plugins: string[] = options.plugins ?? [];
options.plugins = [];
// move the plugins to the top of the preset array so they will run after the typescript preset
options.presets = [
require.resolve('@kbn/babel-preset/common_preset'),
{ plugins },
...(options.presets as Preset[]).filter(isDesiredPreset).map((preset) => {
const tsPreset = getTsPreset(preset);
if (!tsPreset) {
return preset;
}
return [
tsPreset[0],
{
...tsPreset[1],
allowNamespaces: true,
allowDeclareFields: true,
},
];
}),
];
}
}
// copy and modify the webpack plugins added by storybook
const filteredStorybookPlugins = [];
for (const plugin of storybookConfig.plugins ?? []) {
// Remove the progress plugin
if (isProgressPlugin(plugin)) {
continue;
}
// This is the hacky part. We find something that looks like the
// HtmlWebpackPlugin and mutate its `options.template` to point at our
// revised template.
if (isHtmlPlugin(plugin)) {
plugin.options.template = require.resolve('../templates/index.ejs');
}
filteredStorybookPlugins.push(plugin);
}
return webpackMerge<object>(
{
...storybookConfig,
plugins: filteredStorybookPlugins,
module: {
...storybookConfig.module,
rules: updatedModuleRules,
},
},
config
);
}; };

View file

@ -1,77 +0,0 @@
<!DOCTYPE html>
<!-- This is a copy of the
[Storybook IFrame template](https://github.com/storybookjs/storybook/blob/337fdcd0fe7436b46bcca65145ff6db2affd780f/lib/core-common/src/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>
<%= htmlWebpackPlugin.options.title || 'Storybook' %>
</title>
<% if (htmlWebpackPlugin.files.favicon) { %>
<link rel="shortcut icon" href="<%= htmlWebpackPlugin.files.favicon%>" />
<% } %>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="eui-global" />
<meta name="emotion" />
<!-- Added for Kibana shared dependencies -->
<script>
window.__kbnPublicPath__ = { 'kbn-ui-shared-deps-npm': '', 'kbn-ui-shared-deps-src': '' };
window.__kbnHardenPrototypes__ = false;
</script>
<script src="kbn-ui-shared-deps-npm.dll.js"></script>
<script src="kbn-ui-shared-deps-src.js"></script>
<link href="kbn-ui-shared-deps-src.css" rel="stylesheet" />
<!-- -->
<% if (typeof headHtmlSnippet !=='undefined' ) { %>
<%= headHtmlSnippet %>
<% } %>
<% htmlWebpackPlugin.files.css.forEach(file=> { %>
<link href="<%= file %>" rel="stylesheet" />
<% }); %>
<style>
#root[hidden],
#docs-root[hidden] {
display: none !important;
}
</style>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300..700&family=Roboto+Mono:ital,wght@0,400..700;1,400..700&display=swap"
rel="stylesheet">
<meta name="eui-utilities" />
</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>
<% } %>
<% htmlWebpackPlugin.files.js.forEach(file=> { %>
<script src="<%= file %>"></script>
<% }); %>
</body>
</html>

View file

@ -7,7 +7,6 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { storiesOf } from '@storybook/react';
import React from 'react'; import React from 'react';
import { EuiFlexGroup } from '@elastic/eui'; import { EuiFlexGroup } from '@elastic/eui';
import { DataViewField } from '@kbn/data-views-plugin/public'; import { DataViewField } from '@kbn/data-views-plugin/public';
@ -36,13 +35,26 @@ const renderFieldName = (fldName: React.ReactNode) => {
); );
}; };
storiesOf('components/FieldName/FieldNameStories', module) export default {
.add('default', () => renderFieldName(<FieldName fieldName={'Discover test'} />)) title: 'components/FieldName/FieldNameStories',
.add('with field type', () => };
renderFieldName(<FieldName fieldName={'Discover test'} fieldType={'number'} />)
) export const Default = {
.add('with field mapping', () => render: () => renderFieldName(<FieldName fieldName={'Discover test'} />),
name: 'default',
};
export const WithFieldType = {
render: () => renderFieldName(<FieldName fieldName={'Discover test'} fieldType={'number'} />),
name: 'with field type',
};
export const WithFieldMapping = {
render: () =>
renderFieldName( renderFieldName(
<FieldName fieldName={'Discover test'} fieldMapping={field} fieldType={'number'} /> <FieldName fieldName={'Discover test'} fieldMapping={field} fieldType={'number'} />
) ),
);
name: 'with field mapping',
};

View file

@ -21,7 +21,7 @@ import { I18nProvider } from '@kbn/i18n-react';
import { KibanaRootContextProvider } from '@kbn/react-kibana-context-root'; import { KibanaRootContextProvider } from '@kbn/react-kibana-context-root';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import type { DecoratorFn } from '@storybook/react'; import type { Decorator } from '@storybook/react';
import type { CoreTheme } from '@kbn/core-theme-browser'; import type { CoreTheme } from '@kbn/core-theme-browser';
import type { I18nStart } from '@kbn/core-i18n-browser'; import type { I18nStart } from '@kbn/core-i18n-browser';
@ -35,7 +35,7 @@ const i18n: I18nStart = {
Context: ({ children }) => <I18nProvider>{children}</I18nProvider>, Context: ({ children }) => <I18nProvider>{children}</I18nProvider>,
}; };
export const KibanaContextDecorator: DecoratorFn = (storyFn, { globals }) => { export const KibanaContextDecorator: Decorator (storyFn, { globals }) => {
const colorMode = globals.euiTheme === 'v8.dark' ? 'dark' : 'light'; const colorMode = globals.euiTheme === 'v8.dark' ? 'dark' : 'light';
useEffect(() => { useEffect(() => {

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import type { DecoratorFn } from '@storybook/react'; import type { Decorator } from '@storybook/react';
import React from 'react'; import React from 'react';
import * as styledComponents from 'styled-components'; import * as styledComponents from 'styled-components';
@ -52,7 +52,7 @@ const KibanaStyledComponentsThemeProvider = <
* *
* @deprecated All Kibana components need to migrate to Emotion. * @deprecated All Kibana components need to migrate to Emotion.
*/ */
export const KibanaStyledComponentsThemeProviderDecorator: DecoratorFn = (storyFn, { globals }) => { export const KibanaStyledComponentsThemeProviderDecorator: Decorator = (storyFn, { globals }) => {
const darkMode = globals.euiTheme === 'v8.dark' || globals.euiTheme === 'v7.dark'; const darkMode = globals.euiTheme === 'v8.dark' || globals.euiTheme === 'v7.dark';
return ( return (

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { addons } from '@storybook/addons'; import { addons } from '@storybook/manager-api';
import { create } from '@storybook/theming'; import { create } from '@storybook/theming';
import { PANEL_ID as selectedPanel } from '@storybook/addon-actions'; import { PANEL_ID as selectedPanel } from '@storybook/addon-actions';

View file

@ -25,46 +25,50 @@ const argTypes = {
type KnownSolutionParams = Pick<KnownSolutionProps, 'size' | 'name'>; type KnownSolutionParams = Pick<KnownSolutionProps, 'size' | 'name'>;
export const SolutionType = (params: KnownSolutionParams) => { export const SolutionType = {
return <KibanaSolutionAvatar {...params} />; render: (params: KnownSolutionParams) => {
}; return <KibanaSolutionAvatar {...params} />;
},
SolutionType.argTypes = {
name: { argTypes: {
control: 'select', name: {
options: ['Cloud', 'Elastic', 'Kibana', 'Observability', 'Security', 'Enterprise Search'], control: 'select',
defaultValue: 'Elastic', options: ['Cloud', 'Elastic', 'Kibana', 'Observability', 'Security', 'Enterprise Search'],
defaultValue: 'Elastic',
},
...argTypes,
}, },
...argTypes,
}; };
type IconTypeParams = Pick<IconTypeProps, 'size' | 'name' | 'iconType'>; type IconTypeParams = Pick<IconTypeProps, 'size' | 'name' | 'iconType'>;
export const IconType = (params: IconTypeParams) => { export const IconType = {
return <KibanaSolutionAvatar {...params} />; render: (params: IconTypeParams) => {
}; return <KibanaSolutionAvatar {...params} />;
},
IconType.argTypes = { argTypes: {
iconType: { iconType: {
control: 'select', control: 'select',
options: [ options: [
'logoCloud', 'logoCloud',
'logoElastic', 'logoElastic',
'logoElasticsearch', 'logoElasticsearch',
'logoElasticStack', 'logoElasticStack',
'logoKibana', 'logoKibana',
'logoObservability', 'logoObservability',
'logoSecurity', 'logoSecurity',
'logoSiteSearch', 'logoSiteSearch',
'logoWorkplaceSearch', 'logoWorkplaceSearch',
'machineLearningApp', 'machineLearningApp',
'managementApp', 'managementApp',
], ],
defaultValue: 'logoElastic', defaultValue: 'logoElastic',
},
name: {
control: 'text',
defaultValue: 'Solution Name',
},
...argTypes,
}, },
name: {
control: 'text',
defaultValue: 'Solution Name',
},
...argTypes,
}; };

View file

@ -30,7 +30,7 @@ export class StorybookMock extends AbstractStorybookMock<
> { > {
propArguments = { propArguments = {
toggleChrome: { toggleChrome: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: true, defaultValue: true,
}, },
}; };

View file

@ -31,12 +31,14 @@ export default {
const mock = new ExitFullScreenButtonStorybookMock(); const mock = new ExitFullScreenButtonStorybookMock();
export const ExitFullScreenButton = (params: ExitFullScreenButtonStorybookParams) => { export const ExitFullScreenButton = {
return ( render: (params: ExitFullScreenButtonStorybookParams) => {
<ExitFullScreenButtonProvider {...mock.getServices()}> return (
<Component {...mock.getProps(params)} /> <ExitFullScreenButtonProvider {...mock.getServices()}>
</ExitFullScreenButtonProvider> <Component {...mock.getProps(params)} />
); </ExitFullScreenButtonProvider>
}; );
},
ExitFullScreenButton.argTypes = mock.getArgumentTypes(); argTypes: mock.getArgumentTypes(),
};

View file

@ -65,8 +65,10 @@ const argTypes = {
type Params = Record<keyof typeof argTypes, any>; type Params = Record<keyof typeof argTypes, any>;
export const IconButtonGroup = ({ buttonCount }: Params) => { export const IconButtonGroup = {
return <Component legend="Example icon group" buttons={iconButtons.slice(0, buttonCount)} />; render: ({ buttonCount }: Params) => {
}; return <Component legend="Example icon group" buttons={iconButtons.slice(0, buttonCount)} />;
},
IconButtonGroup.argTypes = argTypes; argTypes,
};

View file

@ -47,16 +47,18 @@ const argTypes = {
type Params = Record<keyof typeof argTypes, any>; type Params = Record<keyof typeof argTypes, any>;
export const ToolbarButton = ({ buttonStyle, buttonType, iconSide }: Params) => { export const ToolbarButton = {
return ( render: ({ buttonStyle, buttonType, iconSide }: Params) => {
<Component return (
as={buttonStyle} <Component
label="Toolbar button" as={buttonStyle}
iconType="lensApp" label="Toolbar button"
type={buttonType} iconType="lensApp"
iconSide={iconSide} type={buttonType}
/> iconSide={iconSide}
); />
}; );
},
ToolbarButton.argTypes = argTypes; argTypes,
};

View file

@ -43,41 +43,43 @@ export default {
argTypes, argTypes,
}; };
export const Popover = ({ showIcon, buttonType }: Params) => { export const Popover = {
return ( render: ({ showIcon, buttonType }: Params) => {
<Component return (
type={buttonType} <Component
label="Add element" type={buttonType}
iconType={showIcon ? 'plusInCircle' : undefined} label="Add element"
panelPaddingSize="none" iconType={showIcon ? 'plusInCircle' : undefined}
> panelPaddingSize="none"
{() => ( >
<EuiContextMenu {() => (
initialPanelId={0} <EuiContextMenu
panels={[ initialPanelId={0}
{ panels={[
id: 0, {
title: 'Open editor', id: 0,
items: [ title: 'Open editor',
{ items: [
name: 'Lens', {
icon: 'lensApp', name: 'Lens',
}, icon: 'lensApp',
{ },
name: 'Maps', {
icon: 'logoMaps', name: 'Maps',
}, icon: 'logoMaps',
{ },
name: 'TSVB', {
icon: 'visVisualBuilder', name: 'TSVB',
}, icon: 'visVisualBuilder',
], },
}, ],
]} },
/> ]}
)} />
</Component> )}
); </Component>
}; );
},
Popover.argTypes = argTypes; argTypes,
};

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { Story } from '@storybook/react'; import type { StoryFn } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { EuiContextMenu } from '@elastic/eui'; import { EuiContextMenu } from '@elastic/eui';
@ -174,7 +174,7 @@ export default {
}, },
}; };
const Template: Story<{ const Template: StoryFn<{
solution: 'Generic' | 'Canvas' | 'Dashboard'; solution: 'Generic' | 'Canvas' | 'Dashboard';
iconButtonCount: number; iconButtonCount: number;
showAddFromLibraryButton: boolean; showAddFromLibraryButton: boolean;
@ -205,20 +205,29 @@ const Template: Story<{
); );
}; };
export const Generic = Template.bind({}); export const Generic = {
Generic.args = { render: Template,
...Template.args,
solution: 'Generic', args: {
...Template.args,
solution: 'Generic',
},
}; };
export const Canvas = Template.bind({}); export const Canvas = {
Canvas.args = { render: Template,
...Template.args,
solution: 'Canvas', args: {
...Template.args,
solution: 'Canvas',
},
}; };
export const Dashboard = Template.bind({}); export const Dashboard = {
Dashboard.args = { render: Template,
...Template.args,
solution: 'Dashboard', args: {
...Template.args,
solution: 'Dashboard',
},
}; };

View file

@ -30,12 +30,14 @@ export default {
const mock = new NoDataCardStorybookMock(); const mock = new NoDataCardStorybookMock();
const argTypes = mock.getArgumentTypes(); const argTypes = mock.getArgumentTypes();
export const Card = (params: NoDataCardStorybookParams) => { export const Card = {
return ( render: (params: NoDataCardStorybookParams) => {
<NoDataCardProvider {...mock.getServices(params)}> return (
<NoDataCard {...params} /> <NoDataCardProvider {...mock.getServices(params)}>
</NoDataCardProvider> <NoDataCard {...params} />
); </NoDataCardProvider>
}; );
},
Card.argTypes = argTypes; argTypes,
};

View file

@ -35,25 +35,25 @@ export class StorybookMock extends AbstractStorybookMock<
propArguments = { propArguments = {
category: { category: {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: '', defaultValue: '',
}, },
title: { title: {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: '', defaultValue: '',
}, },
description: { description: {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: '', defaultValue: '',
}, },
button: { button: {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: '', defaultValue: '',
}, },
@ -61,7 +61,7 @@ export class StorybookMock extends AbstractStorybookMock<
serviceArguments = { serviceArguments = {
canAccessFleet: { canAccessFleet: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: true, defaultValue: true,
}, },
}; };

View file

@ -21,7 +21,7 @@ export class StorybookMock extends AbstractStorybookMock<{}, NavigationServices>
serviceArguments = { serviceArguments = {
isSideNavCollapsed: { isSideNavCollapsed: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
}; };

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { ComponentMeta } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import React, { EventHandler, FC, MouseEvent, useState, useEffect } from 'react'; import React, { EventHandler, FC, MouseEvent, useState, useEffect } from 'react';
import { of } from 'rxjs'; import { of } from 'rxjs';
@ -237,24 +237,26 @@ const groupExamplesNavigationTree: NavigationTreeDefinitionUI = {
], ],
}; };
export const GroupsExamples = (args: NavigationServices) => { export const GroupsExamples = {
const services = storybookMock.getServices({ render: (args: NavigationServices) => {
...args, const services = storybookMock.getServices({
recentlyAccessed$: of([ ...args,
{ label: 'This is an example', link: '/app/example/39859', id: '39850' }, recentlyAccessed$: of([
{ label: 'Another example', link: '/app/example/5235', id: '5235' }, { label: 'This is an example', link: '/app/example/39859', id: '39850' },
]), { label: 'Another example', link: '/app/example/5235', id: '5235' },
}); ]),
});
return ( return (
<NavigationWrapper> <NavigationWrapper>
{({ isCollapsed }) => ( {({ isCollapsed }) => (
<NavigationProvider {...services} isSideNavCollapsed={isCollapsed}> <NavigationProvider {...services} isSideNavCollapsed={isCollapsed}>
<Navigation navigationTree$={of(groupExamplesNavigationTree)} /> <Navigation navigationTree$={of(groupExamplesNavigationTree)} />
</NavigationProvider> </NavigationProvider>
)} )}
</NavigationWrapper> </NavigationWrapper>
); );
},
}; };
const navigationTree: NavigationTreeDefinitionUI = { const navigationTree: NavigationTreeDefinitionUI = {
@ -523,24 +525,26 @@ const navigationTree: NavigationTreeDefinitionUI = {
], ],
}; };
export const ComplexObjectDefinition = (args: NavigationServices) => { export const ComplexObjectDefinition: StoryObj<NavigationServices> = {
const services = storybookMock.getServices({ render: (args) => {
...args, const services = storybookMock.getServices({
recentlyAccessed$: of([ ...args,
{ label: 'This is an example', link: '/app/example/39859', id: '39850' }, recentlyAccessed$: of([
{ label: 'Another example', link: '/app/example/5235', id: '5235' }, { label: 'This is an example', link: '/app/example/39859', id: '39850' },
]), { label: 'Another example', link: '/app/example/5235', id: '5235' },
}); ]),
});
return ( return (
<NavigationWrapper> <NavigationWrapper>
{({ isCollapsed }) => ( {({ isCollapsed }) => (
<NavigationProvider {...services} isSideNavCollapsed={isCollapsed}> <NavigationProvider {...services} isSideNavCollapsed={isCollapsed}>
<Navigation navigationTree$={of(navigationTree)} /> <Navigation navigationTree$={of(navigationTree)} />
</NavigationProvider> </NavigationProvider>
)} )}
</NavigationWrapper> </NavigationWrapper>
); );
},
}; };
const panelContentProvider: ContentProvider = (id: string) => { const panelContentProvider: ContentProvider = (id: string) => {
@ -1135,27 +1139,29 @@ const navigationTreeWithPanels: NavigationTreeDefinitionUI = {
], ],
}; };
export const ObjectDefinitionWithPanel = (args: NavigationServices) => { export const ObjectDefinitionWithPanel: StoryObj<NavigationServices> = {
const services = storybookMock.getServices({ render: (args) => {
...args, const services = storybookMock.getServices({
recentlyAccessed$: of([ ...args,
{ label: 'This is an example', link: '/app/example/39859', id: '39850' }, recentlyAccessed$: of([
{ label: 'Another example', link: '/app/example/5235', id: '5235' }, { label: 'This is an example', link: '/app/example/39859', id: '39850' },
]), { label: 'Another example', link: '/app/example/5235', id: '5235' },
}); ]),
});
return ( return (
<NavigationWrapper> <NavigationWrapper>
{({ isCollapsed }) => ( {({ isCollapsed }) => (
<NavigationProvider {...services} isSideNavCollapsed={isCollapsed}> <NavigationProvider {...services} isSideNavCollapsed={isCollapsed}>
<Navigation <Navigation
navigationTree$={of(navigationTreeWithPanels)} navigationTree$={of(navigationTreeWithPanels)}
panelContentProvider={panelContentProvider} panelContentProvider={panelContentProvider}
/> />
</NavigationProvider> </NavigationProvider>
)} )}
</NavigationWrapper> </NavigationWrapper>
); );
},
}; };
export default { export default {
@ -1166,5 +1172,4 @@ export default {
page: mdx, page: mdx,
}, },
}, },
component: ComplexObjectDefinition, } as Meta;
} as ComponentMeta<typeof ComplexObjectDefinition>;

View file

@ -31,19 +31,21 @@ export default {
const mock = new CodeEditorStorybookMock(); const mock = new CodeEditorStorybookMock();
const argTypes = mock.getArgumentTypes(); const argTypes = mock.getArgumentTypes();
export const Basic = (params: CodeEditorStorybookParams) => { export const Basic = {
return ( render: (params: CodeEditorStorybookParams) => {
<CodeEditor return (
{...params} <CodeEditor
languageId="plainText" {...params}
onChange={action('on change')} languageId="plainText"
value="Hello!" onChange={action('on change')}
height={200} value="Hello!"
/> height={200}
); />
}; );
},
Basic.argTypes = argTypes; argTypes,
};
// A sample language definition with a few example tokens // A sample language definition with a few example tokens
// Taken from https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages // Taken from https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
@ -66,25 +68,27 @@ const logs = `[Sun Mar 7 20:54:27 2004] [notice] [client xx.xx.xx.xx] This is a
[Sun Mar 7 21:16:17 2004] [error] [client xx.xx.xx.xx] File does not exist: /home/httpd/twiki/view/Main/WebHome [Sun Mar 7 21:16:17 2004] [error] [client xx.xx.xx.xx] File does not exist: /home/httpd/twiki/view/Main/WebHome
`; `;
export const CustomLogLanguage = (params: CodeEditorStorybookParams) => { export const CustomLogLanguage = {
return ( render: (params: CodeEditorStorybookParams) => {
<div> return (
<CodeEditor <div>
{...params} <CodeEditor
languageId="loglang" {...params}
height={250} languageId="loglang"
value={logs} height={250}
options={{ value={logs}
minimap: { options={{
enabled: false, minimap: {
}, enabled: false,
}} },
/> }}
</div> />
); </div>
}; );
},
CustomLogLanguage.argTypes = argTypes; argTypes,
};
export const JSONSupport = () => { export const JSONSupport = () => {
return ( return (
@ -207,24 +211,26 @@ export const HoverProvider = () => {
); );
}; };
export const AutomaticResize = (params: CodeEditorStorybookParams) => { export const AutomaticResize = {
return ( render: (params: CodeEditorStorybookParams) => {
<div style={{ height: `calc(100vh - 30px)` }}> return (
<CodeEditor <div style={{ height: `calc(100vh - 30px)` }}>
{...params} <CodeEditor
languageId="plainText" {...params}
onChange={action('on change')} languageId="plainText"
value="Hello!" onChange={action('on change')}
height={'100%'} value="Hello!"
options={{ automaticLayout: true }} height={'100%'}
/> options={{ automaticLayout: true }}
</div> />
); </div>
);
},
argTypes,
}; };
AutomaticResize.argTypes = argTypes; const FitToContentComponent = (params: CodeEditorStorybookParams) => {
export const FitToContent = (params: CodeEditorStorybookParams) => {
const [value, setValue] = useState('hello'); const [value, setValue] = useState('hello');
return ( return (
<CodeEditor <CodeEditor
@ -241,4 +247,8 @@ export const FitToContent = (params: CodeEditorStorybookParams) => {
); );
}; };
FitToContent.argTypes = argTypes; export const FitToContent = {
render: (params: CodeEditorStorybookParams) => <FitToContentComponent {...params} />,
argTypes,
};

View file

@ -35,38 +35,38 @@ export class CodeEditorStorybookMock extends AbstractStorybookMock<
propArguments = { propArguments = {
languageId: { languageId: {
control: { control: {
type: 'radio', control: 'radio',
}, },
options: ['json', 'loglang', 'plaintext'], options: ['json', 'loglang', 'plaintext'],
defaultValue: 'json', defaultValue: 'json',
}, },
value: { value: {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: '', defaultValue: '',
}, },
'aria-label': { 'aria-label': {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: 'code editor', defaultValue: 'code editor',
}, },
allowFullScreen: { allowFullScreen: {
control: { control: {
type: 'boolean', control: 'boolean',
}, },
defaultValue: false, defaultValue: false,
}, },
transparentBackground: { transparentBackground: {
control: { control: {
type: 'boolean', control: 'boolean',
}, },
defaultValue: false, defaultValue: false,
}, },
placeholder: { placeholder: {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: 'myplaceholder', defaultValue: 'myplaceholder',
}, },

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { Meta, Story } from '@storybook/react'; import { StoryFn, Meta } from '@storybook/react';
import React from 'react'; import React from 'react';
import { EuiFormFieldset } from '@elastic/eui'; import { EuiFormFieldset } from '@elastic/eui';
@ -32,7 +32,7 @@ export default {
}, },
} as Meta; } as Meta;
export const ErrorInCallout: Story = () => { export const ErrorInCallout: StoryFn = () => {
const services = storybookMock.getServices(); const services = storybookMock.getServices();
return ( return (
@ -46,7 +46,7 @@ export const ErrorInCallout: Story = () => {
); );
}; };
export const SectionErrorInCallout: Story = () => { export const SectionErrorInCallout: StoryFn = () => {
const services = storybookMock.getServices(); const services = storybookMock.getServices();
return ( return (

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import { Meta, Story } from '@storybook/react'; import { StoryFn, Meta } from '@storybook/react';
import React from 'react'; import React from 'react';
import { EuiFormFieldset } from '@elastic/eui'; import { EuiFormFieldset } from '@elastic/eui';
@ -34,7 +34,7 @@ export default {
}, },
} as Meta; } as Meta;
export const ErrorInCallout: Story = () => { export const ErrorInCallout: StoryFn = () => {
const services = storybookMock.getServices(); const services = storybookMock.getServices();
return ( return (
@ -48,7 +48,7 @@ export const ErrorInCallout: Story = () => {
); );
}; };
export const SectionErrorInCallout: Story = () => { export const SectionErrorInCallout: StoryFn = () => {
const services = storybookMock.getServices(); const services = storybookMock.getServices();
return ( return (

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react'; import { Meta } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { base64dLogo } from '@kbn/shared-ux-file-image-mocks'; import { base64dLogo } from '@kbn/shared-ux-file-image-mocks';
import type { FileImageMetadata, FileKindBrowser } from '@kbn/shared-ux-file-types'; import type { FileImageMetadata, FileKindBrowser } from '@kbn/shared-ux-file-types';
@ -17,6 +17,7 @@ import { FilesContext } from '@kbn/shared-ux-file-context';
import { FilePicker, Props as FilePickerProps } from './file_picker'; import { FilePicker, Props as FilePickerProps } from './file_picker';
type ListResponse = ReturnType<FilesClient['list']>; type ListResponse = ReturnType<FilesClient['list']>;
type MetaDecorators = Pick<Meta, 'decorators'>;
const kind = 'filepicker'; const kind = 'filepicker';
const getFileKind = (id: string) => const getFileKind = (id: string) =>
@ -58,11 +59,9 @@ export default {
</FilesContext> </FilesContext>
), ),
], ],
} as ComponentMeta<typeof FilePicker>; } as Meta<typeof FilePicker>;
const Template: ComponentStory<typeof FilePicker> = (props) => <FilePicker {...props} />; export const Empty = {};
export const Empty = Template.bind({});
const d = new Date(); const d = new Date();
let id = 0; let id = 0;
@ -85,47 +84,17 @@ function createFileJSON(file?: Partial<FileJSON<FileImageMetadata>>): FileJSON<F
...file, ...file,
}; };
} }
export const BasicOne = Template.bind({});
BasicOne.decorators = [
(Story) => (
<FilesContext
client={
{
getDownloadHref: () => `data:image/png;base64,${base64dLogo}`,
list: async (): ListResponse => ({
files: [createFileJSON()],
total: 1,
}),
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
];
export const BasicMany = Template.bind({}); export const BasicOne: MetaDecorators = {
BasicMany.decorators = [ decorators: [
(Story) => { (Story) => (
const files = [
createFileJSON({ name: 'abc' }),
createFileJSON({ name: 'def' }),
createFileJSON({ name: 'efg' }),
createFileJSON({ name: 'foo' }),
createFileJSON({ name: 'bar' }),
createFileJSON(),
createFileJSON(),
];
return (
<FilesContext <FilesContext
client={ client={
{ {
getDownloadHref: () => `data:image/png;base64,${base64dLogo}`, getDownloadHref: () => `data:image/png;base64,${base64dLogo}`,
list: async (): ListResponse => ({ list: async (): ListResponse => ({
files, files: [createFileJSON()],
total: files.length, total: 1,
}), }),
getFileKind, getFileKind,
} as unknown as FilesClient } as unknown as FilesClient
@ -133,73 +102,80 @@ BasicMany.decorators = [
> >
<Story /> <Story />
</FilesContext> </FilesContext>
); ),
}, ],
]; };
export const BasicManyMany = Template.bind({}); export const BasicMany: MetaDecorators = {
BasicManyMany.decorators = [ decorators: [
(Story) => { (Story) => {
const array = new Array(102); const files = [
array.fill(null); createFileJSON({ name: 'abc' }),
return ( createFileJSON({ name: 'def' }),
<FilesContext createFileJSON({ name: 'efg' }),
client={ createFileJSON({ name: 'foo' }),
{ createFileJSON({ name: 'bar' }),
getDownloadHref: () => `data:image/png;base64,${base64dLogo}`, createFileJSON(),
list: async (): ListResponse => ({ createFileJSON(),
files: array.map((_, idx) => createFileJSON({ id: String(idx) })), ];
total: array.length,
}),
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
);
},
];
export const ErrorLoading = Template.bind({}); return (
ErrorLoading.decorators = [
(Story) => {
const array = new Array(102);
array.fill(createFileJSON());
return (
<FilesContext
client={
{
getDownloadHref: () => `data:image/png;base64,${base64dLogo}`,
list: async () => {
throw new Error('stop');
},
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
);
},
];
export const TryFilter = Template.bind({});
TryFilter.decorators = [
(Story) => {
const array = { files: [createFileJSON()], total: 1 };
return (
<>
<h2>Try entering a filter!</h2>
<FilesContext <FilesContext
client={ client={
{ {
getDownloadHref: () => `data:image/png;base64,${base64dLogo}`, getDownloadHref: () => `data:image/png;base64,${base64dLogo}`,
list: async ({ name }: { name: string[] }) => { list: async (): ListResponse => ({
if (name) { files,
return { files: [], total: 0 }; total: files.length,
} }),
return array; getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
);
},
],
};
export const BasicManyMany: MetaDecorators = {
decorators: [
(Story) => {
const array = new Array(102);
array.fill(null);
return (
<FilesContext
client={
{
getDownloadHref: () => `data:image/png;base64,${base64dLogo}`,
list: async (): ListResponse => ({
files: array.map((_, idx) => createFileJSON({ id: String(idx) })),
total: array.length,
}),
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
);
},
],
};
export const ErrorLoading: MetaDecorators = {
decorators: [
(Story) => {
const array = new Array(102);
array.fill(createFileJSON());
return (
<FilesContext
client={
{
getDownloadHref: () => `data:image/png;base64,${base64dLogo}`,
list: async () => {
throw new Error('stop');
}, },
getFileKind, getFileKind,
} as unknown as FilesClient } as unknown as FilesClient
@ -207,30 +183,61 @@ TryFilter.decorators = [
> >
<Story /> <Story />
</FilesContext> </FilesContext>
</> );
); },
}, ],
]; };
export const SingleSelect = Template.bind({}); export const TryFilter: MetaDecorators = {
SingleSelect.decorators = [ decorators: [
(Story) => ( (Story) => {
<FilesContext const array = { files: [createFileJSON()], total: 1 };
client={ return (
{ <>
getDownloadHref: () => `data:image/png;base64,${base64dLogo}`, <h2>Try entering a filter!</h2>
list: async (): ListResponse => ({ <FilesContext
files: [createFileJSON(), createFileJSON(), createFileJSON()], client={
total: 1, {
}), getDownloadHref: () => `data:image/png;base64,${base64dLogo}`,
getFileKind, list: async ({ name }: { name: string[] }) => {
} as unknown as FilesClient if (name) {
} return { files: [], total: 0 };
> }
<Story /> return array;
</FilesContext> },
), getFileKind,
]; } as unknown as FilesClient
SingleSelect.args = { }
multiple: undefined, >
<Story />
</FilesContext>
</>
);
},
],
};
export const SingleSelect: Partial<Meta> = {
decorators: [
(Story) => (
<FilesContext
client={
{
getDownloadHref: () => `data:image/png;base64,${base64dLogo}`,
list: async (): ListResponse => ({
files: [createFileJSON(), createFileJSON(), createFileJSON()],
total: 1,
}),
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
],
args: {
multiple: undefined,
},
}; };

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { FileKindBrowser, BaseFilesClient as FilesClient } from '@kbn/shared-ux-file-types'; import { FileKindBrowser, BaseFilesClient as FilesClient } from '@kbn/shared-ux-file-types';
import { FilesContext } from '@kbn/shared-ux-file-context'; import { FilesContext } from '@kbn/shared-ux-file-context';
@ -65,150 +65,162 @@ export default {
</FilesContext> </FilesContext>
), ),
], ],
} as ComponentMeta<typeof FileUpload>; } as Meta<typeof FileUpload>;
const Template: ComponentStory<typeof FileUpload> = (props: Props) => <FileUpload {...props} />; export const Basic = {};
export const Basic = Template.bind({}); export const AllowRepeatedUploads = {
args: {
export const AllowRepeatedUploads = Template.bind({}); allowRepeatedUploads: true,
AllowRepeatedUploads.args = { },
allowRepeatedUploads: true,
}; };
export const LongErrorUX = Template.bind({}); export const LongErrorUX: StoryObj = {
LongErrorUX.decorators = [ decorators: [
(Story) => ( (Story) => (
<FilesContext <FilesContext
client={ client={
{ {
create: async () => ({ file: { id: 'test' } }), create: async () => ({ file: { id: 'test' } }),
upload: async () => { upload: async () => {
await sleep(1000); await sleep(1000);
throw new Error('Something went wrong while uploading! '.repeat(10).trim()); throw new Error('Something went wrong while uploading! '.repeat(10).trim());
}, },
delete: async () => {}, delete: async () => {},
getFileKind, getFileKind,
} as unknown as FilesClient } as unknown as FilesClient
} }
> >
<Story /> <Story />
</FilesContext> </FilesContext>
), ),
]; ],
export const Abort = Template.bind({});
Abort.decorators = [
(Story) => (
<FilesContext
client={
{
create: async () => ({ file: { id: 'test' } }),
upload: async () => {
await sleep(60000);
},
delete: async () => {},
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
];
export const MaxSize = Template.bind({});
MaxSize.args = {
kind: miniFile,
}; };
export const ZipOnly = Template.bind({}); export const Abort: StoryObj = {
ZipOnly.args = { decorators: [
kind: zipOnly, (Story) => (
<FilesContext
client={
{
create: async () => ({ file: { id: 'test' } }),
upload: async () => {
await sleep(60000);
},
delete: async () => {},
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
],
}; };
export const AllowClearAfterUpload = Template.bind({}); export const MaxSize = {
AllowClearAfterUpload.args = { args: {
allowClear: true, kind: miniFile,
},
}; };
export const ImmediateUpload = Template.bind({}); export const ZipOnly = {
ImmediateUpload.args = { args: {
immediate: true, kind: zipOnly,
},
}; };
export const ImmediateUploadError = Template.bind({}); export const AllowClearAfterUpload = {
ImmediateUploadError.args = { args: {
immediate: true, allowClear: true,
}; },
ImmediateUploadError.decorators = [
(Story) => (
<FilesContext
client={
{
create: async () => ({ file: { id: 'test' } }),
upload: async () => {
await sleep(1000);
throw new Error('Something went wrong while uploading!');
},
delete: async () => {},
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
];
export const ImmediateUploadAbort = Template.bind({});
ImmediateUploadAbort.decorators = [
(Story) => (
<FilesContext
client={
{
create: async () => ({ file: { id: 'test' } }),
upload: async () => {
await sleep(60000);
},
delete: async () => {},
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
];
ImmediateUploadAbort.args = {
immediate: true,
}; };
export const Compressed = Template.bind({}); export const ImmediateUpload = {
Compressed.args = { args: {
compressed: true, immediate: true,
},
}; };
export const CompressedError = Template.bind({}); export const ImmediateUploadError: StoryObj = {
CompressedError.args = { args: {
compressed: true, immediate: true,
},
decorators: [
(Story) => (
<FilesContext
client={
{
create: async () => ({ file: { id: 'test' } }),
upload: async () => {
await sleep(1000);
throw new Error('Something went wrong while uploading!');
},
delete: async () => {},
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
],
};
export const ImmediateUploadAbort: StoryObj = {
decorators: [
(Story) => (
<FilesContext
client={
{
create: async () => ({ file: { id: 'test' } }),
upload: async () => {
await sleep(60000);
},
delete: async () => {},
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
],
args: {
immediate: true,
},
};
export const Compressed = {
args: {
compressed: true,
},
};
export const CompressedError: StoryObj = {
args: {
compressed: true,
},
decorators: [
(Story) => (
<FilesContext
client={
{
create: async () => ({ file: { id: 'test' } }),
upload: async () => {
await sleep(1000);
throw new Error('Something went wrong while uploading! '.repeat(10).trim());
},
delete: async () => {},
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
],
}; };
CompressedError.decorators = [
(Story) => (
<FilesContext
client={
{
create: async () => ({ file: { id: 'test' } }),
upload: async () => {
await sleep(1000);
throw new Error('Something went wrong while uploading! '.repeat(10).trim());
},
delete: async () => {},
getFileKind,
} as unknown as FilesClient
}
>
<Story />
</FilesContext>
),
];

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react'; import { StoryFn, Meta } from '@storybook/react';
import { getImageMetadata } from '@kbn/shared-ux-file-util'; import { getImageMetadata } from '@kbn/shared-ux-file-util';
import { getImageData as getBlob, base64dLogo } from '@kbn/shared-ux-file-image-mocks'; import { getImageData as getBlob, base64dLogo } from '@kbn/shared-ux-file-image-mocks';
@ -38,47 +38,62 @@ export default {
); );
}, },
], ],
} as ComponentMeta<typeof Image>; } as Meta<typeof Image>;
const Template: ComponentStory<typeof Image> = (props: Props, { loaded: { meta } }) => ( const Template: StoryFn<typeof Image> = (props: Props, { loaded: { meta } }) => (
<Image {...props} meta={meta} /> <Image {...props} meta={meta} />
); );
export const Basic = Template.bind({}); export const Basic = {
render: Template,
export const WithBlurhash = Template.bind({});
WithBlurhash.storyName = 'With blurhash';
WithBlurhash.loaders = [
async () => ({
meta: await getImageMetadata(getBlob()),
}),
];
export const BrokenSrc = Template.bind({});
BrokenSrc.storyName = 'Broken src';
BrokenSrc.args = {
src: 'foo',
}; };
export const WithBlurhashAndBrokenSrc = Template.bind({}); export const WithBlurhash = {
WithBlurhashAndBrokenSrc.storyName = 'With blurhash and broken src'; render: Template,
WithBlurhashAndBrokenSrc.args = { name: 'With blurhash',
src: 'foo',
loaders: [
async () => ({
meta: await getImageMetadata(getBlob()),
}),
],
}; };
WithBlurhashAndBrokenSrc.loaders = [ export const BrokenSrc = {
async () => ({ render: Template,
blurhash: await getImageMetadata(getBlob()), name: 'Broken src',
}),
];
export const WithCustomSizing = Template.bind({}); args: {
WithCustomSizing.storyName = 'With custom sizing'; src: 'foo',
WithCustomSizing.loaders = [ },
async () => ({ };
meta: await getImageMetadata(getBlob()),
}), export const WithBlurhashAndBrokenSrc = {
]; render: Template,
WithCustomSizing.args = { name: 'With blurhash and broken src',
css: `width: 100px; height: 500px; object-fit: fill`,
args: {
src: 'foo',
},
loaders: [
async () => ({
blurhash: await getImageMetadata(getBlob()),
}),
],
};
export const WithCustomSizing = {
render: Template,
name: 'With custom sizing',
loaders: [
async () => ({
meta: await getImageMetadata(getBlob()),
}),
],
args: {
css: `width: 100px; height: 500px; object-fit: fill`,
},
}; };

View file

@ -28,46 +28,48 @@ export default {
const mock = new RedirectAppLinksStorybookMock(); const mock = new RedirectAppLinksStorybookMock();
export const RedirectAppLinks = () => { export const RedirectAppLinks = {
return ( render: () => {
<EuiFlexGroup direction="column"> return (
<EuiFlexItem> <EuiFlexGroup direction="column">
<Component {...mock.getProps()}> <EuiFlexItem>
<EuiFlexGroup> <Component {...mock.getProps()}>
<EuiFlexItem grow={false}> <EuiFlexGroup>
<EuiButton <EuiFlexItem grow={false}>
data-test-subj="storybookButton" <EuiButton
iconType="plusInCircle" data-test-subj="storybookButton"
href="/some-test-url" iconType="plusInCircle"
> href="/some-test-url"
Button with URL >
</EuiButton> Button with URL
</EuiFlexItem> </EuiButton>
<EuiFlexItem grow={false}> </EuiFlexItem>
<EuiButton <EuiFlexItem grow={false}>
data-test-subj="storybookButton" <EuiButton
iconType="plusInCircle" data-test-subj="storybookButton"
onClick={action('onClick')} iconType="plusInCircle"
> onClick={action('onClick')}
Button without URL >
</EuiButton> Button without URL
</EuiFlexItem> </EuiButton>
</EuiFlexGroup> </EuiFlexItem>
</Component> </EuiFlexGroup>
</EuiFlexItem> </Component>
<EuiFlexItem> </EuiFlexItem>
<div> <EuiFlexItem>
<EuiButton <div>
data-test-subj="storybookButton" <EuiButton
iconType="plusInCircle" data-test-subj="storybookButton"
href="/?path=/story/redirect-app-links--component" iconType="plusInCircle"
> href="/?path=/story/redirect-app-links--component"
Button outside RedirectAppLinks >
</EuiButton> Button outside RedirectAppLinks
</div> </EuiButton>
</EuiFlexItem> </div>
</EuiFlexGroup> </EuiFlexItem>
); </EuiFlexGroup>
}; );
},
RedirectAppLinks.argTypes = mock.getArgumentTypes(); argTypes: mock.getArgumentTypes(),
};

View file

@ -28,15 +28,17 @@ export default {
const mock = new MarkdownStorybookMock(); const mock = new MarkdownStorybookMock();
const argTypes = mock.getArgumentTypes(); const argTypes = mock.getArgumentTypes();
export const MarkdownStoryComponent = (params: MarkdownStorybookParams) => { export const MarkdownStoryComponent = {
return ( render: (params: MarkdownStorybookParams) => {
// The markdown component is wrapped in the EuiFlexItem with width set to 50% return (
// Height can be set for the markdown component // The markdown component is wrapped in the EuiFlexItem with width set to 50%
<EuiFlexItem style={{ width: '400px' }}> // Height can be set for the markdown component
{/* readOnly is set to false because the Markdown component editor will error if set to true without markdown content or children */} <EuiFlexItem style={{ width: '400px' }}>
<Markdown {...params} readOnly={false} /> {/* readOnly is set to false because the Markdown component editor will error if set to true without markdown content or children */}
</EuiFlexItem> <Markdown {...params} readOnly={false} />
); </EuiFlexItem>
}; );
},
MarkdownStoryComponent.argTypes = argTypes; argTypes,
};

View file

@ -28,25 +28,27 @@ export default {
const mock = new MarkdownStorybookMock(); const mock = new MarkdownStorybookMock();
const argTypes = mock.getArgumentTypes(); const argTypes = mock.getArgumentTypes();
export const MarkdownStoryComponent = (params: MarkdownStorybookParams) => { export const MarkdownStoryComponent = {
return ( render: (params: MarkdownStorybookParams) => {
<EuiFlexGroup> return (
<EuiFlexItem> <EuiFlexGroup>
<Markdown <EuiFlexItem>
{...params} <Markdown
readOnly={true} {...params}
markdownContent={'My content in **markdown** format set as the *markdownContent prop*'} readOnly={true}
/> markdownContent={'My content in **markdown** format set as the *markdownContent prop*'}
<Markdown {...params} readOnly={true}> />
{`My content in **markdown** format passed as *children* <Markdown {...params} readOnly={true}>
\`openLinksInNewTab\` [test link to open in new tab or not](https://www.elastic.co) {`My content in **markdown** format passed as *children*
\`enableTooltipSupport\` !{tooltip[anchor text](Tooltip content)} \`openLinksInNewTab\` [test link to open in new tab or not](https://www.elastic.co)
\`validateLinks\` [link with non-standard scheme](testing-testing-this-is-a-non-standatd-scheme://) \`enableTooltipSupport\` !{tooltip[anchor text](Tooltip content)}
`} \`validateLinks\` [link with non-standard scheme](testing-testing-this-is-a-non-standatd-scheme://)
</Markdown> `}
</EuiFlexItem> </Markdown>
</EuiFlexGroup> </EuiFlexItem>
); </EuiFlexGroup>
}; );
},
MarkdownStoryComponent.argTypes = argTypes; argTypes,
};

View file

@ -35,49 +35,49 @@ export class MarkdownStorybookMock extends AbstractStorybookMock<
> { > {
propArguments = { propArguments = {
readOnly: { readOnly: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
openLinksInNewTab: { openLinksInNewTab: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: true, defaultValue: true,
}, },
placeholder: { placeholder: {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: '', defaultValue: '',
}, },
markdownContent: { markdownContent: {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: '', defaultValue: '',
}, },
ariaLabelContent: { ariaLabelContent: {
control: { control: {
type: 'text', control: 'text',
}, },
defaultValue: 'markdown component', defaultValue: 'markdown component',
}, },
height: { height: {
control: { control: {
type: 'select', control: 'select',
defaultValue: 'full', defaultValue: 'full',
label: 'height', label: 'height',
options: [0, 20, 50, 'full'], options: [0, 20, 50, 'full'],
}, },
}, },
enableTooltipSupport: { enableTooltipSupport: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
validateLinks: { validateLinks: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
enableSoftLineBreaks: { enableSoftLineBreaks: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
}; };

View file

@ -25,47 +25,49 @@ export default {
const mock = new TabbedModalStorybookMock(); const mock = new TabbedModalStorybookMock();
const argTypes = mock.getArgumentTypes(); const argTypes = mock.getArgumentTypes();
export const TrivialExample = (params: TabbedModalStorybookParams) => { export const TrivialExample = {
return ( render: (params: TabbedModalStorybookParams) => {
<TabbedModal return (
{...params} <TabbedModal
modalTitle="Trivial Example" {...params}
tabs={[ modalTitle="Trivial Example"
{ tabs={[
id: 'hello', {
name: 'Hello', id: 'hello',
content: () => { name: 'Hello',
return ( content: () => {
<Fragment> return (
<EuiSpacer size="m" /> <Fragment>
<EuiText> <EuiSpacer size="m" />
<p>Click the button to send a message into the void</p> <EuiText>
</EuiText> <p>Click the button to send a message into the void</p>
</Fragment> </EuiText>
); </Fragment>
}, );
initialState: { },
message: 'Hello World!!', initialState: {
}, message: 'Hello World!!',
modalActionBtn: { },
id: 'wave', modalActionBtn: {
label: 'Say Hi 👋🏾', id: 'wave',
dataTestSubj: 'wave', label: 'Say Hi 👋🏾',
handler: ({ state }) => { dataTestSubj: 'wave',
alert(state.message); handler: ({ state }) => {
alert(state.message);
},
}, },
}, },
}, ]}
]} defaultSelectedTabId="hello"
defaultSelectedTabId="hello" onClose={() => {}}
onClose={() => {}} />
/> );
); },
argTypes,
}; };
TrivialExample.argTypes = argTypes; const NonTrivialExampleComponent = (params: TabbedModalStorybookParams) => {
export const NonTrivialExample = (params: TabbedModalStorybookParams) => {
const checkboxGroupItemId1 = useGeneratedHtmlId({ const checkboxGroupItemId1 = useGeneratedHtmlId({
prefix: 'checkboxGroupItem', prefix: 'checkboxGroupItem',
suffix: 'first', suffix: 'first',
@ -177,4 +179,8 @@ export const NonTrivialExample = (params: TabbedModalStorybookParams) => {
); );
}; };
NonTrivialExample.argTypes = argTypes; export const NonTrivialExample = {
render: (params: TabbedModalStorybookParams) => <NonTrivialExampleComponent {...params} />,
argTypes,
};

View file

@ -31,19 +31,19 @@ export class StorybookMock extends AbstractStorybookMock<
propArguments = { propArguments = {
tabs: { tabs: {
control: { control: {
type: 'array', control: 'array',
}, },
defaultValue: [], defaultValue: [],
}, },
defaultSelectedTabId: { defaultSelectedTabId: {
control: { control: {
type: 'array', control: 'array',
}, },
defaultValue: [], defaultValue: [],
}, },
onClose: { onClose: {
control: { control: {
type: 'array', control: 'array',
}, },
defaultValue: [], defaultValue: [],
}, },

View file

@ -42,42 +42,50 @@ const solutionNavMock = new SolutionNavStorybookMock();
const noDataConfigMock = new NoDataConfigStorybookMock(); const noDataConfigMock = new NoDataConfigStorybookMock();
const innerMock = new InnerPageTemplateStorybookMock(); const innerMock = new InnerPageTemplateStorybookMock();
export const WithNoDataConfig = (params: NoDataConfigStorybookParams) => { export const WithNoDataConfig = {
return ( render: (params: NoDataConfigStorybookParams) => {
<KibanaPageTemplateProvider {...noDataConfigMock.getServices(params)}> return (
<Component {...noDataConfigMock.getProps(params)} /> <KibanaPageTemplateProvider {...noDataConfigMock.getServices(params)}>
</KibanaPageTemplateProvider> <Component {...noDataConfigMock.getProps(params)} />
); </KibanaPageTemplateProvider>
);
},
argTypes: noDataConfigMock.getArgumentTypes(),
}; };
WithNoDataConfig.argTypes = noDataConfigMock.getArgumentTypes(); export const WithSolutionNav = {
render: (params: SolutionNavStorybookParams) => {
return (
<KibanaPageTemplateProvider {...solutionNavMock.getServices(params)}>
<Component {...solutionNavMock.getProps(params)} />
</KibanaPageTemplateProvider>
);
},
export const WithSolutionNav = (params: SolutionNavStorybookParams) => { argTypes: solutionNavMock.getArgumentTypes(),
return (
<KibanaPageTemplateProvider {...solutionNavMock.getServices(params)}>
<Component {...solutionNavMock.getProps(params)} />
</KibanaPageTemplateProvider>
);
}; };
WithSolutionNav.argTypes = solutionNavMock.getArgumentTypes(); export const WithBoth = {
render: (params: KibanaPageTemplateStorybookParams) => {
return (
<KibanaPageTemplateProvider {...templateMock.getServices(params)}>
<Component {...templateMock.getProps(params)} />
</KibanaPageTemplateProvider>
);
},
export const WithBoth = (params: KibanaPageTemplateStorybookParams) => { argTypes: templateMock.getArgumentTypes(),
return (
<KibanaPageTemplateProvider {...templateMock.getServices(params)}>
<Component {...templateMock.getProps(params)} />
</KibanaPageTemplateProvider>
);
}; };
WithBoth.argTypes = templateMock.getArgumentTypes(); export const WithNeither = {
render: (params: InnerPageTemplateStorybookParams) => {
return (
<KibanaPageTemplateProvider {...innerMock.getServices(params)}>
<Component {...innerMock.getProps(params)} />
</KibanaPageTemplateProvider>
);
},
export const WithNeither = (params: InnerPageTemplateStorybookParams) => { argTypes: innerMock.getArgumentTypes(),
return (
<KibanaPageTemplateProvider {...innerMock.getServices(params)}>
<Component {...innerMock.getProps(params)} />
</KibanaPageTemplateProvider>
);
}; };
WithNeither.argTypes = innerMock.getArgumentTypes();

View file

@ -33,24 +33,24 @@ const noDataConfigMock = new NoDataConfigPageStorybookMock();
export const pageHeaderArguments: ArgumentParams<PropArguments> = { export const pageHeaderArguments: ArgumentParams<PropArguments> = {
isEmptyState: { isEmptyState: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: false, defaultValue: false,
}, },
iconType: { iconType: {
control: { type: 'radio' }, control: { control: 'radio' },
options: ['logoElastic', 'logoKibana', 'logoCloud', undefined], options: ['logoElastic', 'logoKibana', 'logoCloud', undefined],
defaultValue: undefined, defaultValue: undefined,
}, },
pageTitle: { pageTitle: {
control: 'text', control: { control: 'text' },
defaultValue: 'Page title', defaultValue: 'Page title',
}, },
description: { description: {
control: 'text', control: { control: 'text' },
defaultValue: 'Page description', defaultValue: 'Page description',
}, },
rightSideItems: { rightSideItems: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: true, defaultValue: true,
}, },
}; };

View file

@ -32,20 +32,20 @@ const noDataConfigMock = new NoDataConfigPageStorybookMock();
export const noDataConfigArguments: ArgumentParams<NoDataConfigArguments> = { export const noDataConfigArguments: ArgumentParams<NoDataConfigArguments> = {
solution: { solution: {
control: 'text', control: { control: 'text' },
defaultValue: 'Observability', defaultValue: 'Observability',
}, },
logo: { logo: {
control: { type: 'radio' }, control: { control: 'radio' },
options: ['logoElastic', 'logoKibana', 'logoCloud', undefined], options: ['logoElastic', 'logoKibana', 'logoCloud', undefined],
defaultValue: undefined, defaultValue: undefined,
}, },
docsLink: { docsLink: {
control: 'text', control: { control: 'text' },
defaultValue: 'docs/link', defaultValue: 'docs/link',
}, },
pageTitle: { pageTitle: {
control: 'text', control: { control: 'text' },
defaultValue: '', defaultValue: '',
}, },
}; };

View file

@ -29,16 +29,16 @@ const noDataConfigMock = new NoDataConfigPageStorybookMock();
export const solutionNavArguments: ArgumentParams<SolutionNavArguments> = { export const solutionNavArguments: ArgumentParams<SolutionNavArguments> = {
name: { name: {
control: 'text', control: { control: 'text' },
defaultValue: 'Kibana', defaultValue: 'Kibana',
}, },
icon: { icon: {
control: { type: 'radio' }, control: { control: 'radio' },
options: ['logoKibana', 'logoObservability', 'logoSecurity'], options: ['logoKibana', 'logoObservability', 'logoSecurity'],
defaultValue: 'logoKibana', defaultValue: 'logoKibana',
}, },
canBeCollapsed: { canBeCollapsed: {
control: 'boolean', control: { control: 'boolean' },
defaultValue: true, defaultValue: true,
}, },
}; };

Some files were not shown because too many files have changed in this diff Show more