diff --git a/.buildkite/scripts/bootstrap.sh b/.buildkite/scripts/bootstrap.sh index c80f215052a26..413c56e2e8109 100755 --- a/.buildkite/scripts/bootstrap.sh +++ b/.buildkite/scripts/bootstrap.sh @@ -19,7 +19,7 @@ if ! yarn kbn bootstrap; then fi if [[ "$DISABLE_BOOTSTRAP_VALIDATION" != "true" ]]; then - verify_no_git_changes 'yarn kbn bootstrap' + check_for_changed_files 'yarn kbn bootstrap' fi ### diff --git a/.buildkite/scripts/common/util.sh b/.buildkite/scripts/common/util.sh index 4a81506200cfa..d6aa988a4c696 100755 --- a/.buildkite/scripts/common/util.sh +++ b/.buildkite/scripts/common/util.sh @@ -14,17 +14,46 @@ is_pr() { false } -verify_no_git_changes() { +check_for_changed_files() { RED='\033[0;31m' YELLOW='\033[0;33m' C_RESET='\033[0m' # Reset color + SHOULD_AUTO_COMMIT_CHANGES="${2:-}" GIT_CHANGES="$(git ls-files --modified -- . ':!:.bazelrc')" + if [ "$GIT_CHANGES" ]; then - echo -e "\n${RED}ERROR: '$1' caused changes to the following files:${C_RESET}\n" - echo -e "$GIT_CHANGES\n" - echo -e "\n${YELLOW}TO FIX: Run '$1' locally, commit the changes and push to your branch${C_RESET}\n" - exit 1 + if [[ "$SHOULD_AUTO_COMMIT_CHANGES" == "true" && "${BUILDKITE_PULL_REQUEST:-}" ]]; then + NEW_COMMIT_MESSAGE="[CI] Auto-commit changed files from '$1'" + PREVIOUS_COMMIT_MESSAGE="$(git log -1 --pretty=%B)" + + if [[ "$NEW_COMMIT_MESSAGE" == "$PREVIOUS_COMMIT_MESSAGE" ]]; then + echo -e "\n${RED}ERROR: '$1' caused changes to the following files:${C_RESET}\n" + echo -e "$GIT_CHANGES\n" + echo -e "CI already attempted to commit these changes, but the file(s) seem to have changed again." + echo -e "Please review and fix manually." + exit 1 + fi + + echo "'$1' caused changes to the following files:" + echo "$GIT_CHANGES" + echo "" + echo "Auto-committing these changes now. A new build should start soon if successful." + + git config --global user.name kibanamachine + git config --global user.email '42973632+kibanamachine@users.noreply.github.com' + gh pr checkout "${BUILDKITE_PULL_REQUEST}" + git add -u -- . ':!.bazelrc' + + git commit -m "$NEW_COMMIT_MESSAGE" + git push + exit 1 + else + echo -e "\n${RED}ERROR: '$1' caused changes to the following files:${C_RESET}\n" + echo -e "$GIT_CHANGES\n" + echo -e "\n${YELLOW}TO FIX: Run '$1' locally, commit the changes and push to your branch${C_RESET}\n" + exit 1 + fi fi } diff --git a/.buildkite/scripts/steps/checks/bazel_packages.sh b/.buildkite/scripts/steps/checks/bazel_packages.sh index 85268bdcb0f06..ca82c6fef609e 100755 --- a/.buildkite/scripts/steps/checks/bazel_packages.sh +++ b/.buildkite/scripts/steps/checks/bazel_packages.sh @@ -5,4 +5,6 @@ set -euo pipefail source .buildkite/scripts/common/util.sh echo --- Check Bazel Packages Manifest -node scripts/generate packages_build_manifest --validate +node scripts/generate packages_build_manifest + +check_for_changed_files 'node scripts/generate packages_build_manifest' true diff --git a/.buildkite/scripts/steps/checks/kbn_pm_dist.sh b/.buildkite/scripts/steps/checks/kbn_pm_dist.sh index 61281bc136311..6f75bd9ea4e29 100755 --- a/.buildkite/scripts/steps/checks/kbn_pm_dist.sh +++ b/.buildkite/scripts/steps/checks/kbn_pm_dist.sh @@ -7,4 +7,4 @@ source .buildkite/scripts/common/util.sh echo "--- Building kbn-pm distributable" yarn kbn run build -i @kbn/pm -verify_no_git_changes 'yarn kbn run build -i @kbn/pm' +check_for_changed_files 'yarn kbn run build -i @kbn/pm' true diff --git a/.buildkite/scripts/steps/checks/plugin_list_docs.sh b/.buildkite/scripts/steps/checks/plugin_list_docs.sh index b422e478aaf7d..04797887a9006 100755 --- a/.buildkite/scripts/steps/checks/plugin_list_docs.sh +++ b/.buildkite/scripts/steps/checks/plugin_list_docs.sh @@ -7,4 +7,4 @@ source .buildkite/scripts/common/util.sh echo "--- Building plugin list docs" node scripts/build_plugin_list_docs -verify_no_git_changes 'node scripts/build_plugin_list_docs' +check_for_changed_files 'node scripts/build_plugin_list_docs' true diff --git a/.buildkite/scripts/steps/cloud/build_and_deploy.sh b/.buildkite/scripts/steps/cloud/build_and_deploy.sh index 8b202bea9fc84..2bb9bc90e88da 100755 --- a/.buildkite/scripts/steps/cloud/build_and_deploy.sh +++ b/.buildkite/scripts/steps/cloud/build_and_deploy.sh @@ -32,6 +32,10 @@ node scripts/build \ --skip-docker-ubuntu \ --skip-docker-contexts +ELASTICSEARCH_MANIFEST_URL="https://storage.googleapis.com/kibana-ci-es-snapshots-daily/$(jq -r '.version' package.json)/manifest-latest-verified.json" +ELASTICSEARCH_SHA=$(curl -s $ELASTICSEARCH_MANIFEST_URL | jq -r '.sha') +ELASTICSEARCH_CLOUD_IMAGE="docker.elastic.co/kibana-ci/elasticsearch-cloud:$VERSION-$ELASTICSEARCH_SHA" + CLOUD_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" docker.elastic.co/kibana-ci/kibana-cloud) CLOUD_DEPLOYMENT_NAME="kibana-pr-$BUILDKITE_PULL_REQUEST" @@ -40,6 +44,7 @@ JSON_FILE=$(mktemp --suffix ".json") if [ -z "${CLOUD_DEPLOYMENT_ID}" ]; then jq ' .resources.kibana[0].plan.kibana.docker_image = "'$CLOUD_IMAGE'" | + .resources.elasticsearch[0].plan.elasticsearch.docker_image = "'$ELASTICSEARCH_CLOUD_IMAGE'" | .name = "'$CLOUD_DEPLOYMENT_NAME'" | .resources.kibana[0].plan.kibana.version = "'$VERSION'" | .resources.elasticsearch[0].plan.elasticsearch.version = "'$VERSION'" | @@ -70,6 +75,7 @@ if [ -z "${CLOUD_DEPLOYMENT_ID}" ]; then else ecctl deployment show "$CLOUD_DEPLOYMENT_ID" --generate-update-payload | jq ' .resources.kibana[0].plan.kibana.docker_image = "'$CLOUD_IMAGE'" | + .resources.elasticsearch[0].plan.elasticsearch.docker_image = "'$ELASTICSEARCH_CLOUD_IMAGE'" | (.. | select(.version? != null).version) = "'$VERSION'" ' > /tmp/deploy.json ecctl deployment update "$CLOUD_DEPLOYMENT_ID" --track --output json --file /tmp/deploy.json &> "$JSON_FILE" @@ -87,7 +93,9 @@ cat << EOF | buildkite-agent annotate --style "info" --context cloud Credentials: \`vault read secret/kibana-issues/dev/cloud-deploy/$CLOUD_DEPLOYMENT_NAME\` - Image: $CLOUD_IMAGE + Kibana image: \`$CLOUD_IMAGE\` + + Elasticsearch image: \`$ELASTICSEARCH_CLOUD_IMAGE\` EOF buildkite-agent meta-data set pr_comment:deploy_cloud:head "* [Cloud Deployment](${CLOUD_DEPLOYMENT_KIBANA_URL})" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1ee6089df8c5f..1ccad2a3ac36d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -118,7 +118,11 @@ /x-pack/test/api_integration/apis/monitoring @elastic/infra-monitoring-ui # Fleet +/fleet_packages.json @elastic/fleet /x-pack/plugins/fleet/ @elastic/fleet +/x-pack/test/fleet_api_integration @elastic/fleet +/x-pack/test/fleet_cypress @elastic/fleet +/x-pack/test/fleet_functional @elastic/fleet # APM /x-pack/plugins/apm/ @elastic/apm-ui diff --git a/.i18nrc.json b/.i18nrc.json index eeb2578ef3472..402932902f249 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -60,6 +60,7 @@ "server": "src/legacy/server", "share": "src/plugins/share", "sharedUX": "src/plugins/shared_ux", + "sharedUXComponents": "packages/kbn-shared-ux-components/src", "statusPage": "src/legacy/core_plugins/status_page", "telemetry": ["src/plugins/telemetry", "src/plugins/telemetry_management_section"], "timelion": ["src/plugins/vis_types/timelion"], diff --git a/config/kibana.yml b/config/kibana.yml index 8ca8eb673c276..50ddad9a4b32a 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -62,6 +62,10 @@ # must be a positive integer. #elasticsearch.requestTimeout: 30000 +# The maximum number of sockets that can be used for communications with elasticsearch. +# Defaults to `Infinity`. +#elasticsearch.maxSockets: 1024 + # Specifies whether Kibana should use compression for communications with elasticsearch # Defaults to `false`. #elasticsearch.compression: false diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md index e7b65fc9dfa56..2a8a99c17ad63 100644 --- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclientconfig.md @@ -9,7 +9,7 @@ Configuration options to be used to create a [cluster client](./kibana-plugin-co Signature: ```typescript -export declare type ElasticsearchClientConfig = Pick & { +export declare type ElasticsearchClientConfig = Pick & { pingTimeout?: ElasticsearchConfig['pingTimeout'] | ClientOptions['pingTimeout']; requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout']; ssl?: Partial; diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.maxsockets.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.maxsockets.md new file mode 100644 index 0000000000000..64403f0cad543 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.maxsockets.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchConfig](./kibana-plugin-core-server.elasticsearchconfig.md) > [maxSockets](./kibana-plugin-core-server.elasticsearchconfig.maxsockets.md) + +## ElasticsearchConfig.maxSockets property + +The maximum number of sockets that can be used for communications with elasticsearch. + +Signature: + +```typescript +readonly maxSockets: number; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.md index 3e9bb43c98501..593836664d5bf 100644 --- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.md +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchconfig.md @@ -28,6 +28,7 @@ export declare class ElasticsearchConfig | [healthCheckDelay](./kibana-plugin-core-server.elasticsearchconfig.healthcheckdelay.md) | | Duration | The interval between health check requests Kibana sends to the Elasticsearch. | | [hosts](./kibana-plugin-core-server.elasticsearchconfig.hosts.md) | | string\[\] | Hosts that the client will connect to. If sniffing is enabled, this list will be used as seeds to discover the rest of your cluster. | | [ignoreVersionMismatch](./kibana-plugin-core-server.elasticsearchconfig.ignoreversionmismatch.md) | | boolean | Whether to allow kibana to connect to a non-compatible elasticsearch node. | +| [maxSockets](./kibana-plugin-core-server.elasticsearchconfig.maxsockets.md) | | number | The maximum number of sockets that can be used for communications with elasticsearch. | | [password?](./kibana-plugin-core-server.elasticsearchconfig.password.md) | | string | (Optional) If Elasticsearch is protected with basic authentication, this setting provides the password that the Kibana server uses to perform its administrative functions. | | [pingTimeout](./kibana-plugin-core-server.elasticsearchconfig.pingtimeout.md) | | Duration | Timeout after which PING HTTP request will be aborted and retried. | | [requestHeadersWhitelist](./kibana-plugin-core-server.elasticsearchconfig.requestheaderswhitelist.md) | | string\[\] | List of Kibana client-side headers to send to Elasticsearch when request scoped cluster client is used. If this is an empty array then \*no\* client-side will be sent. | diff --git a/docs/maps/import-geospatial-data.asciidoc b/docs/maps/import-geospatial-data.asciidoc index 7a35b7d1df599..3746b21437a44 100644 --- a/docs/maps/import-geospatial-data.asciidoc +++ b/docs/maps/import-geospatial-data.asciidoc @@ -6,9 +6,6 @@ To import geospatical data into the Elastic Stack, the data must be indexed as { Geospatial data comes in many formats. Choose an import tool based on the format of your geospatial data. -TIP: When you upload files in {kib}, there is a file size -limit, which is configurable in <>. - [discrete] [[import-geospatial-privileges]] === Security privileges diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 3a1e0f1a7f4ff..2b36e1fb66185 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -90,6 +90,10 @@ enforce even rudimentary CSP rules, though {kib} is still accessible. This configuration is effectively ignored when <> is enabled. *Default: `true`* +|[[elasticsearch-maxSockets]] `elasticsearch.maxSockets` + | The maximum number of sockets that can be used for communications with elasticsearch. +*Default: `Infinity`* + | `elasticsearch.customHeaders:` | Header names and values to send to {es}. Any custom headers cannot be overwritten by client-side headers, regardless of the diff --git a/package.json b/package.json index aa165813aa2c1..aeecb72a3d748 100644 --- a/package.json +++ b/package.json @@ -167,6 +167,10 @@ "@kbn/securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils", "@kbn/server-http-tools": "link:bazel-bin/packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:bazel-bin/packages/kbn-server-route-repository", + "@kbn/shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components", + "@kbn/shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services", + "@kbn/shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook", + "@kbn/shared-ux-utility": "link:bazel-bin/packages/kbn-shared-ux-utility", "@kbn/std": "link:bazel-bin/packages/kbn-std", "@kbn/timelion-grammar": "link:bazel-bin/packages/kbn-timelion-grammar", "@kbn/tinymath": "link:bazel-bin/packages/kbn-tinymath", @@ -196,6 +200,10 @@ "@turf/helpers": "6.0.1", "@turf/length": "^6.0.2", "@types/jsonwebtoken": "^8.5.6", + "@types/kbn__shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components/npm_module_types", + "@types/kbn__shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services/npm_module_types", + "@types/kbn__shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook/npm_module_types", + "@types/kbn__shared-ux-utility": "link:bazel-bin/packages/kbn-shared-ux-utility/npm_module_types", "@types/moment-duration-format": "^2.2.3", "JSONStream": "1.3.5", "abort-controller": "^3.0.0", @@ -410,7 +418,7 @@ "usng.js": "^0.4.5", "utility-types": "^3.10.0", "uuid": "3.3.2", - "vega": "^5.21.0", + "vega": "^5.22.0", "vega-interpreter": "^1.0.4", "vega-lite": "^5.2.0", "vega-schema-url-parser": "^2.2.0", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index f01042cecfae9..e880df5f55782 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -66,6 +66,10 @@ filegroup( "//packages/kbn-securitysolution-utils:build", "//packages/kbn-server-http-tools:build", "//packages/kbn-server-route-repository:build", + "//packages/kbn-shared-ux-components:build", + "//packages/kbn-shared-ux-services:build", + "//packages/kbn-shared-ux-storybook:build", + "//packages/kbn-shared-ux-utility:build", "//packages/kbn-spec-to-console:build", "//packages/kbn-std:build", "//packages/kbn-storybook:build", @@ -139,6 +143,10 @@ filegroup( "//packages/kbn-securitysolution-utils:build_types", "//packages/kbn-server-http-tools:build_types", "//packages/kbn-server-route-repository:build_types", + "//packages/kbn-shared-ux-components:build_types", + "//packages/kbn-shared-ux-services:build_types", + "//packages/kbn-shared-ux-storybook:build_types", + "//packages/kbn-shared-ux-utility:build_types", "//packages/kbn-std:build_types", "//packages/kbn-storybook:build_types", "//packages/kbn-telemetry-tools:build_types", diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index fe4b04548244a..e420b4ea65947 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -236,6 +236,7 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: mainFields: ['browser', 'main'], alias: { core_app_image_assets: Path.resolve(worker.repoRoot, 'src/core/public/core_app/images'), + vega: Path.resolve(worker.repoRoot, 'node_modules/vega/build-es5/vega.js'), }, symlinks: false, }, diff --git a/packages/kbn-shared-ux-components/BUILD.bazel b/packages/kbn-shared-ux-components/BUILD.bazel new file mode 100644 index 0000000000000..16dfe9e0620e1 --- /dev/null +++ b/packages/kbn-shared-ux-components/BUILD.bazel @@ -0,0 +1,150 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-shared-ux-components" +PKG_REQUIRE_NAME = "@kbn/shared-ux-components" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.scss", + "src/**/*.mdx", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "//packages/kbn-i18n", + "//packages/kbn-i18n-react", + "//packages/kbn-shared-ux-services", + "//packages/kbn-shared-ux-storybook", + "//packages/kbn-shared-ux-utility", + "@npm//@elastic/eui", + "@npm//@emotion/react", + "@npm//@emotion/css", + "@npm//classnames", + "@npm//react-use", + "@npm//react", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "//packages/kbn-i18n:npm_module_types", + "//packages/kbn-i18n-react:npm_module_types", + "//packages/kbn-shared-ux-services:npm_module_types", + "//packages/kbn-shared-ux-storybook:npm_module_types", + "//packages/kbn-shared-ux-utility:npm_module_types", + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/react", + "@npm//@types/classnames", + "@npm//@emotion/react", + "@npm//@emotion/css", + "@npm//@elastic/eui", + "@npm//react-use", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, + additional_args = [ + "--copy-files", + "--quiet" + ], +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-shared-ux-components/README.mdx b/packages/kbn-shared-ux-components/README.mdx new file mode 100644 index 0000000000000..8fee1571e5220 --- /dev/null +++ b/packages/kbn-shared-ux-components/README.mdx @@ -0,0 +1,10 @@ +--- +id: kibSharedUXComponents +slug: /kibana-dev-docs/shared-ux/packages/kbn-shared-ux-components +title: Shared UX Components +summary: +date: 2022-03-11 +tags: ['kibana', 'dev', 'sharedUX'] +--- + +> TODO diff --git a/packages/kbn-shared-ux-components/jest.config.js b/packages/kbn-shared-ux-components/jest.config.js new file mode 100644 index 0000000000000..c2ac8c3a57106 --- /dev/null +++ b/packages/kbn-shared-ux-components/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-shared-ux-components'], +}; diff --git a/packages/kbn-shared-ux-components/package.json b/packages/kbn-shared-ux-components/package.json new file mode 100644 index 0000000000000..948df7fadd712 --- /dev/null +++ b/packages/kbn-shared-ux-components/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-components", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/plugins/shared_ux/public/components/empty_state/assets/data_view_illustration.tsx b/packages/kbn-shared-ux-components/src/empty_state/assets/data_view_illustration.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/empty_state/assets/data_view_illustration.tsx rename to packages/kbn-shared-ux-components/src/empty_state/assets/data_view_illustration.tsx diff --git a/src/plugins/shared_ux/public/components/empty_state/assets/index.tsx b/packages/kbn-shared-ux-components/src/empty_state/assets/index.tsx similarity index 93% rename from src/plugins/shared_ux/public/components/empty_state/assets/index.tsx rename to packages/kbn-shared-ux-components/src/empty_state/assets/index.tsx index c234cdf40055b..129bbbf3b202d 100644 --- a/src/plugins/shared_ux/public/components/empty_state/assets/index.tsx +++ b/packages/kbn-shared-ux-components/src/empty_state/assets/index.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; -import { withSuspense } from '../../utility'; +import { withSuspense } from '@kbn/shared-ux-utility'; export const LazyDataViewIllustration = React.lazy(() => import('../assets/data_view_illustration').then(({ DataViewIllustration }) => ({ diff --git a/src/plugins/shared_ux/public/components/empty_state/index.tsx b/packages/kbn-shared-ux-components/src/empty_state/index.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/empty_state/index.tsx rename to packages/kbn-shared-ux-components/src/empty_state/index.tsx diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap similarity index 83% rename from src/plugins/shared_ux/public/components/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap rename to packages/kbn-shared-ux-components/src/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap index 5526b78bceb73..e84b997d8df87 100644 --- a/src/plugins/shared_ux/public/components/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap @@ -10,7 +10,7 @@ exports[` is rendered correctly 1`] = ` > @@ -26,7 +26,7 @@ exports[` is rendered correctly 1`] = ` > diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.test.tsx b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/documentation_link.test.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.test.tsx rename to packages/kbn-shared-ux-components/src/empty_state/no_data_views/documentation_link.test.tsx diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.tsx b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/documentation_link.tsx similarity index 89% rename from src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.tsx rename to packages/kbn-shared-ux-components/src/empty_state/no_data_views/documentation_link.tsx index 4ac07899fef5f..3b3e742ea74ce 100644 --- a/src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.tsx +++ b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/documentation_link.tsx @@ -20,7 +20,7 @@ export function DocumentationLink({ href }: Props) {
@@ -29,7 +29,7 @@ export function DocumentationLink({ href }: Props) {
diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/index.tsx b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/index.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/empty_state/no_data_views/index.tsx rename to packages/kbn-shared-ux-components/src/empty_state/no_data_views/index.tsx diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.test.tsx b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/no_data_views.component.test.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.test.tsx rename to packages/kbn-shared-ux-components/src/empty_state/no_data_views/no_data_views.component.test.tsx diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.tsx b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/no_data_views.component.tsx similarity index 89% rename from src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.tsx rename to packages/kbn-shared-ux-components/src/empty_state/no_data_views/no_data_views.component.tsx index bfab91ef03b26..bffa7c2a5269c 100644 --- a/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.tsx +++ b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/no_data_views.component.tsx @@ -23,7 +23,7 @@ export interface Props { emptyPromptColor?: EuiEmptyPromptProps['color']; } -const createDataViewText = i18n.translate('sharedUX.noDataViewsPage.addDataViewText', { +const createDataViewText = i18n.translate('sharedUXComponents.noDataViewsPage.addDataViewText', { defaultMessage: 'Create Data View', }); @@ -62,12 +62,12 @@ export const NoDataViews = ({ title={


@@ -75,7 +75,7 @@ export const NoDataViews = ({ body={

is rendered 1`] = ` } docLinks={ Object { - "dataViewsDocsLink": "dummy link", + "dataViewsDocLink": "dummy link", } } editors={ @@ -134,5 +134,5 @@ exports[` is rendered 1`] = ` - + `; diff --git a/src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.component.tsx b/packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.component.tsx similarity index 87% rename from src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.component.tsx rename to packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.component.tsx index 311f2236c34a0..6c037cc5f65cb 100644 --- a/src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.component.tsx +++ b/packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.component.tsx @@ -21,13 +21,19 @@ import cx from 'classnames'; import './exit_full_screen_button.scss'; -const text = i18n.translate('sharedUX.exitFullScreenButton.exitFullScreenModeButtonText', { - defaultMessage: 'Exit full screen', -}); +const text = i18n.translate( + 'sharedUXComponents.exitFullScreenButton.exitFullScreenModeButtonText', + { + defaultMessage: 'Exit full screen', + } +); -const description = i18n.translate('sharedUX.exitFullScreenButton.fullScreenModeDescription', { - defaultMessage: 'In full screen mode, press ESC to exit.', -}); +const description = i18n.translate( + 'sharedUXComponents.exitFullScreenButton.fullScreenModeDescription', + { + defaultMessage: 'In full screen mode, press ESC to exit.', + } +); /** * Props for the Exit Full Screen button component. diff --git a/src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.mdx b/packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.mdx similarity index 100% rename from src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.mdx rename to packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.mdx diff --git a/src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.scss b/packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.scss similarity index 100% rename from src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.scss rename to packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.scss diff --git a/src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.stories.tsx b/packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.stories.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.stories.tsx rename to packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.stories.tsx diff --git a/src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.test.tsx b/packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.test.tsx similarity index 89% rename from src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.test.tsx rename to packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.test.tsx index dc634810a5794..da205ddfbe377 100644 --- a/src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.test.tsx +++ b/packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.test.tsx @@ -10,18 +10,21 @@ import React from 'react'; import { mount as enzymeMount, ReactWrapper } from 'enzyme'; import { keys } from '@elastic/eui'; -import { ServicesProvider, SharedUXServices } from '../../services'; -import { servicesFactory } from '../../services/mocks'; +import { + SharedUxServicesProvider, + SharedUxServices, + mockServicesFactory, +} from '@kbn/shared-ux-services'; import { ExitFullScreenButton } from './exit_full_screen_button'; describe('', () => { - let services: SharedUXServices; + let services: SharedUxServices; let mount: (element: JSX.Element) => ReactWrapper; beforeEach(() => { - services = servicesFactory(); + services = mockServicesFactory(); mount = (element: JSX.Element) => - enzymeMount({element}); + enzymeMount({element}); }); afterEach(() => { diff --git a/src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.tsx b/packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.tsx similarity index 97% rename from src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.tsx rename to packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.tsx index 1f0b2f43a6b25..30deaaa86f4c5 100644 --- a/src/plugins/shared_ux/public/components/exit_full_screen_button/exit_full_screen_button.tsx +++ b/packages/kbn-shared-ux-components/src/exit_full_screen_button/exit_full_screen_button.tsx @@ -10,8 +10,9 @@ import React, { useCallback, useEffect } from 'react'; import { useEuiTheme, keys } from '@elastic/eui'; import { css } from '@emotion/react'; +import { usePlatformService } from '@kbn/shared-ux-services'; + import { ExitFullScreenButton as Component } from './exit_full_screen_button.component'; -import { usePlatformService } from '../../services'; /** * Props for the service-enabled Exit Full Screen button component. diff --git a/src/plugins/shared_ux/public/components/exit_full_screen_button/index.ts b/packages/kbn-shared-ux-components/src/exit_full_screen_button/index.ts similarity index 100% rename from src/plugins/shared_ux/public/components/exit_full_screen_button/index.ts rename to packages/kbn-shared-ux-components/src/exit_full_screen_button/index.ts diff --git a/src/plugins/shared_ux/public/components/index.ts b/packages/kbn-shared-ux-components/src/index.ts similarity index 89% rename from src/plugins/shared_ux/public/components/index.ts rename to packages/kbn-shared-ux-components/src/index.ts index 5a83f457dd34c..185d10f3b5235 100644 --- a/src/plugins/shared_ux/public/components/index.ts +++ b/packages/kbn-shared-ux-components/src/index.ts @@ -7,7 +7,7 @@ */ import React from 'react'; -import { withSuspense } from './utility'; +import { withSuspense } from '@kbn/shared-ux-utility'; /** * The Lazily-loaded `ExitFullScreenButton` component. Consumers should use `React.Suspennse` or the @@ -50,18 +50,18 @@ export { AddFromLibraryButton } from './toolbar/buttons/add_from_library/add_fro * The Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspennse` or the * `withSuspense` HOC to load this component. */ -export const LazyNoDataViewsPage = React.lazy(() => +export const LazyNoDataViews = React.lazy(() => import('./empty_state/no_data_views').then(({ NoDataViews }) => ({ default: NoDataViews, })) ); /** - * A `NoDataViewsPage` component that is wrapped by the `withSuspense` HOC. This component can - * be used directly by consumers and will load the `LazyNoDataViewsPage` component lazily with + * A `NoDataViews` component that is wrapped by the `withSuspense` HOC. This component can + * be used directly by consumers and will load the `LazyNoDataViews` component lazily with * a predefined fallback and error boundary. */ -export const NoDataViewsPage = withSuspense(LazyNoDataViewsPage); +export const NoDataViews = withSuspense(LazyNoDataViews); /** * The Lazily-loaded `IconButtonGroup` component. Consumers should use `React.Suspennse` or the diff --git a/src/plugins/shared_ux/public/types/mdx.d.ts b/packages/kbn-shared-ux-components/src/mdx.d.ts similarity index 100% rename from src/plugins/shared_ux/public/types/mdx.d.ts rename to packages/kbn-shared-ux-components/src/mdx.d.ts diff --git a/src/plugins/shared_ux/public/components/page_template/index.tsx b/packages/kbn-shared-ux-components/src/page_template/index.tsx similarity index 82% rename from src/plugins/shared_ux/public/components/page_template/index.tsx rename to packages/kbn-shared-ux-components/src/page_template/index.tsx index 0c4fb2e066bfc..adbfdce45be76 100644 --- a/src/plugins/shared_ux/public/components/page_template/index.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/index.tsx @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { NoDataCard, ElasticAgentCard } from './no_data_page/no_data_card'; +export { NoDataCard, ElasticAgentCard } from './no_data_page'; diff --git a/src/plugins/shared_ux/.storybook/main.ts b/packages/kbn-shared-ux-components/src/page_template/no_data_page/index.ts similarity index 81% rename from src/plugins/shared_ux/.storybook/main.ts rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/index.ts index 1261fe5a06f69..9b0ee88250422 100644 --- a/src/plugins/shared_ux/.storybook/main.ts +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/index.ts @@ -6,6 +6,4 @@ * Side Public License, v 1. */ -import { defaultConfig } from '@kbn/storybook'; - -module.exports = defaultConfig; +export { NoDataCard, ElasticAgentCard } from './no_data_card'; diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.component.test.tsx.snap b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.component.test.tsx.snap similarity index 100% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.component.test.tsx.snap rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.component.test.tsx.snap diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap similarity index 99% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap index dd8ae6993ecc6..76f1beb7ac481 100644 --- a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap @@ -5,7 +5,7 @@ exports[`ElasticAgentCard renders 1`] = ` max-width: 400px; } - - + `; diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap similarity index 100% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/assets/elastic_agent_card.svg b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/assets/elastic_agent_card.svg similarity index 100% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/assets/elastic_agent_card.svg rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/assets/elastic_agent_card.svg diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.component.test.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.test.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.component.test.tsx rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.test.tsx diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx similarity index 86% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx index 81b7b6bc61a2d..0bca3929f4c2d 100644 --- a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx @@ -22,25 +22,28 @@ export type ElasticAgentCardComponentProps = ElasticAgentCardProps & { }; const noPermissionTitle = i18n.translate( - 'sharedUX.noDataPage.elasticAgentCard.noPermission.title', + 'sharedUXComponents.noDataPage.elasticAgentCard.noPermission.title', { defaultMessage: `Contact your administrator`, } ); const noPermissionDescription = i18n.translate( - 'sharedUX.noDataPage.elasticAgentCard.noPermission.description', + 'sharedUXComponents.noDataPage.elasticAgentCard.noPermission.description', { defaultMessage: `This integration is not yet enabled. Your administrator has the required permissions to turn it on.`, } ); -const elasticAgentCardTitle = i18n.translate('sharedUX.noDataPage.elasticAgentCard.title', { - defaultMessage: 'Add Elastic Agent', -}); +const elasticAgentCardTitle = i18n.translate( + 'sharedUXComponents.noDataPage.elasticAgentCard.title', + { + defaultMessage: 'Add Elastic Agent', + } +); const elasticAgentCardDescription = i18n.translate( - 'sharedUX.noDataPage.elasticAgentCard.description', + 'sharedUXComponents.noDataPage.elasticAgentCard.description', { defaultMessage: `Use Elastic Agent for a simple, unified way to collect data from your machines.`, } diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx similarity index 92% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx index ceeb66cbd8cdd..3be77ad8d6dde 100644 --- a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx @@ -7,11 +7,12 @@ */ import React from 'react'; +import { applicationServiceFactory } from '@kbn/shared-ux-storybook'; + import { ElasticAgentCardComponent, ElasticAgentCardComponentProps, } from './elastic_agent_card.component'; -import { applicationServiceFactory } from '../../../../services/storybook/application'; export default { title: 'Elastic Agent Data Card', diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx similarity index 87% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx index fc9e72a011241..87196e041b2ab 100644 --- a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx @@ -8,21 +8,24 @@ import { ReactWrapper } from 'enzyme'; import React from 'react'; -import { ElasticAgentCard } from './elastic_agent_card'; - -import { servicesFactory } from '../../../../services/mocks'; -import { ServicesProvider, SharedUXServices } from '../../../../services'; import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { + SharedUxServicesProvider, + SharedUxServices, + mockServicesFactory, +} from '@kbn/shared-ux-services'; + +import { ElasticAgentCard } from './elastic_agent_card'; import { ElasticAgentCardComponent } from './elastic_agent_card.component'; describe('ElasticAgentCard', () => { - let services: SharedUXServices; + let services: SharedUxServices; let mount: (element: JSX.Element) => ReactWrapper; beforeEach(() => { - services = servicesFactory(); + services = mockServicesFactory(); mount = (element: JSX.Element) => - mountWithIntl({element}); + mountWithIntl({element}); }); afterEach(() => { diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.tsx similarity index 93% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.tsx rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.tsx index f99c4ec9196f5..d917514a96ccc 100644 --- a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/elastic_agent_card.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.tsx @@ -7,8 +7,9 @@ */ import React from 'react'; +import { useApplication, useHttp, usePermissions } from '@kbn/shared-ux-services'; + import { ElasticAgentCardProps } from './types'; -import { useApplication, useHttp, usePermissions } from '../../../../services'; import { ElasticAgentCardComponent } from './elastic_agent_card.component'; export const ElasticAgentCard = (props: ElasticAgentCardProps) => { diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/index.ts b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/index.ts similarity index 100% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/index.ts rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/index.ts diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.stories.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.stories.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.stories.tsx rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.stories.tsx diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.styles.ts b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.styles.ts similarity index 100% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.styles.ts rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.styles.ts diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.test.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.test.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.test.tsx rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.test.tsx diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.tsx similarity index 82% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.tsx rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.tsx index 2ea601757fbf7..4cba71bc09851 100644 --- a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.tsx @@ -9,16 +9,23 @@ import { i18n } from '@kbn/i18n'; import React, { FunctionComponent } from 'react'; import { EuiButton, EuiCard } from '@elastic/eui'; + import type { NoDataCardProps } from './types'; import { NoDataCardStyles } from './no_data_card.styles'; -const recommendedLabel = i18n.translate('sharedUX.pageTemplate.noDataPage.recommendedLabel', { - defaultMessage: 'Recommended', -}); +const recommendedLabel = i18n.translate( + 'sharedUXComponents.pageTemplate.noDataPage.recommendedLabel', + { + defaultMessage: 'Recommended', + } +); -const defaultDescription = i18n.translate('sharedUX.pageTemplate.noDataCard.description', { - defaultMessage: 'Proceed without collecting data', -}); +const defaultDescription = i18n.translate( + 'sharedUXComponents.pageTemplate.noDataCard.description', + { + defaultMessage: `Proceed without collecting data`, + } +); export const NoDataCard: FunctionComponent = ({ recommended, diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/types.ts b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/types.ts similarity index 100% rename from src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/types.ts rename to packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/types.ts diff --git a/src/plugins/shared_ux/public/components/redirect_app_links/click_handler.test.ts b/packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.test.ts similarity index 97% rename from src/plugins/shared_ux/public/components/redirect_app_links/click_handler.test.ts rename to packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.test.ts index 1569203c394df..dd26443eed171 100644 --- a/src/plugins/shared_ux/public/components/redirect_app_links/click_handler.test.ts +++ b/packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.test.ts @@ -7,7 +7,6 @@ */ import { MouseEvent } from 'react'; -import { ApplicationStart } from 'src/core/public'; import { createNavigateToUrlClickHandler } from './click_handler'; const createLink = ({ @@ -42,9 +41,11 @@ const createEvent = ({ } as unknown as MouseEvent; }; +type NavigateToURLFn = (url: string) => Promise; + describe('createNavigateToUrlClickHandler', () => { let container: HTMLElement; - let navigateToUrl: jest.MockedFunction; + let navigateToUrl: jest.MockedFunction; const createHandler = () => createNavigateToUrlClickHandler({ diff --git a/src/plugins/shared_ux/public/components/redirect_app_links/click_handler.ts b/packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.ts similarity index 87% rename from src/plugins/shared_ux/public/components/redirect_app_links/click_handler.ts rename to packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.ts index 89b057ffd0eaa..db2990726dc93 100644 --- a/src/plugins/shared_ux/public/components/redirect_app_links/click_handler.ts +++ b/packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.ts @@ -7,11 +7,10 @@ */ import React from 'react'; -import { ApplicationStart } from 'src/core/public'; -import { getClosestLink, hasActiveModifierKey } from '../utility/utils'; +import { getClosestLink, hasActiveModifierKey } from '@kbn/shared-ux-utility'; interface CreateCrossAppClickHandlerOptions { - navigateToUrl: ApplicationStart['navigateToUrl']; + navigateToUrl(url: string): Promise; container?: HTMLElement; } diff --git a/src/plugins/shared_ux/public/components/redirect_app_links/index.ts b/packages/kbn-shared-ux-components/src/redirect_app_links/index.ts similarity index 100% rename from src/plugins/shared_ux/public/components/redirect_app_links/index.ts rename to packages/kbn-shared-ux-components/src/redirect_app_links/index.ts diff --git a/src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.mdx b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.mdx similarity index 100% rename from src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.mdx rename to packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.mdx diff --git a/src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.stories.tsx b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.stories.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.stories.tsx rename to packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.stories.tsx diff --git a/src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.test.tsx b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.test.tsx similarity index 93% rename from src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.test.tsx rename to packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.test.tsx index d2cf5aa664cc6..d36bace70b7c8 100644 --- a/src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.test.tsx +++ b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.test.tsx @@ -8,18 +8,30 @@ import React, { MouseEvent } from 'react'; import { mount } from 'enzyme'; -import { applicationServiceMock } from '../../../../../core/public/mocks'; -import { RedirectAppLinks } from './redirect_app_links'; import { BehaviorSubject } from 'rxjs'; +import { RedirectAppLinks } from './redirect_app_links'; + +export type UnmountCallback = () => void; +export type MountPoint = (element: T) => UnmountCallback; + +const createServiceMock = () => { + const currentAppId$ = new BehaviorSubject('currentApp'); + + return { + currentAppId$: currentAppId$.asObservable(), + navigateToApp: jest.fn(), + navigateToUrl: jest.fn(), + }; +}; + /* eslint-disable jsx-a11y/click-events-have-key-events */ describe('RedirectAppLinks', () => { - let application: ReturnType; + let application = createServiceMock(); beforeEach(() => { - application = applicationServiceMock.createStartContract(); - application.currentAppId$ = new BehaviorSubject('currentApp'); + application = createServiceMock(); }); it('intercept click events on children link elements', () => { diff --git a/src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.tsx b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.tsx similarity index 76% rename from src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.tsx rename to packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.tsx index 5c2d9260639ca..e1d0bd4bed653 100644 --- a/src/plugins/shared_ux/public/components/redirect_app_links/redirect_app_links.tsx +++ b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.tsx @@ -6,17 +6,21 @@ * Side Public License, v 1. */ -import React, { FunctionComponent, useRef, useMemo } from 'react'; +import React, { useRef, useMemo } from 'react'; +import type { HTMLAttributes, DetailedHTMLProps, FC } from 'react'; import useObservable from 'react-use/lib/useObservable'; -import { ApplicationStart } from 'src/core/public'; -import { createNavigateToUrlClickHandler } from './click_handler'; +import { Observable } from 'rxjs'; -type Props = React.HTMLAttributes & - Pick; +import { createNavigateToUrlClickHandler } from './click_handler'; -export interface RedirectAppLinksProps extends Props { - className?: string; - 'data-test-subj'?: string; +type DivProps = DetailedHTMLProps, HTMLDivElement>; +/** + * TODO: this interface recreates props from the `ApplicationStart` interface. + * see: https://github.com/elastic/kibana/issues/127695 + */ +export interface RedirectAppLinksProps extends DivProps { + currentAppId$: Observable; + navigateToUrl(url: string): Promise; } /** @@ -36,7 +40,7 @@ export interface RedirectAppLinksProps extends Props { * require to handle the links. A good practice is to consider it as a context provider and to use it * at the root level of an application or of the page that require the feature. */ -export const RedirectAppLinks: FunctionComponent = ({ +export const RedirectAppLinks: FC = ({ navigateToUrl, currentAppId$, children, diff --git a/packages/kbn-shared-ux-components/src/svg.d.ts b/packages/kbn-shared-ux-components/src/svg.d.ts new file mode 100644 index 0000000000000..9169166fe7af9 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/svg.d.ts @@ -0,0 +1,13 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +declare module '*.svg' { + const content: string; + // eslint-disable-next-line import/no-default-export + export default content; +} diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap rename to packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/add_from_library.mdx b/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/add_from_library.mdx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/add_from_library.mdx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/add_from_library.mdx diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/add_from_library.stories.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/add_from_library.stories.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/add_from_library.stories.tsx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/add_from_library.stories.tsx diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/add_from_library.test.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/add_from_library.test.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/add_from_library.test.tsx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/add_from_library.test.tsx diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/add_from_library.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/add_from_library.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/add_from_library/add_from_library.tsx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/add_from_library.tsx diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap similarity index 98% rename from src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap rename to packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap index 9e0e13d628702..b49efda2a1cf7 100644 --- a/src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap @@ -16,7 +16,7 @@ exports[` is rendered 1`] = ` border-bottom-right-radius: 6px!important; } - is rendered 1`] = ` } docLinks={ Object { - "dataViewsDocsLink": "dummy link", + "dataViewsDocLink": "dummy link", } } editors={ @@ -212,5 +212,5 @@ exports[` is rendered 1`] = ` - + `; diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.mdx b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.mdx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.mdx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.mdx diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.stories.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.stories.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.stories.tsx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.stories.tsx diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.styles.ts b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.styles.ts similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.styles.ts rename to packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.styles.ts diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.test.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.test.tsx similarity index 77% rename from src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.test.tsx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.test.tsx index 751476bb5648c..aefcd59fbd7d5 100644 --- a/src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.test.tsx +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.test.tsx @@ -8,19 +8,22 @@ import React from 'react'; import { mount as enzymeMount, ReactWrapper } from 'enzyme'; +import { + mockServicesFactory, + SharedUxServices, + SharedUxServicesProvider, +} from '@kbn/shared-ux-services'; -import { ServicesProvider, SharedUXServices } from '../../../../services'; -import { servicesFactory } from '../../../../services/mocks'; import { IconButtonGroup } from './icon_button_group'; describe('', () => { - let services: SharedUXServices; + let services: SharedUxServices; let mount: (element: JSX.Element) => ReactWrapper; beforeEach(() => { - services = servicesFactory(); + services = mockServicesFactory(); mount = (element: JSX.Element) => - enzymeMount({element}); + enzymeMount({element}); }); test('is rendered', () => { diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/icon_button_group/icon_button_group.tsx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/icon_button_group.tsx diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap similarity index 94% rename from src/plugins/shared_ux/public/components/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap rename to packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap index eecbcee5d99eb..8e447f7a0ee5c 100644 --- a/src/plugins/shared_ux/public/components/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` is rendered 1`] = ` - is rendered 1`] = ` } docLinks={ Object { - "dataViewsDocsLink": "dummy link", + "dataViewsDocLink": "dummy link", } } editors={ @@ -88,5 +88,5 @@ exports[` is rendered 1`] = ` - + `; diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/primary/primary.mdx b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/primary/primary.mdx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/primary/primary.stories.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.stories.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/primary/primary.stories.tsx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.stories.tsx diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/primary/primary.test.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.test.tsx similarity index 78% rename from src/plugins/shared_ux/public/components/toolbar/buttons/primary/primary.test.tsx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.test.tsx index a1a22b7653893..3e0e153f453e5 100644 --- a/src/plugins/shared_ux/public/components/toolbar/buttons/primary/primary.test.tsx +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.test.tsx @@ -8,19 +8,23 @@ import { mount as enzymeMount, ReactWrapper } from 'enzyme'; import React from 'react'; -import { ServicesProvider, SharedUXServices } from '../../../../services'; -import { servicesFactory } from '../../../../services/mocks'; + +import { + SharedUxServicesProvider, + SharedUxServices, + mockServicesFactory, +} from '@kbn/shared-ux-services'; import { ToolbarButton } from './primary'; describe('', () => { - let services: SharedUXServices; + let services: SharedUxServices; let mount: (element: JSX.Element) => ReactWrapper; beforeEach(() => { - services = servicesFactory(); + services = mockServicesFactory(); mount = (element: JSX.Element) => - enzymeMount({element}); + enzymeMount({element}); }); test('is rendered', () => { diff --git a/src/plugins/shared_ux/public/components/toolbar/buttons/primary/primary.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/buttons/primary/primary.tsx rename to packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx diff --git a/src/plugins/shared_ux/public/components/toolbar/index.ts b/packages/kbn-shared-ux-components/src/toolbar/index.ts similarity index 100% rename from src/plugins/shared_ux/public/components/toolbar/index.ts rename to packages/kbn-shared-ux-components/src/toolbar/index.ts diff --git a/packages/kbn-shared-ux-components/tsconfig.json b/packages/kbn-shared-ux-components/tsconfig.json new file mode 100644 index 0000000000000..cca91264519cc --- /dev/null +++ b/packages/kbn-shared-ux-components/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node", + "@emotion/react/types/css-prop" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-shared-ux-services/BUILD.bazel b/packages/kbn-shared-ux-services/BUILD.bazel new file mode 100755 index 0000000000000..0345e98cd7123 --- /dev/null +++ b/packages/kbn-shared-ux-services/BUILD.bazel @@ -0,0 +1,126 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-shared-ux-services" +PKG_REQUIRE_NAME = "@kbn/shared-ux-services" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//react", + "@npm//rxjs", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/react", + "@npm//rxjs", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-shared-ux-services/README.mdx b/packages/kbn-shared-ux-services/README.mdx new file mode 100755 index 0000000000000..205c175000fb2 --- /dev/null +++ b/packages/kbn-shared-ux-services/README.mdx @@ -0,0 +1,203 @@ +--- +id: kibSharedUXServices +slug: /kibana-dev-docs/shared-ux/packages/kbn-shared-ux-services +title: Shared UX Services +summary: The `@kbn/shared-ux-services` package provides a thin service abstraction for components and solutions created by the Shared UX team. +date: 2022-03-11 +tags: ['kibana', 'dev', 'sharedUX'] +--- + +## About Shared UX Services + +This package contains a set of services that are used by Shared UX components and solutions. This package serves as a thin abstraction layer between Kibana dependencies and the components in Shared UX that use them. It also allows us to "swap out" different implementations of the interfaces for different environments, (e.g. Storybook, Jest, etc). This decouples the components from what could be complicated or heavily-dependent logic that is difficult to mock. + +## Implementations + +Several implementations of these interfaces exist: + +- `@kbn/shared-ux-services/src/services/stub`: A stub implementation free of dependencies, (and functionality). +- `@kbn/shared-ux-services/src/services/mock`: A Jest mock implementation used in `jest` tests. +- `@kbn/shared-ux-storybook/src/services/`: A Storybook implementation used in Storybook decorators and stories. +- `src/plugins/shared_ux/src/services/`: A Kibana implementation used in Kibana plugins. + +Other implementations could easily be written to support other environments. + +## Architecture + +Lots of components require access to the services provided by other plugins. When we identify a routine that relies on these dependencies, we can write a new method and add it to a namespace, (e.g. `platform`, `user`, etc). These namespaces become interfaces of simple methods stored in `@kbn/shared-ux-services`. From there, we can create implementations for each environment we support. + +Suppose we're creating a new service, `SharedUxFooService`: + +```ts +interface SharedUxFooService { + getFoo(): string; + setBar(bar: string): void; + isBaz(): boolean; +} +``` + +Once defined, we create factories to create those services. + +### Creating a `ServiceFactory` + +A `ServiceFactory` is a simple type that describes 1/ what service is being created, and 2/ what parameters are required to create that service for a given environment. + +### Stub and Mock Factories + +Given the service definition above, we can create a `ServiceFactory` for a stubbed service that gives the bare minimum of functionality: + +```ts +/** + * A factory function for creating a stubbed implementation of `SharedUxFooService`. + */ +export type FooServiceFactory = ServiceFactory; + +/** + * A factory function for creating a stubbed implementation of `SharedUxFooService`. + */ +export const fooServiceFactory: FooServiceFactory = () => ({ + getFoo: () => 'foo', + setBar: () => {}, + isBaz: () => false, +}); +``` + +We can also create a mock for Jest: + +```ts +/** + * A factory function for creating a mock implementation of `SharedUxFooService`. + */ +export type FooServiceFactory = ServiceFactory; + +/** + * A factory function for creating a stubbed implementation of `SharedUxFooService`. + */ +export const fooServiceFactory: FooServiceFactory = () => ({ + getFoo: () => jest.fn(), + setBar: () => jest.fn(), + isBaz: () => jest.fn(), +}); +``` + +### Storybook Factories + +Storybook is where we can begin to take advantage of `Parameters` for a given service. Since stories can use controls to provide parameters, we can create a `ServiceFactory` that uses the `Parameters` generic and returns a `SharedUxFooService` that uses their values. + +```ts +import { action } from '@storybook/addon-actions'; + +interface FooServiceStorybookParameters { + foo: string; + baz: boolean; +} + +/** + * A factory function for creating a Storybook implementation of `SharedUxFooService`. + */ +export type FooServiceFactory = ServiceFactory; + +/** + * A factory function for creating a stubbed implementation of `SharedUxFooService`. + */ +export const fooServiceFactory: FooServiceFactory = ({ foo, baz }) => ({ + getFoo: () => foo, + setBar: () => action('setBar'), + isBaz: () => baz, +}); +``` + +A story can then optionally provide values for those parameters as part of its controls. + +```ts +type Params = Pick; + +export const ComponentStory = ({ foo }: Params) => { + const service = fooServiceFactory({ foo, baz: false }); + + return ( + ; +}; + +PureComponent.argTypes = { + foo: { + options: ['alpha', 'beta', 'gamma', 'delta'], + control: { type: 'radio' }, + }, +}; +``` + +### Kibana Factories + +Using these services in Kibana is a bit more complex, but is still relatively simple. First, we define what dependencies we'll need, (we use this interface in `src/plugins/shared_ux` as it relies on types found only in plugins, where packages cannot use them): + +```ts +/** + * Parameters necessary to create a Kibana-based service, (e.g. during Plugin + * startup or setup). + * + * The `Start` generic refers to the specific Plugin `TPluginsStart`. + */ +export interface KibanaPluginServiceParams { + coreStart: CoreStart; + startPlugins: Start; + appUpdater?: BehaviorSubject; + initContext?: PluginInitializerContext; +} + +/** + * A factory function for creating a Kibana-based service. + * + * The `Service` generic determines the shape of the Service being produced. + * The `Start` generic refers to the specific Plugin `TPluginsStart`. + */ +export type KibanaPluginServiceFactory = ( + params: KibanaPluginServiceParams +) => Service; +``` + +From there, a plugin might have a collection of dependencies on core or other plugins: + +```ts +export interface MyPluginStartDeps { + bar: BarPluginStart; + baz: BazPluginStart; +} +``` + +We'd then use this dependency interface to create a `ServiceFactory` for our service in Kibana: + +```ts +export type FooServiceFactory = KibanaPluginServiceFactory< + SharedUxFooService, + MyPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUxFooService`. + */ +export const fooServiceFactory: FooServiceFactory = ({ coreStart, startPlugins }) => ({ + getFoo: startPlugins.bar.getSomeOtherFoo, + setBar: startPlugins.baz.setHappyPathBar, + isBaz: () => { + return coreStart.uiSettings.get('someSetting') === 'expectedValue'; + } +}); +``` + +From there, the pattern is the same: invoke the service factory with the required dependencies and provide them to the `SharedUxServicesContext` Provider: + +```ts + +// plugin.tsx +public start(coreStart: CoreStart, startPlugins: SharedUXPluginStartDeps): SharedUXPluginStart { + const service = fooServiceFactory({ coreStart, startPlugins }); + const Context = {children}; + + // ...wrap React content with the context.. +} +``` + +## Use in Kibana plugins + +In order to make consumption of these services easy by Kibana plugins, `src/plugins/shared_ux` provides a pre-wired `SharedUxServicesProvider` component as part of the `start` lifecycle. Plugins can simply make `sharedUX` a dependency and wrap their solution root or any component. See the documentation for `sharedUX` for more details. diff --git a/packages/kbn-shared-ux-services/jest.config.js b/packages/kbn-shared-ux-services/jest.config.js new file mode 100755 index 0000000000000..f1ef008d0f62d --- /dev/null +++ b/packages/kbn-shared-ux-services/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-shared-ux-services'], +}; diff --git a/packages/kbn-shared-ux-services/package.json b/packages/kbn-shared-ux-services/package.json new file mode 100755 index 0000000000000..7d7d999bf6961 --- /dev/null +++ b/packages/kbn-shared-ux-services/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-services", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-shared-ux-services/src/context.tsx b/packages/kbn-shared-ux-services/src/context.tsx new file mode 100644 index 0000000000000..55d05c57a08fb --- /dev/null +++ b/packages/kbn-shared-ux-services/src/context.tsx @@ -0,0 +1,65 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import React, { FC, createContext, useContext } from 'react'; + +import type { SharedUxServices } from './types'; + +// The React Context used to provide the services to the SharedUX components. +const SharedUxServicesContext = createContext(null); + +/** + * The `React.Context` Provider component for the `SharedUxServices` context. Any + * plugin or environment that consumes SharedUX components needs to wrap their React + * tree with this provider. + * + * Within a plugin, you can use use the Shared UX plugin and retrieve a fully-configured + * context from the `start` contract. + */ +export const SharedUxServicesProvider: FC = ({ children, ...services }) => ( + {children} +); + +/** + * React hook for accessing pre-wired `SharedUxServices`. + */ +export function useSharedUxServices() { + const context = useContext(SharedUxServicesContext); + + if (!context) { + throw new Error( + 'SharedUxServicesContext missing. Ensure your component or React root is wrapped with SharedUxServicesProvider.' + ); + } + + return context; +} + +/** + * React hook for accessing the pre-wired `SharedUxPlatformService`. + */ +export const usePlatformService = () => useSharedUxServices().platform; + +/** + * React hook for accessing the pre-wired `SharedUxPermissionsService`. + */ +export const usePermissions = () => useSharedUxServices().permissions; + +/** + * React hook for accessing the pre-wired `SharedUxEditorsService`. + */ +export const useEditors = () => useSharedUxServices().editors; + +/** + * React hook for accessing the pre-wired `SharedUxDocLinksService`. + */ +export const useDocLinks = () => useSharedUxServices().docLinks; + +export const useHttp = () => useSharedUxServices().http; + +export const useApplication = () => useSharedUxServices().application; diff --git a/packages/kbn-shared-ux-services/src/index.ts b/packages/kbn-shared-ux-services/src/index.ts new file mode 100755 index 0000000000000..d9ea2c670c9c5 --- /dev/null +++ b/packages/kbn-shared-ux-services/src/index.ts @@ -0,0 +1,35 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export type { ServiceFactory, SharedUxServices, SharedUxServicesContext } from './types'; +export type { + SharedUxApplicationService, + SharedUxDocLinksService, + SharedUxEditorsService, + SharedUxHttpService, + SharedUxPlatformService, + SharedUxUserPermissionsService, +} from './services'; + +export { + SharedUxServicesProvider, + useApplication, + useDocLinks, + useEditors, + useHttp, + usePermissions, + usePlatformService, + useSharedUxServices, +} from './context'; + +export { + mockServiceFactories, + mockServicesFactory, + stubServiceFactories, + stubServicesFactory, +} from './services'; diff --git a/packages/kbn-shared-ux-services/src/services/application.ts b/packages/kbn-shared-ux-services/src/services/application.ts new file mode 100644 index 0000000000000..be2ca2f2730cb --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/application.ts @@ -0,0 +1,14 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { Observable } from 'rxjs'; + +export interface SharedUxApplicationService { + navigateToUrl: (url: string) => Promise; + currentAppId$: Observable; +} diff --git a/packages/kbn-shared-ux-services/src/services/doc_links.ts b/packages/kbn-shared-ux-services/src/services/doc_links.ts new file mode 100644 index 0000000000000..3b8eb4748d76f --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/doc_links.ts @@ -0,0 +1,15 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +/** + * A service providing links to documentation about various features in Kibana. + */ +export interface SharedUxDocLinksService { + /** A link to information about Data Views in Kibana */ + dataViewsDocLink: string; +} diff --git a/packages/kbn-shared-ux-services/src/services/editors.ts b/packages/kbn-shared-ux-services/src/services/editors.ts new file mode 100644 index 0000000000000..4dc5b7d9bc269 --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/editors.ts @@ -0,0 +1,36 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +/** + * TODO: `DataView` is a class exported by `src/plugins/data_views/public`. Since this service + * is contained in this package-- and packages can only depend on other packages and never on + * plugins-- we have to set this to `unknown`. If and when `DataView` is exported from a + * stateless package, we can remove this. + * + * @see: https://github.com/elastic/kibana/issues/127695 + */ +type DataView = unknown; + +/** + * A subset of the `DataViewEditorOptions` interface relevant to our service and components. + * + * @see: src/plugins/data_view_editor/public/types.ts + */ +interface DataViewEditorOptions { + /** Handler to be invoked when the Data View Editor completes a save operation. */ + onSave: (dataView: DataView) => void; +} + +/** + * A service providing methods to invoke and interact with various editors provided + * in Kibana. + */ +export interface SharedUxEditorsService { + /** A method to open the Data View Editor flow. */ + openDataViewEditor: (options: DataViewEditorOptions) => () => void; +} diff --git a/packages/kbn-shared-ux-services/src/services/http.ts b/packages/kbn-shared-ux-services/src/services/http.ts new file mode 100644 index 0000000000000..024222092b7b1 --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/http.ts @@ -0,0 +1,11 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export interface SharedUxHttpService { + addBasePath: (url: string) => string; +} diff --git a/packages/kbn-shared-ux-services/src/services/index.ts b/packages/kbn-shared-ux-services/src/services/index.ts new file mode 100644 index 0000000000000..485264353feb2 --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/index.ts @@ -0,0 +1,17 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export type { SharedUxApplicationService } from './application'; +export type { SharedUxDocLinksService } from './doc_links'; +export type { SharedUxEditorsService } from './editors'; +export type { SharedUxHttpService } from './http'; +export type { SharedUxUserPermissionsService } from './permissions'; +export type { SharedUxPlatformService } from './platform'; + +export { mockServicesFactory, mockServiceFactories } from './mock'; +export { stubServicesFactory, stubServiceFactories } from './stub'; diff --git a/src/plugins/shared_ux/public/services/mocks/application.mock.ts b/packages/kbn-shared-ux-services/src/services/mock/application.mock.ts similarity index 76% rename from src/plugins/shared_ux/public/services/mocks/application.mock.ts rename to packages/kbn-shared-ux-services/src/services/mock/application.mock.ts index c2010956401e8..abb68a1edb642 100644 --- a/src/plugins/shared_ux/public/services/mocks/application.mock.ts +++ b/packages/kbn-shared-ux-services/src/services/mock/application.mock.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXApplicationService } from '../application'; import { Observable } from 'rxjs'; +import { ServiceFactory } from '../../types'; +import { SharedUxApplicationService } from '../application'; -export type MockApplicationServiceFactory = PluginServiceFactory; +export type MockApplicationServiceFactory = ServiceFactory; /** * A factory function for creating a Jest-based implementation of `SharedUXApplicationService`. diff --git a/src/plugins/shared_ux/public/services/mocks/doc_links.mock.ts b/packages/kbn-shared-ux-services/src/services/mock/doc_links.mock.ts similarity index 59% rename from src/plugins/shared_ux/public/services/mocks/doc_links.mock.ts rename to packages/kbn-shared-ux-services/src/services/mock/doc_links.mock.ts index 28cfa14c50d28..378ead63fbc9f 100644 --- a/src/plugins/shared_ux/public/services/mocks/doc_links.mock.ts +++ b/packages/kbn-shared-ux-services/src/services/mock/doc_links.mock.ts @@ -6,14 +6,17 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXDocLinksService } from '../doc_links'; +import type { ServiceFactory } from '../../types'; +import type { SharedUxDocLinksService } from '../doc_links'; -export type MockDockLinksServiceFactory = PluginServiceFactory; +/** + * A factory function for creating a Jest implementation of `SharedUxDocLinksService`. + */ +export type MockDockLinksServiceFactory = ServiceFactory; /** - * A factory function for creating a Jest-based implementation of `SharedUXDocLinksService`. + * A factory function for creating a Jest-based implementation of `SharedUxDocLinksService`. */ export const docLinksServiceFactory: MockDockLinksServiceFactory = () => ({ - dataViewsDocsLink: 'dummy link', + dataViewsDocLink: 'dummy link', }); diff --git a/src/plugins/shared_ux/public/services/mocks/editors.mock.ts b/packages/kbn-shared-ux-services/src/services/mock/editors.mock.ts similarity index 62% rename from src/plugins/shared_ux/public/services/mocks/editors.mock.ts rename to packages/kbn-shared-ux-services/src/services/mock/editors.mock.ts index 28a89d5326d62..80742f15d93cd 100644 --- a/src/plugins/shared_ux/public/services/mocks/editors.mock.ts +++ b/packages/kbn-shared-ux-services/src/services/mock/editors.mock.ts @@ -6,13 +6,16 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXEditorsService } from '../editors'; +import type { ServiceFactory } from '../../types'; +import type { SharedUxEditorsService } from '../editors'; -export type MockEditorsServiceFactory = PluginServiceFactory; +/** + * A factory function for creating a Jest-based implementation of `SharedUxEditorsService`. + */ +export type MockEditorsServiceFactory = ServiceFactory; /** - * A factory function for creating a Jest-based implementation of `SharedUXEditorsService`. + * A factory function for creating a Jest-based implementation of `SharedUxEditorsService`. */ export const editorsServiceFactory: MockEditorsServiceFactory = () => ({ openDataViewEditor: jest.fn(), diff --git a/src/plugins/shared_ux/public/services/mocks/http.mock.ts b/packages/kbn-shared-ux-services/src/services/mock/http.mock.ts similarity index 77% rename from src/plugins/shared_ux/public/services/mocks/http.mock.ts rename to packages/kbn-shared-ux-services/src/services/mock/http.mock.ts index 1751f2b77efc1..0ef07526e5116 100644 --- a/src/plugins/shared_ux/public/services/mocks/http.mock.ts +++ b/packages/kbn-shared-ux-services/src/services/mock/http.mock.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXHttpService } from '../http'; +import { ServiceFactory } from '../../types'; +import { SharedUxHttpService } from '../http'; -export type MockHttpServiceFactory = PluginServiceFactory; +export type MockHttpServiceFactory = ServiceFactory; /** * A factory function for creating a Jest-based implementation of `SharedUXHttpService`. diff --git a/src/plugins/shared_ux/public/services/mocks/index.ts b/packages/kbn-shared-ux-services/src/services/mock/index.ts similarity index 51% rename from src/plugins/shared_ux/public/services/mocks/index.ts rename to packages/kbn-shared-ux-services/src/services/mock/index.ts index 8fb4372295d76..2fc049a84aeb4 100644 --- a/src/plugins/shared_ux/public/services/mocks/index.ts +++ b/packages/kbn-shared-ux-services/src/services/mock/index.ts @@ -5,27 +5,50 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { docLinksServiceFactory } from './doc_links.mock'; -export type { MockPlatformServiceFactory } from './platform.mock'; -export { platformServiceFactory } from './platform.mock'; +import type { SharedUxServices, ServiceFactory } from '../../types'; -import type { SharedUXServices } from '../.'; -import { PluginServiceFactory } from '../types'; -import { platformServiceFactory } from './platform.mock'; -import { userPermissionsServiceFactory } from './permissions.mock'; +import { applicationServiceFactory } from './application.mock'; +import { docLinksServiceFactory } from './doc_links.mock'; import { editorsServiceFactory } from './editors.mock'; import { httpServiceFactory } from './http.mock'; -import { applicationServiceFactory } from './application.mock'; +import { userPermissionsServiceFactory } from './permissions.mock'; +import { platformServiceFactory } from './platform.mock'; + +export type { MockApplicationServiceFactory } from './application.mock'; +export type { MockDockLinksServiceFactory } from './doc_links.mock'; +export type { MockEditorsServiceFactory } from './editors.mock'; +export type { MockHttpServiceFactory } from './http.mock'; +export type { MockUserPermissionsServiceFactory } from './permissions.mock'; +export type { MockPlatformServiceFactory } from './platform.mock'; + +export { applicationServiceFactory } from './application.mock'; +export { docLinksServiceFactory } from './doc_links.mock'; +export { editorsServiceFactory } from './editors.mock'; +export { httpServiceFactory } from './http.mock'; +export { userPermissionsServiceFactory } from './permissions.mock'; +export { platformServiceFactory } from './platform.mock'; /** - * A factory function for creating a Jest-based implementation of `SharedUXServices`. + * A factory function for creating a Jest-based implementation of `SharedUxServices`. */ -export const servicesFactory: PluginServiceFactory = () => ({ - platform: platformServiceFactory(), - permissions: userPermissionsServiceFactory(), - editors: editorsServiceFactory(), +export const mockServicesFactory: ServiceFactory = () => ({ + application: applicationServiceFactory(), docLinks: docLinksServiceFactory(), + editors: editorsServiceFactory(), http: httpServiceFactory(), - application: applicationServiceFactory(), + permissions: userPermissionsServiceFactory(), + platform: platformServiceFactory(), }); + +/** + * A collection of mock Service Factories. + */ +export const mockServiceFactories = { + applicationServiceFactory, + docLinksServiceFactory, + editorsServiceFactory, + httpServiceFactory, + platformServiceFactory, + userPermissionsServiceFactory, +}; diff --git a/src/plugins/shared_ux/public/services/mocks/permissions.mock.ts b/packages/kbn-shared-ux-services/src/services/mock/permissions.mock.ts similarity index 61% rename from src/plugins/shared_ux/public/services/mocks/permissions.mock.ts rename to packages/kbn-shared-ux-services/src/services/mock/permissions.mock.ts index 4884d5071ec43..3fb5e78a5b839 100644 --- a/src/plugins/shared_ux/public/services/mocks/permissions.mock.ts +++ b/packages/kbn-shared-ux-services/src/services/mock/permissions.mock.ts @@ -6,14 +6,16 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXUserPermissionsService } from '../permissions'; +import type { ServiceFactory } from '../../types'; +import type { SharedUxUserPermissionsService } from '../permissions'; -export type MockUserPermissionsServiceFactory = - PluginServiceFactory; +/** + * A factory function for creating a Jest-based implementation of `SharedUxUserPermissionsService`. + */ +export type MockUserPermissionsServiceFactory = ServiceFactory; /** - * A factory function for creating a Jest-based implementation of `SharedUXUserPermissionsService`. + * A factory function for creating a Jest-based implementation of `SharedUxUserPermissionsService`. */ export const userPermissionsServiceFactory: MockUserPermissionsServiceFactory = () => ({ canCreateNewDataView: true, diff --git a/src/plugins/shared_ux/public/services/mocks/platform.mock.ts b/packages/kbn-shared-ux-services/src/services/mock/platform.mock.ts similarity index 70% rename from src/plugins/shared_ux/public/services/mocks/platform.mock.ts rename to packages/kbn-shared-ux-services/src/services/mock/platform.mock.ts index c36d63cfcacbe..8e6ec205d2856 100644 --- a/src/plugins/shared_ux/public/services/mocks/platform.mock.ts +++ b/packages/kbn-shared-ux-services/src/services/mock/platform.mock.ts @@ -6,16 +6,16 @@ * Side Public License, v 1. */ -import type { PluginServiceFactory } from '../types'; -import type { SharedUXPlatformService } from '../platform'; +import type { ServiceFactory } from '../../types'; +import type { SharedUxPlatformService } from '../platform'; /** - * A factory function for creating a Jest-based implementation of `SharedUXPlatformService`. + * A factory function for creating a Jest-based implementation of `SharedUxPlatformService`. */ -export type MockPlatformServiceFactory = PluginServiceFactory; +export type MockPlatformServiceFactory = ServiceFactory; /** - * A factory function for creating a Jest-based implementation of `SharedUXPlatformService`. + * A factory function for creating a Jest-based implementation of `SharedUxPlatformService`. */ export const platformServiceFactory: MockPlatformServiceFactory = () => ({ setIsFullscreen: jest.fn(), diff --git a/packages/kbn-shared-ux-services/src/services/permissions.ts b/packages/kbn-shared-ux-services/src/services/permissions.ts new file mode 100644 index 0000000000000..d6fcabd8ccedf --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/permissions.ts @@ -0,0 +1,16 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +/** + * A service providing permissions information, typically for the current user. + */ +export interface SharedUxUserPermissionsService { + /** True if the user has permission to create a new Data View, false otherwise. */ + canCreateNewDataView: boolean; + canAccessFleet: boolean; +} diff --git a/packages/kbn-shared-ux-services/src/services/platform.ts b/packages/kbn-shared-ux-services/src/services/platform.ts new file mode 100644 index 0000000000000..83148abac8643 --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/platform.ts @@ -0,0 +1,24 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +/** + * A service providing methods to interact with the platform in which this code is + * running, (almost always Kibana). + * + * Rather than provide the entire `CoreStart` contract to components, we provide simplified + * abstractions around a use case specific to Shared UX. This way, we know exactly how the + * `CoreStart` and other plugins are used. This makes mocking and refactoring easier when + * upstream dependencies change. + */ +export interface SharedUxPlatformService { + /** + * Sets the fullscreen state of the chrome. + * @param isFullscreen True if the chrome should be fullscreen, false otherwise. + */ + setIsFullscreen: (isFullscreen: boolean) => void; +} diff --git a/src/plugins/shared_ux/public/services/stub/application.ts b/packages/kbn-shared-ux-services/src/services/stub/application.ts similarity index 80% rename from src/plugins/shared_ux/public/services/stub/application.ts rename to packages/kbn-shared-ux-services/src/services/stub/application.ts index 25ad680a48073..72c1f3db30089 100644 --- a/src/plugins/shared_ux/public/services/stub/application.ts +++ b/packages/kbn-shared-ux-services/src/services/stub/application.ts @@ -7,10 +7,10 @@ */ import { Observable } from 'rxjs'; -import { PluginServiceFactory } from '../types'; -import { SharedUXApplicationService } from '../application'; +import { ServiceFactory } from '../../types'; +import { SharedUxApplicationService } from '../application'; -export type ApplicationServiceFactory = PluginServiceFactory; +export type ApplicationServiceFactory = ServiceFactory; /** * A factory function for creating for creating a simple stubbed implementation of `SharedUXApplicationService`. diff --git a/src/plugins/shared_ux/public/services/stub/doc_links.ts b/packages/kbn-shared-ux-services/src/services/stub/doc_links.ts similarity index 52% rename from src/plugins/shared_ux/public/services/stub/doc_links.ts rename to packages/kbn-shared-ux-services/src/services/stub/doc_links.ts index 424a24c578539..6d22eed944521 100644 --- a/src/plugins/shared_ux/public/services/stub/doc_links.ts +++ b/packages/kbn-shared-ux-services/src/services/stub/doc_links.ts @@ -6,14 +6,17 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXDocLinksService } from '../doc_links'; +import type { ServiceFactory } from '../../types'; +import type { SharedUxDocLinksService } from '../doc_links'; -export type DockLinksServiceFactory = PluginServiceFactory; +/** + * A factory function for creating a stubbed implementation of `SharedUxDocLinksService`. + */ +export type DockLinksServiceFactory = ServiceFactory; /** - * A factory function for creating a Jest-based implementation of `SharedUXDocLinksService`. + * A factory function for creating a stubbed implementation of `SharedUxDocLinksService`. */ export const docLinksServiceFactory: DockLinksServiceFactory = () => ({ - dataViewsDocsLink: 'docs', + dataViewsDocLink: 'docs', }); diff --git a/src/plugins/shared_ux/public/services/stub/editors.ts b/packages/kbn-shared-ux-services/src/services/stub/editors.ts similarity index 71% rename from src/plugins/shared_ux/public/services/stub/editors.ts rename to packages/kbn-shared-ux-services/src/services/stub/editors.ts index 03fea5e6c98b5..545539d873941 100644 --- a/src/plugins/shared_ux/public/services/stub/editors.ts +++ b/packages/kbn-shared-ux-services/src/services/stub/editors.ts @@ -6,16 +6,16 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXEditorsService } from '../editors'; +import type { ServiceFactory } from '../../types'; +import type { SharedUxEditorsService } from '../editors'; /** - * A factory function for creating a simple stubbed implementation of `SharedUXEditorsService`. + * A factory function for creating a simple stubbed implementation of `SharedUxEditorsService`. */ -export type EditorsServiceFactory = PluginServiceFactory; +export type EditorsServiceFactory = ServiceFactory; /** - * A factory function for creating a simple stubbed implementation of `SharedUXEditorsService`. + * A factory function for creating a simple stubbed implementation of `SharedUxEditorsService`. */ export const editorsServiceFactory: EditorsServiceFactory = () => ({ openDataViewEditor: () => () => {}, diff --git a/src/plugins/shared_ux/public/services/stub/http.ts b/packages/kbn-shared-ux-services/src/services/stub/http.ts similarity index 79% rename from src/plugins/shared_ux/public/services/stub/http.ts rename to packages/kbn-shared-ux-services/src/services/stub/http.ts index 4fb101d4e85f6..08dae62ecdb7f 100644 --- a/src/plugins/shared_ux/public/services/stub/http.ts +++ b/packages/kbn-shared-ux-services/src/services/stub/http.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXHttpService } from '../http'; +import { ServiceFactory } from '../../types'; +import { SharedUxHttpService } from '../http'; /** * A factory function for creating a simple stubbed implementation of `SharedUXHttpService`. */ -export type HttpServiceFactory = PluginServiceFactory; +export type HttpServiceFactory = ServiceFactory; /** * A factory function for creating a simple stubbed implementation of `SharedUXHttpService`. diff --git a/src/plugins/shared_ux/public/services/stub/index.ts b/packages/kbn-shared-ux-services/src/services/stub/index.ts similarity index 65% rename from src/plugins/shared_ux/public/services/stub/index.ts rename to packages/kbn-shared-ux-services/src/services/stub/index.ts index 0a8a0b7bd3daf..10323b7db8762 100644 --- a/src/plugins/shared_ux/public/services/stub/index.ts +++ b/packages/kbn-shared-ux-services/src/services/stub/index.ts @@ -6,23 +6,35 @@ * Side Public License, v 1. */ -import type { SharedUXServices } from '../.'; -import { PluginServiceFactory } from '../types'; -import { platformServiceFactory } from './platform'; -import { userPermissionsServiceFactory } from './permissions'; -import { editorsServiceFactory } from './editors'; +import type { SharedUxServices, ServiceFactory } from '../../types'; + +import { applicationServiceFactory } from './application'; import { docLinksServiceFactory } from './doc_links'; +import { editorsServiceFactory } from './editors'; import { httpServiceFactory } from './http'; -import { applicationServiceFactory } from './application'; +import { platformServiceFactory } from './platform'; +import { userPermissionsServiceFactory } from './permissions'; /** - * A factory function for creating a simple stubbed implemetation of `SharedUXServices`. + * A factory function for creating simple stubbed implementations of all `SharedUxServices`. */ -export const servicesFactory: PluginServiceFactory = () => ({ - platform: platformServiceFactory(), - permissions: userPermissionsServiceFactory(), - editors: editorsServiceFactory(), +export const stubServicesFactory: ServiceFactory = () => ({ + application: applicationServiceFactory(), docLinks: docLinksServiceFactory(), + editors: editorsServiceFactory(), http: httpServiceFactory(), - application: applicationServiceFactory(), + permissions: userPermissionsServiceFactory(), + platform: platformServiceFactory(), }); + +/** + * A collection of stubbed service factories. + */ +export const stubServiceFactories = { + applicationServiceFactory, + docLinksServiceFactory, + editorsServiceFactory, + httpServiceFactory, + platformServiceFactory, + userPermissionsServiceFactory, +}; diff --git a/src/plugins/shared_ux/public/services/stub/permissions.ts b/packages/kbn-shared-ux-services/src/services/stub/permissions.ts similarity index 68% rename from src/plugins/shared_ux/public/services/stub/permissions.ts rename to packages/kbn-shared-ux-services/src/services/stub/permissions.ts index 2b9e9f4a8f409..e5588f818f82a 100644 --- a/src/plugins/shared_ux/public/services/stub/permissions.ts +++ b/packages/kbn-shared-ux-services/src/services/stub/permissions.ts @@ -6,16 +6,16 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXUserPermissionsService } from '../permissions'; +import type { ServiceFactory } from '../../types'; +import type { SharedUxUserPermissionsService } from '../permissions'; /** - * A factory function for creating a simple stubbed implementation of `SharedUXUserPermissionsService`. + * A factory function for creating a simple stubbed implementation of `SharedUxUserPermissionsService`. */ -export type UserPermissionsServiceFactory = PluginServiceFactory; +export type UserPermissionsServiceFactory = ServiceFactory; /** - * A factory function for creating a simple stubbed implementation of `SharedUXUserPermissionsService`. + * A factory function for creating a simple stubbed implementation of `SharedUxUserPermissionsService`. */ export const userPermissionsServiceFactory: UserPermissionsServiceFactory = () => ({ canCreateNewDataView: true, diff --git a/src/plugins/shared_ux/public/services/stub/platform.ts b/packages/kbn-shared-ux-services/src/services/stub/platform.ts similarity index 70% rename from src/plugins/shared_ux/public/services/stub/platform.ts rename to packages/kbn-shared-ux-services/src/services/stub/platform.ts index 90fa8edb3e06e..2e31238347307 100644 --- a/src/plugins/shared_ux/public/services/stub/platform.ts +++ b/packages/kbn-shared-ux-services/src/services/stub/platform.ts @@ -6,16 +6,16 @@ * Side Public License, v 1. */ -import { PluginServiceFactory } from '../types'; -import { SharedUXPlatformService } from '../platform'; +import type { ServiceFactory } from '../../types'; +import type { SharedUxPlatformService } from '../platform'; /** - * A factory function for creating a simple stubbed implementation of `SharedUXPlatformService`. + * A factory function for creating a simple stubbed implementation of `SharedUxPlatformService`. */ -export type PlatformServiceFactory = PluginServiceFactory; +export type PlatformServiceFactory = ServiceFactory; /** - * A factory function for creating a simple stubbed implementation of `SharedUXPlatformService`. + * A factory function for creating a simple stubbed implementation of `SharedUxPlatformService`. */ export const platformServiceFactory: PlatformServiceFactory = () => ({ setIsFullscreen: (_isFullscreen) => {}, diff --git a/packages/kbn-shared-ux-services/src/types.ts b/packages/kbn-shared-ux-services/src/types.ts new file mode 100755 index 0000000000000..2bfbafb880e80 --- /dev/null +++ b/packages/kbn-shared-ux-services/src/types.ts @@ -0,0 +1,50 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { FC } from 'react'; + +import { + SharedUxApplicationService, + SharedUxDocLinksService, + SharedUxEditorsService, + SharedUxHttpService, + SharedUxPlatformService, + SharedUxUserPermissionsService, +} from './services'; + +/** + * A collection of services utilized by SharedUX. This serves as a thin + * abstraction layer between services provided by Kibana and other plugins + * while allowing this plugin to be developed independently of those contracts. + * + * It also allows us to "swap out" differenct implementations of these services + * for different environments, (e.g. Jest, Storybook, etc.) + */ +export interface SharedUxServices { + application: SharedUxApplicationService; + docLinks: SharedUxDocLinksService; + editors: SharedUxEditorsService; + http: SharedUxHttpService; + permissions: SharedUxUserPermissionsService; + platform: SharedUxPlatformService; +} + +/** + * A type representing a component that provides the `SharedUxServices` through a + * React Context. + */ +export type SharedUxServicesContext = FC<{}>; + +/** + * A factory function for creating one or more services. + * + * The `S` generic determines the shape of the API being produced. + * The `Parameters` generic determines what parameters are expected to + * create the service. + */ +export type ServiceFactory = (params: Parameters) => S; diff --git a/packages/kbn-shared-ux-services/tsconfig.json b/packages/kbn-shared-ux-services/tsconfig.json new file mode 100755 index 0000000000000..a8cfc2cceb08b --- /dev/null +++ b/packages/kbn-shared-ux-services/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-shared-ux-storybook/BUILD.bazel b/packages/kbn-shared-ux-storybook/BUILD.bazel new file mode 100644 index 0000000000000..3624a708b6cef --- /dev/null +++ b/packages/kbn-shared-ux-storybook/BUILD.bazel @@ -0,0 +1,130 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-shared-ux-storybook" +PKG_REQUIRE_NAME = "@kbn/shared-ux-storybook" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "//packages/kbn-shared-ux-services", + "//packages/kbn-storybook", + "@npm//@storybook/react", + "@npm//@storybook/addon-actions", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "//packages/kbn-shared-ux-services:npm_module_types", + "//packages/kbn-storybook:npm_module_types", + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@storybook/react", + "@npm//@storybook/addon-actions", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-shared-ux-storybook/README.mdx b/packages/kbn-shared-ux-storybook/README.mdx new file mode 100644 index 0000000000000..cfebeedcd1ac3 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/README.mdx @@ -0,0 +1,34 @@ +--- +id: kibSharedUXStorybook +slug: /kibana-dev-docs/shared-ux/packages/kbn-shared-ux-storybook +title: Shared UX Storybook +summary: The `@kbn/shared-ux-storybook` package provides Storybook assets for Shared UX and other teams. +date: 2022-03-11 +tags: ['kibana', 'dev', 'sharedUX'] +--- + +## About Shared UX Storybook + +This package provides the Storybook implementation of `@kbn/shared-ux-services` as well as the configuration for the Shared UX Storybook site. + +- `/src/services` The `@kbn/shared-ux-services` implementation. +- `src/config` The Storybook site configuration. + +## Storybook site + +Run `yarn storybook shared_ux` from `/kibana` to view the site. It pulls in `*.stories.tsx` from all Shared UX packages and plugins and combines them into a single configuration. + +## Decorator + +If you're writing stories for your own components that compose Shared UX components, you can use a pre-configured [Storybook Decorator](https://storybook.js.org/docs/react/writing-stories/decorators) in your Storybook configuration: + +```ts +// preview.ts + +import { addDecorator } from '@storybook/react'; +import { servicesDecorator } from '@kbn/shared-ux-storybook'; + +addDecorator(servicesDecorator); +``` + +This will not only expose parameters, but also wrap your story in a pre-wired `SharedUxServicesProvider`. \ No newline at end of file diff --git a/packages/kbn-shared-ux-storybook/jest.config.js b/packages/kbn-shared-ux-storybook/jest.config.js new file mode 100644 index 0000000000000..91285e025f069 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-shared-ux-storybook'], +}; diff --git a/packages/kbn-shared-ux-storybook/package.json b/packages/kbn-shared-ux-storybook/package.json new file mode 100644 index 0000000000000..c5f22bb151431 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-storybook", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-shared-ux-storybook/src/config/main.ts b/packages/kbn-shared-ux-storybook/src/config/main.ts new file mode 100644 index 0000000000000..1981ff546c353 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/src/config/main.ts @@ -0,0 +1,17 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { defaultConfig } from '@kbn/storybook'; + +module.exports = { + ...defaultConfig, + stories: [ + '../../../kbn-shared-ux*/**/*.stories.+(tsx|mdx)', + '../../../../src/plugins/shared_ux/**/*.stories.+(tsx|mdx)', + ], +}; diff --git a/src/plugins/shared_ux/.storybook/manager.ts b/packages/kbn-shared-ux-storybook/src/config/manager.ts similarity index 100% rename from src/plugins/shared_ux/.storybook/manager.ts rename to packages/kbn-shared-ux-storybook/src/config/manager.ts diff --git a/src/plugins/shared_ux/.storybook/preview.ts b/packages/kbn-shared-ux-storybook/src/config/preview.ts similarity index 89% rename from src/plugins/shared_ux/.storybook/preview.ts rename to packages/kbn-shared-ux-storybook/src/config/preview.ts index e4be2592482f3..194a16474af2c 100644 --- a/src/plugins/shared_ux/.storybook/preview.ts +++ b/packages/kbn-shared-ux-storybook/src/config/preview.ts @@ -7,6 +7,6 @@ */ import { addDecorator } from '@storybook/react'; -import { servicesDecorator } from './decorators'; +import { servicesDecorator } from '../decorators'; addDecorator(servicesDecorator); diff --git a/src/plugins/shared_ux/.storybook/decorators.tsx b/packages/kbn-shared-ux-storybook/src/decorators.tsx similarity index 75% rename from src/plugins/shared_ux/.storybook/decorators.tsx rename to packages/kbn-shared-ux-storybook/src/decorators.tsx index c17af2cda0406..cae87a15b54dd 100644 --- a/src/plugins/shared_ux/.storybook/decorators.tsx +++ b/packages/kbn-shared-ux-storybook/src/decorators.tsx @@ -8,13 +8,15 @@ import React from 'react'; import { DecoratorFn } from '@storybook/react'; -import { ServicesProvider } from '../public/services'; -import { servicesFactory } from '../public/services/storybook'; + +import { SharedUxServicesProvider } from '@kbn/shared-ux-services'; + +import { servicesFactory } from './services'; /** * A Storybook decorator that provides the Shared UX `ServicesProvider` with Storybook-specific * implementations to stories. */ export const servicesDecorator: DecoratorFn = (storyFn) => ( - {storyFn()} + {storyFn()} ); diff --git a/packages/kbn-shared-ux-storybook/src/file_system_cache.d.ts b/packages/kbn-shared-ux-storybook/src/file_system_cache.d.ts new file mode 100644 index 0000000000000..7831c50f10178 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/src/file_system_cache.d.ts @@ -0,0 +1,31 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +// Storybook uses this module and its types are defined in the source but not in the type output +declare module 'file-system-cache' { + interface Options { + basePath?: string; + ns?: string | string[]; + extension?: string; + } + + class FileSystemCache { + constructor(options: Options); + path(key: string): string; + fileExists(key: string): Promise; + ensureBasePath(): Promise; + get(key: string, defaultValue?: any): Promise; + getSync(key: string, defaultValue?: any): any | typeof defaultValue; + set(key: string, value: any): Promise<{ path: string }>; + setSync(key: string, value: any): this; + remove(key: string): Promise; + clear(): Promise; + save(): Promise<{ paths: string[] }>; + load(): Promise<{ files: Array<{ path: string; value: any }> }>; + } +} diff --git a/packages/kbn-shared-ux-storybook/src/index.ts b/packages/kbn-shared-ux-storybook/src/index.ts new file mode 100755 index 0000000000000..51f1c61165020 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/src/index.ts @@ -0,0 +1,19 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export { servicesDecorator } from './decorators'; + +export { + applicationServiceFactory, + docLinksServiceFactory, + editorsServiceFactory, + httpServiceFactory, + platformServiceFactory, + servicesFactory, + userPermissionsServiceFactory, +} from './services'; diff --git a/packages/kbn-shared-ux-storybook/src/mdx.d.ts b/packages/kbn-shared-ux-storybook/src/mdx.d.ts new file mode 100644 index 0000000000000..b4633af9cb87d --- /dev/null +++ b/packages/kbn-shared-ux-storybook/src/mdx.d.ts @@ -0,0 +1,14 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +// Importing MDX files requires a type definition not currently included in the stack. +declare module '*.mdx' { + let MDXComponent: (props: any) => JSX.Element; + // eslint-disable-next-line import/no-default-export + export default MDXComponent; +} diff --git a/src/plugins/shared_ux/public/services/storybook/application.ts b/packages/kbn-shared-ux-storybook/src/services/application.ts similarity index 79% rename from src/plugins/shared_ux/public/services/storybook/application.ts rename to packages/kbn-shared-ux-storybook/src/services/application.ts index 9b38d3b28689e..2a544445fc474 100644 --- a/src/plugins/shared_ux/public/services/storybook/application.ts +++ b/packages/kbn-shared-ux-storybook/src/services/application.ts @@ -8,10 +8,9 @@ import { BehaviorSubject } from 'rxjs'; import { action } from '@storybook/addon-actions'; -import { PluginServiceFactory } from '../types'; -import { SharedUXApplicationService } from '../application'; +import { ServiceFactory, SharedUxApplicationService } from '@kbn/shared-ux-services'; -export type ApplicationServiceFactory = PluginServiceFactory; +export type ApplicationServiceFactory = ServiceFactory; /** * A factory function for creating for creating a storybook implementation of `SharedUXApplicationService`. diff --git a/packages/kbn-shared-ux-storybook/src/services/doc_links.ts b/packages/kbn-shared-ux-storybook/src/services/doc_links.ts new file mode 100644 index 0000000000000..5ae1dcccc3664 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/src/services/doc_links.ts @@ -0,0 +1,21 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { ServiceFactory, SharedUxDocLinksService } from '@kbn/shared-ux-services'; + +/** + * A factory function for creating a Storybook implementation of `SharedUxDocLinksService`. + */ +export type SharedUxDocLinksServiceFactory = ServiceFactory; + +/** + * A factory function for creating a Storybook implementation of `SharedUxDocLinksService`. + */ +export const docLinksServiceFactory: SharedUxDocLinksServiceFactory = () => ({ + dataViewsDocLink: 'https://www.elastic.co/guide/en/kibana/master/data-views.html', +}); diff --git a/packages/kbn-shared-ux-storybook/src/services/editors.ts b/packages/kbn-shared-ux-storybook/src/services/editors.ts new file mode 100644 index 0000000000000..69b9d7062da94 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/src/services/editors.ts @@ -0,0 +1,22 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { action } from '@storybook/addon-actions'; +import { ServiceFactory, SharedUxEditorsService } from '@kbn/shared-ux-services'; + +/** + * A factory function for creating a Storybook implementation of `SharedUxEditorsService`. + */ +export type SharedUxEditorsServiceFactory = ServiceFactory; + +/** + * A factory function for creating a Storybook implementation of `SharedUxEditorsService`. + */ +export const editorsServiceFactory: SharedUxEditorsServiceFactory = () => ({ + openDataViewEditor: action('openEditor') as SharedUxEditorsService['openDataViewEditor'], +}); diff --git a/src/plugins/shared_ux/public/services/storybook/http.ts b/packages/kbn-shared-ux-storybook/src/services/http.ts similarity index 74% rename from src/plugins/shared_ux/public/services/storybook/http.ts rename to packages/kbn-shared-ux-storybook/src/services/http.ts index d5a04d4587967..f44fb10566dc3 100644 --- a/src/plugins/shared_ux/public/services/storybook/http.ts +++ b/packages/kbn-shared-ux-storybook/src/services/http.ts @@ -8,17 +8,16 @@ import { action } from '@storybook/addon-actions'; -import { PluginServiceFactory } from '../types'; -import { SharedUXHttpService } from '../http'; +import { ServiceFactory, SharedUxHttpService } from '@kbn/shared-ux-services'; /** * A factory function for creating a Storybook-based implementation of `SharedUXHttpService`. */ -export type HttpServiceFactory = PluginServiceFactory; +export type HttpServiceFactory = ServiceFactory; /** * A factory function for creating a Storybook-based implementation of `SharedUXHttpService`. */ export const httpServiceFactory: HttpServiceFactory = () => ({ - addBasePath: action('addBasePath') as SharedUXHttpService['addBasePath'], + addBasePath: action('addBasePath') as SharedUxHttpService['addBasePath'], }); diff --git a/src/plugins/shared_ux/public/services/storybook/index.ts b/packages/kbn-shared-ux-storybook/src/services/index.ts similarity index 61% rename from src/plugins/shared_ux/public/services/storybook/index.ts rename to packages/kbn-shared-ux-storybook/src/services/index.ts index 878df9ca2beba..2de34f3392312 100644 --- a/src/plugins/shared_ux/public/services/storybook/index.ts +++ b/packages/kbn-shared-ux-storybook/src/services/index.ts @@ -6,23 +6,30 @@ * Side Public License, v 1. */ -import type { SharedUXServices } from '../.'; -import { PluginServiceFactory } from '../types'; -import { platformServiceFactory } from './platform'; -import { editorsServiceFactory } from './editors'; -import { userPermissionsServiceFactory } from './permissions'; +import type { ServiceFactory, SharedUxServices } from '@kbn/shared-ux-services'; + +import { applicationServiceFactory } from './application'; import { docLinksServiceFactory } from './doc_links'; +import { editorsServiceFactory } from './editors'; import { httpServiceFactory } from './http'; -import { applicationServiceFactory } from './application'; +import { platformServiceFactory } from './platform'; +import { userPermissionsServiceFactory } from './permissions'; + +export { applicationServiceFactory } from './application'; +export { docLinksServiceFactory } from './doc_links'; +export { editorsServiceFactory } from './editors'; +export { httpServiceFactory } from './http'; +export { platformServiceFactory } from './platform'; +export { userPermissionsServiceFactory } from './permissions'; /** - * A factory function for creating a Storybook-based implementation of `SharedUXServices`. + * A factory function for creating a Storybook implementation of `SharedUxServices`. */ -export const servicesFactory: PluginServiceFactory = (params) => ({ - platform: platformServiceFactory(params), - permissions: userPermissionsServiceFactory(), - editors: editorsServiceFactory(), +export const servicesFactory: ServiceFactory = (params) => ({ + application: applicationServiceFactory(), docLinks: docLinksServiceFactory(), + editors: editorsServiceFactory(), http: httpServiceFactory(params), - application: applicationServiceFactory(), + permissions: userPermissionsServiceFactory(), + platform: platformServiceFactory(params), }); diff --git a/packages/kbn-shared-ux-storybook/src/services/permissions.ts b/packages/kbn-shared-ux-storybook/src/services/permissions.ts new file mode 100644 index 0000000000000..5d00ac3ee1cb2 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/src/services/permissions.ts @@ -0,0 +1,22 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { ServiceFactory, SharedUxUserPermissionsService } from '@kbn/shared-ux-services'; + +/** + * A factory function for creating a Storybook implementation of `SharedUxUserPermissionsService`. + */ +export type SharedUxUserPermissionsServiceFactory = ServiceFactory; + +/** + * A factory function for creating a Storybook implementation of `SharedUxUserPermissionsService`. + */ +export const userPermissionsServiceFactory: SharedUxUserPermissionsServiceFactory = () => ({ + canCreateNewDataView: true, + canAccessFleet: true, +}); diff --git a/src/plugins/shared_ux/public/services/storybook/platform.ts b/packages/kbn-shared-ux-storybook/src/services/platform.ts similarity index 58% rename from src/plugins/shared_ux/public/services/storybook/platform.ts rename to packages/kbn-shared-ux-storybook/src/services/platform.ts index 8d7645ee441ca..2a1cb6c3ff42b 100644 --- a/src/plugins/shared_ux/public/services/storybook/platform.ts +++ b/packages/kbn-shared-ux-storybook/src/services/platform.ts @@ -7,17 +7,15 @@ */ import { action } from '@storybook/addon-actions'; - -import { PluginServiceFactory } from '../types'; -import { SharedUXPlatformService } from '../platform'; +import { ServiceFactory, SharedUxPlatformService } from '@kbn/shared-ux-services'; /** - * A factory function for creating a Storybook-based implementation of `SharedUXPlatformService`. + * A factory function for creating a Storybook implementation of `SharedUxPlatformService`. */ -export type PlatformServiceFactory = PluginServiceFactory; +export type PlatformServiceFactory = ServiceFactory; /** - * A factory function for creating a Storybook-based implementation of `SharedUXPlatformService`. + * A factory function for creating a Storybook implementation of `SharedUxPlatformService`. */ export const platformServiceFactory: PlatformServiceFactory = () => ({ setIsFullscreen: action('setIsChromeVisible'), diff --git a/packages/kbn-shared-ux-storybook/tsconfig.json b/packages/kbn-shared-ux-storybook/tsconfig.json new file mode 100644 index 0000000000000..a8cfc2cceb08b --- /dev/null +++ b/packages/kbn-shared-ux-storybook/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-shared-ux-utility/BUILD.bazel b/packages/kbn-shared-ux-utility/BUILD.bazel new file mode 100644 index 0000000000000..b4008aa43f1ac --- /dev/null +++ b/packages/kbn-shared-ux-utility/BUILD.bazel @@ -0,0 +1,130 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-shared-ux-utility" +PKG_REQUIRE_NAME = "@kbn/shared-ux-utility" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//react", + "@npm//@emotion/css", + "@npm//@emotion/react", + "@npm//@elastic/eui", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/react", + "@npm//@emotion/react", + "@npm//@emotion/css", + "@npm//@elastic/eui", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-shared-ux-utility/README.mdx b/packages/kbn-shared-ux-utility/README.mdx new file mode 100644 index 0000000000000..a3a3ee965f699 --- /dev/null +++ b/packages/kbn-shared-ux-utility/README.mdx @@ -0,0 +1,14 @@ +--- +id: kibSharedUXUtility +slug: /kibana-dev-docs/shared-ux/packages/kbn-shared-ux-utility +title: Shared UX Utilities +summary: The `@kbn/shared-ux-utility` package contains a set of React, DOM and other utilities. +date: 2022-03-11 +tags: ['kibana', 'dev', 'sharedUX'] +--- + +## About Shared UX Utility + +At present, this package is currently a "catch-all" for universally-shared utilities. View the specific documentation for each utility for + +> Important: do not add new utilities to this package without considering if a different home is more appropriate. Reach out to the Shared UX team for help. \ No newline at end of file diff --git a/packages/kbn-shared-ux-utility/jest.config.js b/packages/kbn-shared-ux-utility/jest.config.js new file mode 100644 index 0000000000000..3696855dc86b5 --- /dev/null +++ b/packages/kbn-shared-ux-utility/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-shared-ux-utility'], +}; diff --git a/packages/kbn-shared-ux-utility/package.json b/packages/kbn-shared-ux-utility/package.json new file mode 100644 index 0000000000000..dca0f7758ddbc --- /dev/null +++ b/packages/kbn-shared-ux-utility/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-utility", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/plugins/shared_ux/public/components/utility/fallback.tsx b/packages/kbn-shared-ux-utility/src/fallback.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/utility/fallback.tsx rename to packages/kbn-shared-ux-utility/src/fallback.tsx diff --git a/src/plugins/shared_ux/public/components/utility/index.ts b/packages/kbn-shared-ux-utility/src/index.ts similarity index 87% rename from src/plugins/shared_ux/public/components/utility/index.ts rename to packages/kbn-shared-ux-utility/src/index.ts index 4e930810a6895..d6c35ea3702ae 100644 --- a/src/plugins/shared_ux/public/components/utility/index.ts +++ b/packages/kbn-shared-ux-utility/src/index.ts @@ -8,3 +8,4 @@ export { Fallback } from './fallback'; export { withSuspense } from './with_suspense'; +export { getClosestLink, hasActiveModifierKey } from './utils'; diff --git a/src/plugins/shared_ux/public/components/utility/utils.test.ts b/packages/kbn-shared-ux-utility/src/utils.test.ts similarity index 100% rename from src/plugins/shared_ux/public/components/utility/utils.test.ts rename to packages/kbn-shared-ux-utility/src/utils.test.ts diff --git a/src/plugins/shared_ux/public/components/utility/utils.ts b/packages/kbn-shared-ux-utility/src/utils.ts similarity index 84% rename from src/plugins/shared_ux/public/components/utility/utils.ts rename to packages/kbn-shared-ux-utility/src/utils.ts index 0ac501d160815..e26f677c031a2 100644 --- a/src/plugins/shared_ux/public/components/utility/utils.ts +++ b/packages/kbn-shared-ux-utility/src/utils.ts @@ -6,17 +6,18 @@ * Side Public License, v 1. */ -import React from 'react'; +import type { MouseEvent } from 'react'; /** * Returns true if any modifier key is active on the event, false otherwise. */ -export const hasActiveModifierKey = (event: React.MouseEvent): boolean => { +export const hasActiveModifierKey = (event: MouseEvent): boolean => { return event.metaKey || event.altKey || event.ctrlKey || event.shiftKey; }; /** - * Returns the closest anchor (``) element in the element parents (self included) up to the given container (excluded), or undefined if none is found. + * Returns the closest anchor (``) element in the element parents (self included) up + * to the given container (excluded), or undefined if none is found. */ export const getClosestLink = ( element: HTMLElement | null | undefined, diff --git a/src/plugins/shared_ux/public/components/utility/with_suspense.tsx b/packages/kbn-shared-ux-utility/src/with_suspense.tsx similarity index 100% rename from src/plugins/shared_ux/public/components/utility/with_suspense.tsx rename to packages/kbn-shared-ux-utility/src/with_suspense.tsx diff --git a/packages/kbn-shared-ux-utility/tsconfig.json b/packages/kbn-shared-ux-utility/tsconfig.json new file mode 100644 index 0000000000000..cca91264519cc --- /dev/null +++ b/packages/kbn-shared-ux-utility/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node", + "@emotion/react/types/css-prop" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/src/core/server/elasticsearch/client/client_config.test.ts b/src/core/server/elasticsearch/client/client_config.test.ts index cf5a15ab9c19b..fb5f83c2a6bc0 100644 --- a/src/core/server/elasticsearch/client/client_config.test.ts +++ b/src/core/server/elasticsearch/client/client_config.test.ts @@ -16,6 +16,7 @@ const createConfig = ( return { customHeaders: {}, compression: false, + maxSockets: Infinity, sniffOnStart: false, sniffOnConnectionFault: false, sniffInterval: false, @@ -107,6 +108,18 @@ describe('parseClientOptions', () => { }); }); + describe('`maxSockets` option', () => { + it('uses the specified config value', () => { + const options = parseClientOptions(createConfig({ maxSockets: 1024 }), false); + expect(options.agent).toHaveProperty('maxSockets', 1024); + }); + + it('defaults to `Infinity` if not specified by the config', () => { + const options = parseClientOptions(createConfig({}), false); + expect(options.agent).toHaveProperty('maxSockets', Infinity); + }); + }); + describe('`compression` option', () => { it('`compression` is true', () => { const options = parseClientOptions(createConfig({ compression: true }), false); diff --git a/src/core/server/elasticsearch/client/client_config.ts b/src/core/server/elasticsearch/client/client_config.ts index 8a1eacb78d923..9a0b72a36c3db 100644 --- a/src/core/server/elasticsearch/client/client_config.ts +++ b/src/core/server/elasticsearch/client/client_config.ts @@ -22,6 +22,7 @@ import { DEFAULT_HEADERS } from '../default_headers'; export type ElasticsearchClientConfig = Pick< ElasticsearchConfig, | 'customHeaders' + | 'maxSockets' | 'compression' | 'sniffOnStart' | 'sniffOnConnectionFault' @@ -61,7 +62,7 @@ export function parseClientOptions( // fixes https://github.com/elastic/kibana/issues/101944 disablePrototypePoisoningProtection: true, agent: { - maxSockets: Infinity, + maxSockets: config.maxSockets, keepAlive: config.keepAlive ?? true, }, compression: config.compression, diff --git a/src/core/server/elasticsearch/client/cluster_client.test.ts b/src/core/server/elasticsearch/client/cluster_client.test.ts index 98ef7e8dfc4a4..bcec150b4d06e 100644 --- a/src/core/server/elasticsearch/client/cluster_client.test.ts +++ b/src/core/server/elasticsearch/client/cluster_client.test.ts @@ -26,6 +26,7 @@ const createConfig = ( sniffOnStart: false, sniffOnConnectionFault: false, sniffInterval: false, + maxSockets: Infinity, compression: false, requestHeadersWhitelist: ['authorization'], customHeaders: {}, diff --git a/src/core/server/elasticsearch/elasticsearch_config.test.ts b/src/core/server/elasticsearch/elasticsearch_config.test.ts index 5c6db10e8695a..47453f1a80610 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.test.ts @@ -36,6 +36,7 @@ test('set correct defaults', () => { "http://localhost:9200", ], "ignoreVersionMismatch": false, + "maxSockets": Infinity, "password": undefined, "pingTimeout": "PT30S", "requestHeadersWhitelist": Array [ @@ -80,6 +81,45 @@ test('#hosts accepts both string and array of strings', () => { expect(configValue.hosts).toEqual(['http://some.host:1234', 'https://some.another.host']); }); +describe('#maxSockets', () => { + test('accepts positive numeric values', () => { + const configValue = new ElasticsearchConfig(config.schema.validate({ maxSockets: 512 })); + expect(configValue.maxSockets).toEqual(512); + }); + + test('throws if it does not contain a numeric value', () => { + expect(() => { + config.schema.validate({ maxSockets: 'foo' }); + }).toThrowErrorMatchingInlineSnapshot( + `"[maxSockets]: expected value of type [number] but got [string]"` + ); + + expect(() => { + config.schema.validate({ maxSockets: true }); + }).toThrowErrorMatchingInlineSnapshot( + `"[maxSockets]: expected value of type [number] but got [boolean]"` + ); + }); + + test('throws if it does not contain a valid numeric value', () => { + expect(() => { + config.schema.validate({ maxSockets: -1 }); + }).toThrowErrorMatchingInlineSnapshot( + '"[maxSockets]: Value must be equal to or greater than [1]."' + ); + + expect(() => { + config.schema.validate({ maxSockets: 0 }); + }).toThrowErrorMatchingInlineSnapshot( + '"[maxSockets]: Value must be equal to or greater than [1]."' + ); + + expect(() => { + config.schema.validate({ maxSockets: Infinity }); + }).toThrowErrorMatchingInlineSnapshot('"[maxSockets]: \\"maxSockets\\" cannot be infinity"'); + }); +}); + test('#requestHeadersWhitelist accepts both string and array of strings', () => { let configValue = new ElasticsearchConfig( config.schema.validate({ requestHeadersWhitelist: 'token' }) diff --git a/src/core/server/elasticsearch/elasticsearch_config.ts b/src/core/server/elasticsearch/elasticsearch_config.ts index 91b1a1f4475e1..1baef038e313a 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.ts @@ -36,6 +36,7 @@ export const configSchema = schema.object({ hosts: schema.oneOf([hostURISchema, schema.arrayOf(hostURISchema, { minSize: 1 })], { defaultValue: 'http://localhost:9200', }), + maxSockets: schema.number({ defaultValue: Infinity, min: 1 }), compression: schema.boolean({ defaultValue: false }), username: schema.maybe( schema.string({ @@ -298,6 +299,11 @@ export class ElasticsearchConfig { */ public readonly apiVersion: string; + /** + * The maximum number of sockets that can be used for communications with elasticsearch. + */ + public readonly maxSockets: number; + /** * Whether to use compression for communications with elasticsearch. */ @@ -405,6 +411,7 @@ export class ElasticsearchConfig { this.password = rawConfig.password; this.serviceAccountToken = rawConfig.serviceAccountToken; this.customHeaders = rawConfig.customHeaders; + this.maxSockets = rawConfig.maxSockets; this.compression = rawConfig.compression; this.skipStartupConnectionCheck = rawConfig.skipStartupConnectionCheck; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 7024899388c05..f5516649804a2 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -231,6 +231,7 @@ export const config: { sniffInterval: Type; sniffOnConnectionFault: Type; hosts: Type; + maxSockets: Type; compression: Type; username: Type; password: Type; @@ -889,7 +890,7 @@ export { EcsEventType } export type ElasticsearchClient = Omit; // @public -export type ElasticsearchClientConfig = Pick & { +export type ElasticsearchClientConfig = Pick & { pingTimeout?: ElasticsearchConfig['pingTimeout'] | ClientOptions['pingTimeout']; requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout']; ssl?: Partial; @@ -907,6 +908,7 @@ export class ElasticsearchConfig { readonly healthCheckDelay: Duration; readonly hosts: string[]; readonly ignoreVersionMismatch: boolean; + readonly maxSockets: number; readonly password?: string; readonly pingTimeout: Duration; readonly requestHeadersWhitelist: string[]; diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index db0791f41b0a7..eb7f75c27661f 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -35,6 +35,6 @@ export const storybookAliases = { observability: 'x-pack/plugins/observability/.storybook', presentation: 'src/plugins/presentation_util/storybook', security_solution: 'x-pack/plugins/security_solution/.storybook', - shared_ux: 'src/plugins/shared_ux/.storybook', + shared_ux: 'packages/kbn-shared-ux-storybook/src/config', ui_actions_enhanced: 'x-pack/plugins/ui_actions_enhanced/.storybook', }; diff --git a/src/plugins/shared_ux/kibana.json b/src/plugins/shared_ux/kibana.json index 308a252f70b54..540514b4ab7e7 100755 --- a/src/plugins/shared_ux/kibana.json +++ b/src/plugins/shared_ux/kibana.json @@ -9,6 +9,6 @@ "description": "A plugin providing components and services for shared user experiences in Kibana.", "server": true, "ui": true, - "requiredPlugins": ["dataViewEditor", "dataViews"], + "requiredPlugins": ["dataViewEditor"], "optionalPlugins": [] } diff --git a/src/plugins/shared_ux/public/index.ts b/src/plugins/shared_ux/public/index.ts index 5505f5ffb1e02..1dcf84eaf4991 100755 --- a/src/plugins/shared_ux/public/index.ts +++ b/src/plugins/shared_ux/public/index.ts @@ -16,6 +16,3 @@ export function plugin() { } export type { SharedUXPluginSetup, SharedUXPluginStart } from './types'; -export { ExitFullScreenButton, LazyExitFullScreenButton } from './components'; -export { NoDataViewsPage, LazyNoDataViewsPage } from './components'; -export { IconButtonGroup } from './components'; diff --git a/src/plugins/shared_ux/public/plugin.tsx b/src/plugins/shared_ux/public/plugin.tsx index 1d2840566d4d9..b91675fd88f5b 100755 --- a/src/plugins/shared_ux/public/plugin.tsx +++ b/src/plugins/shared_ux/public/plugin.tsx @@ -7,6 +7,9 @@ */ import React from 'react'; + +import { SharedUxServicesProvider } from '@kbn/shared-ux-services'; + import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { SharedUXPluginSetup, @@ -15,8 +18,7 @@ import { SharedUXPluginSetupDeps, } from './types'; -import { ServicesProvider } from './services'; -import { servicesFactory } from './services/kibana'; +import { servicesFactory } from './services'; /** * The Kibana plugin for Shared User Experience (Shared UX). @@ -34,7 +36,7 @@ export class SharedUXPlugin implements Plugin ( - {children} + {children} ), }; } diff --git a/src/plugins/shared_ux/public/services/application.ts b/src/plugins/shared_ux/public/services/application.ts index b1901bd79c6d2..95d6d4b4d72d9 100644 --- a/src/plugins/shared_ux/public/services/application.ts +++ b/src/plugins/shared_ux/public/services/application.ts @@ -6,9 +6,20 @@ * Side Public License, v 1. */ -import { Observable } from 'rxjs'; +import { SharedUxApplicationService } from '@kbn/shared-ux-services'; -export interface SharedUXApplicationService { - navigateToUrl: (url: string) => Promise; - currentAppId$: Observable; -} +import { KibanaPluginServiceFactory } from './types'; +import { SharedUXPluginStartDeps } from '../types'; + +export type ApplicationServiceFactory = KibanaPluginServiceFactory< + SharedUxApplicationService, + SharedUXPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. + */ +export const applicationServiceFactory: ApplicationServiceFactory = ({ coreStart }) => ({ + navigateToUrl: coreStart.application.navigateToUrl, + currentAppId$: coreStart.application.currentAppId$, +}); diff --git a/src/plugins/shared_ux/public/services/doc_links.ts b/src/plugins/shared_ux/public/services/doc_links.ts index 3c6d23bd33ae4..b457cddb7c3bc 100644 --- a/src/plugins/shared_ux/public/services/doc_links.ts +++ b/src/plugins/shared_ux/public/services/doc_links.ts @@ -5,6 +5,20 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -export interface SharedUXDocLinksService { - dataViewsDocsLink: string; -} + +import { SharedUxDocLinksService } from '@kbn/shared-ux-services'; + +import { KibanaPluginServiceFactory } from './types'; +import { SharedUXPluginStartDeps } from '../types'; + +export type DocLinksServiceFactory = KibanaPluginServiceFactory< + SharedUxDocLinksService, + SharedUXPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. + */ +export const docLinksServiceFactory: DocLinksServiceFactory = ({ coreStart }) => ({ + dataViewsDocLink: coreStart.docLinks.links.indexPatterns?.introduction, +}); diff --git a/src/plugins/shared_ux/public/services/editors.ts b/src/plugins/shared_ux/public/services/editors.ts index 176b22a6006e1..498b42954091c 100644 --- a/src/plugins/shared_ux/public/services/editors.ts +++ b/src/plugins/shared_ux/public/services/editors.ts @@ -5,11 +5,20 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { DataView } from '../../../data_views/common'; - -export interface SharedUxDataViewEditorProps { - onSave: (dataView: DataView) => void; -} -export interface SharedUXEditorsService { - openDataViewEditor: (options: SharedUxDataViewEditorProps) => () => void; -} + +import { SharedUxEditorsService } from '@kbn/shared-ux-services'; + +import { KibanaPluginServiceFactory } from './types'; +import { SharedUXPluginStartDeps } from '../types'; + +export type EditorsServiceFactory = KibanaPluginServiceFactory< + SharedUxEditorsService, + SharedUXPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. + */ +export const editorsServiceFactory: EditorsServiceFactory = ({ startPlugins }) => ({ + openDataViewEditor: startPlugins.dataViewEditor.openEditor, +}); diff --git a/src/plugins/shared_ux/public/services/http.ts b/src/plugins/shared_ux/public/services/http.ts index da32d659b445f..afc2f00713988 100644 --- a/src/plugins/shared_ux/public/services/http.ts +++ b/src/plugins/shared_ux/public/services/http.ts @@ -6,6 +6,19 @@ * Side Public License, v 1. */ -export interface SharedUXHttpService { - addBasePath: (url: string) => string; -} +import { SharedUxHttpService } from '@kbn/shared-ux-services'; + +import { KibanaPluginServiceFactory } from './types'; +import { SharedUXPluginStartDeps } from '../types'; + +export type HttpServiceFactory = KibanaPluginServiceFactory< + SharedUxHttpService, + SharedUXPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. + */ +export const httpServiceFactory: HttpServiceFactory = ({ coreStart }) => ({ + addBasePath: coreStart.http.basePath.prepend, +}); diff --git a/src/plugins/shared_ux/public/services/kibana/index.ts b/src/plugins/shared_ux/public/services/index.ts similarity index 85% rename from src/plugins/shared_ux/public/services/kibana/index.ts rename to src/plugins/shared_ux/public/services/index.ts index 0df8604379b4f..358a2b8035ecf 100644 --- a/src/plugins/shared_ux/public/services/kibana/index.ts +++ b/src/plugins/shared_ux/public/services/index.ts @@ -6,9 +6,11 @@ * Side Public License, v 1. */ -import type { SharedUXServices } from '..'; -import type { SharedUXPluginStartDeps } from '../../types'; -import type { KibanaPluginServiceFactory } from '../types'; +import { SharedUxServices } from '@kbn/shared-ux-services'; + +import type { SharedUXPluginStartDeps } from '../types'; +import type { KibanaPluginServiceFactory } from './types'; + import { platformServiceFactory } from './platform'; import { userPermissionsServiceFactory } from './permissions'; import { editorsServiceFactory } from './editors'; @@ -20,7 +22,7 @@ import { applicationServiceFactory } from './application'; * A factory function for creating a Kibana-based implementation of `SharedUXServices`. */ export const servicesFactory: KibanaPluginServiceFactory< - SharedUXServices, + SharedUxServices, SharedUXPluginStartDeps > = (params) => ({ platform: platformServiceFactory(params), diff --git a/src/plugins/shared_ux/public/services/index.tsx b/src/plugins/shared_ux/public/services/index.tsx deleted file mode 100644 index e83e1eab4b95d..0000000000000 --- a/src/plugins/shared_ux/public/services/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import React, { FC, createContext, useContext } from 'react'; -import { SharedUXPlatformService } from './platform'; -import { servicesFactory } from './stub'; -import { SharedUXUserPermissionsService } from './permissions'; -import { SharedUXEditorsService } from './editors'; -import { SharedUXDocLinksService } from './doc_links'; -import { SharedUXHttpService } from './http'; -import { SharedUXApplicationService } from './application'; - -/** - * A collection of services utilized by SharedUX. This serves as a thin - * abstraction layer between services provided by Kibana and other plugins - * while allowing this plugin to be developed independently of those contracts. - * - * It also allows us to "swap out" differenct implementations of these services - * for different environments, (e.g. Jest, Storybook, etc.) - */ -export interface SharedUXServices { - platform: SharedUXPlatformService; - permissions: SharedUXUserPermissionsService; - editors: SharedUXEditorsService; - docLinks: SharedUXDocLinksService; - http: SharedUXHttpService; - application: SharedUXApplicationService; -} - -// The React Context used to provide the services to the SharedUX components. -const ServicesContext = createContext(servicesFactory()); - -/** - * The `React.Context` Provider component for the `SharedUXServices` context. Any - * plugin or environment that consumes SharedUX components needs to wrap their React - * tree with this provider. - * - * Within a plugin, you can use the `ServicesContext` provided by the SharedUX plugin start - * lifeycle method. - */ -export const ServicesProvider: FC = ({ children, ...services }) => ( - {children} -); - -/** - * React hook for accessing the pre-wired `SharedUXServices`. - */ -export function useServices() { - return useContext(ServicesContext); -} - -/** - * React hook for accessing the pre-wired `SharedUXPlatformService`. - */ -export const usePlatformService = () => useServices().platform; - -export const usePermissions = () => useServices().permissions; - -export const useEditors = () => useServices().editors; - -export const useDocLinks = () => useServices().docLinks; - -export const useHttp = () => useServices().http; - -export const useApplication = () => useServices().application; diff --git a/src/plugins/shared_ux/public/services/kibana/application.ts b/src/plugins/shared_ux/public/services/kibana/application.ts deleted file mode 100644 index dfb13e6088270..0000000000000 --- a/src/plugins/shared_ux/public/services/kibana/application.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import { KibanaPluginServiceFactory } from '../types'; -import { SharedUXPluginStartDeps } from '../../types'; -import { SharedUXApplicationService } from '../application'; - -export type ApplicationServiceFactory = KibanaPluginServiceFactory< - SharedUXApplicationService, - SharedUXPluginStartDeps ->; - -/** - * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. - */ -export const applicationServiceFactory: ApplicationServiceFactory = ({ coreStart }) => ({ - navigateToUrl: coreStart.application.navigateToUrl, - currentAppId$: coreStart.application.currentAppId$, -}); diff --git a/src/plugins/shared_ux/public/services/kibana/doc_links.ts b/src/plugins/shared_ux/public/services/kibana/doc_links.ts deleted file mode 100644 index eb25114a188a2..0000000000000 --- a/src/plugins/shared_ux/public/services/kibana/doc_links.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import { KibanaPluginServiceFactory } from '../types'; -import { SharedUXPluginStartDeps } from '../../types'; -import { SharedUXDocLinksService } from '../doc_links'; - -export type DocLinksServiceFactory = KibanaPluginServiceFactory< - SharedUXDocLinksService, - SharedUXPluginStartDeps ->; - -/** - * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. - */ -export const docLinksServiceFactory: DocLinksServiceFactory = ({ coreStart }) => ({ - dataViewsDocsLink: coreStart.docLinks.links.indexPatterns?.introduction, -}); diff --git a/src/plugins/shared_ux/public/services/kibana/editors.ts b/src/plugins/shared_ux/public/services/kibana/editors.ts deleted file mode 100644 index 0f8082d7d00e2..0000000000000 --- a/src/plugins/shared_ux/public/services/kibana/editors.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import { KibanaPluginServiceFactory } from '../types'; -import { SharedUXEditorsService } from '../editors'; -import { SharedUXPluginStartDeps } from '../../types'; - -export type EditorsServiceFactory = KibanaPluginServiceFactory< - SharedUXEditorsService, - SharedUXPluginStartDeps ->; - -/** - * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. - */ -export const editorsServiceFactory: EditorsServiceFactory = ({ startPlugins }) => ({ - openDataViewEditor: startPlugins.dataViewEditor.openEditor, -}); diff --git a/src/plugins/shared_ux/public/services/kibana/http.ts b/src/plugins/shared_ux/public/services/kibana/http.ts deleted file mode 100644 index 0947d416724df..0000000000000 --- a/src/plugins/shared_ux/public/services/kibana/http.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import { KibanaPluginServiceFactory } from '../types'; -import { SharedUXHttpService } from '../http'; -import { SharedUXPluginStartDeps } from '../../types'; - -export type HttpServiceFactory = KibanaPluginServiceFactory< - SharedUXHttpService, - SharedUXPluginStartDeps ->; - -/** - * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. - */ -export const httpServiceFactory: HttpServiceFactory = ({ coreStart, startPlugins }) => ({ - addBasePath: coreStart.http.basePath.prepend, -}); diff --git a/src/plugins/shared_ux/public/services/kibana/permissions.ts b/src/plugins/shared_ux/public/services/kibana/permissions.ts deleted file mode 100644 index 5b1cedd51e969..0000000000000 --- a/src/plugins/shared_ux/public/services/kibana/permissions.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import { KibanaPluginServiceFactory } from '../types'; -import { SharedUXPluginStartDeps } from '../../types'; -import { SharedUXUserPermissionsService } from '../permissions'; - -export type UserPermissionsServiceFactory = KibanaPluginServiceFactory< - SharedUXUserPermissionsService, - SharedUXPluginStartDeps ->; - -/** - * A factory function for creating a Kibana-based implementation of `SharedUXPermissionsService`. - */ -export const userPermissionsServiceFactory: UserPermissionsServiceFactory = ({ - coreStart, - startPlugins, -}) => ({ - canCreateNewDataView: startPlugins.dataViewEditor.userPermissions.editDataView(), - canAccessFleet: coreStart.application.capabilities.navLinks.integrations, -}); diff --git a/src/plugins/shared_ux/public/services/kibana/platform.ts b/src/plugins/shared_ux/public/services/kibana/platform.ts deleted file mode 100644 index 9872149ee02f2..0000000000000 --- a/src/plugins/shared_ux/public/services/kibana/platform.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import { SharedUXPluginStartDeps } from '../../types'; -import { KibanaPluginServiceFactory } from '../types'; -import { SharedUXPlatformService } from '../platform'; - -/** - * A factory function for creating a Kibana-based implementation of `SharedUXPlatformService`. - */ -export type PlatformServiceFactory = KibanaPluginServiceFactory< - SharedUXPlatformService, - SharedUXPluginStartDeps ->; - -/** - * A factory function for creating a Kibana-based implementation of `SharedUXPlatformService`. - */ -export const platformServiceFactory: PlatformServiceFactory = ({ coreStart }) => ({ - setIsFullscreen: (isVisible: boolean) => coreStart.chrome.setIsVisible(isVisible), -}); diff --git a/src/plugins/shared_ux/public/services/permissions.ts b/src/plugins/shared_ux/public/services/permissions.ts index 8e8f5a54341d6..694405b5c6610 100644 --- a/src/plugins/shared_ux/public/services/permissions.ts +++ b/src/plugins/shared_ux/public/services/permissions.ts @@ -6,7 +6,22 @@ * Side Public License, v 1. */ -export interface SharedUXUserPermissionsService { - canCreateNewDataView: boolean; - canAccessFleet: boolean; -} +import { SharedUxUserPermissionsService } from '@kbn/shared-ux-services'; +import { KibanaPluginServiceFactory } from './types'; +import { SharedUXPluginStartDeps } from '../types'; + +export type UserPermissionsServiceFactory = KibanaPluginServiceFactory< + SharedUxUserPermissionsService, + SharedUXPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXPermissionsService`. + */ +export const userPermissionsServiceFactory: UserPermissionsServiceFactory = ({ + coreStart, + startPlugins, +}) => ({ + canCreateNewDataView: startPlugins.dataViewEditor.userPermissions.editDataView(), + canAccessFleet: coreStart.application.capabilities.navLinks.integrations, +}); diff --git a/src/plugins/shared_ux/public/services/platform.ts b/src/plugins/shared_ux/public/services/platform.ts index 52f88a1133c8b..b0ee61583dd8d 100644 --- a/src/plugins/shared_ux/public/services/platform.ts +++ b/src/plugins/shared_ux/public/services/platform.ts @@ -6,18 +6,21 @@ * Side Public License, v 1. */ +import { SharedUxPlatformService } from '@kbn/shared-ux-services'; +import { SharedUXPluginStartDeps } from '../types'; +import { KibanaPluginServiceFactory } from './types'; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXPlatformService`. + */ +export type PlatformServiceFactory = KibanaPluginServiceFactory< + SharedUxPlatformService, + SharedUXPluginStartDeps +>; + /** - * A services providing methods to interact with the Platform in which this plugin is - * running, (almost always Kibana). - * - * Rather than provide the entire `CoreStart` contract to components, we provide simplified - * abstractions around a use case specific to Shared UX. This way, we know exactly how the - * `CoreStart` and other plugins are used, like specifically which methods. This makes - * mocking and refactoring easier when upstream dependencies change. + * A factory function for creating a Kibana-based implementation of `SharedUXPlatformService`. */ -export interface SharedUXPlatformService { - /** - * Sets the fullscreen state of the chrome. - */ - setIsFullscreen: (isFullscreen: boolean) => void; -} +export const platformServiceFactory: PlatformServiceFactory = ({ coreStart }) => ({ + setIsFullscreen: (isVisible: boolean) => coreStart.chrome.setIsVisible(isVisible), +}); diff --git a/src/plugins/shared_ux/public/services/storybook/doc_links.ts b/src/plugins/shared_ux/public/services/storybook/doc_links.ts deleted file mode 100644 index 548d504336108..0000000000000 --- a/src/plugins/shared_ux/public/services/storybook/doc_links.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import { PluginServiceFactory } from '../types'; -import { SharedUXDocLinksService } from '../doc_links'; - -export type SharedUXDockLinksServiceFactory = PluginServiceFactory; - -/** - * A factory function for creating a Jest-based implementation of `SharedUXDocLinksService`. - */ -export const docLinksServiceFactory: SharedUXDockLinksServiceFactory = () => ({ - dataViewsDocsLink: 'https://www.elastic.co/guide/en/kibana/master/data-views.html', -}); diff --git a/src/plugins/shared_ux/public/services/storybook/editors.ts b/src/plugins/shared_ux/public/services/storybook/editors.ts deleted file mode 100644 index 248699d5beb95..0000000000000 --- a/src/plugins/shared_ux/public/services/storybook/editors.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import { action } from '@storybook/addon-actions'; -import { PluginServiceFactory } from '../types'; -import { SharedUxDataViewEditorProps, SharedUXEditorsService } from '../editors'; - -export type SharedUXEditorsServiceFactory = PluginServiceFactory; - -/** - * A factory function for creating a storybook implementation of `SharedUXEditorsService`. - */ -export const editorsServiceFactory: SharedUXEditorsServiceFactory = () => ({ - openDataViewEditor: action('openEditor') as SharedUXEditorsService['openDataViewEditor'] as ( - options: SharedUxDataViewEditorProps - ) => () => void, -}); diff --git a/src/plugins/shared_ux/public/services/storybook/permissions.ts b/src/plugins/shared_ux/public/services/storybook/permissions.ts deleted file mode 100644 index 299e06f31f123..0000000000000 --- a/src/plugins/shared_ux/public/services/storybook/permissions.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 or the Server - * Side Public License, v 1. - */ - -import { PluginServiceFactory } from '../types'; -import { SharedUXUserPermissionsService } from '../permissions'; - -export type SharedUXUserPermissionsServiceFactory = - PluginServiceFactory; - -/** - * A factory function for creating a storybook implementation of `SharedUXUserPermissionsService`. - */ -export const userPermissionsServiceFactory: SharedUXUserPermissionsServiceFactory = () => ({ - canCreateNewDataView: true, - canAccessFleet: true, -}); diff --git a/src/plugins/shared_ux/public/services/types.ts b/src/plugins/shared_ux/public/services/types.ts index 2645d5303a33b..15191a493d378 100644 --- a/src/plugins/shared_ux/public/services/types.ts +++ b/src/plugins/shared_ux/public/services/types.ts @@ -9,15 +9,6 @@ import { BehaviorSubject } from 'rxjs'; import { CoreStart, AppUpdater, PluginInitializerContext } from 'src/core/public'; -/** - * A factory function for creating one or more services. - * - * The `S` generic determines the shape of the API being produced. - * The `Parameters` generic determines what parameters are expected to - * create the service. - */ -export type PluginServiceFactory = (params: Parameters) => S; - /** * Parameters necessary to create a Kibana-based service, (e.g. during Plugin * startup or setup). diff --git a/src/plugins/shared_ux/tsconfig.json b/src/plugins/shared_ux/tsconfig.json index 069fe1d069b4a..9e9844d985987 100644 --- a/src/plugins/shared_ux/tsconfig.json +++ b/src/plugins/shared_ux/tsconfig.json @@ -10,7 +10,6 @@ "common/**/*", "public/**/*", "server/**/*", - ".storybook/**/*", "../../../typings/**/*" ], "references": [ @@ -19,9 +18,6 @@ }, { "path": "../data_view_editor/tsconfig.json" - }, - { - "path": "../data_views/tsconfig.json" } ] } diff --git a/x-pack/plugins/alerting/server/rules_client/lib/mapped_params_utils.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/mapped_params_utils.test.ts index d8618d0ed6c21..705fc1b05ede3 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/mapped_params_utils.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/mapped_params_utils.test.ts @@ -128,4 +128,23 @@ describe('modifyFilterKueryNode', () => { value: '40-medium', }); }); + + it('do NOT modify the resulting kuery node AST filter for alert params when they are not part of mapped params', () => { + // Make sure it works for both camel and snake case params + const astFilter = fromKueryExpression( + 'alert.attributes.name: "Rule I" and alert.attributes.tags: "fast" and alert.attributes.params.threat.tactic.name: Exfiltration' + ); + + modifyFilterKueryNode({ astFilter }); + + expect(astFilter.arguments[2].arguments[0]).toEqual({ + type: 'literal', + value: 'alert.attributes.params.threat.tactic.name', + }); + + expect(astFilter.arguments[2].arguments[1]).toEqual({ + type: 'literal', + value: 'Exfiltration', + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/mapped_params_utils.ts b/x-pack/plugins/alerting/server/rules_client/lib/mapped_params_utils.ts index b4d82990654c2..e9624d4604c46 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/mapped_params_utils.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/mapped_params_utils.ts @@ -145,7 +145,16 @@ export const modifyFilterKueryNode = ({ const firstAttribute = getFieldNameAttribute(fieldName, ['alert', 'attributes']); // Replace the ast.value for params to mapped_params if (firstAttribute === 'params') { - ast.value = getModifiedFilter(ast.value); + const attributeAfterParams = getFieldNameAttribute(fieldName, [ + 'alert', + 'attributes', + 'params', + ]); + if ( + MAPPED_PARAMS_PROPERTIES.includes(attributeAfterParams as keyof MappedParamsProperties) + ) { + ast.value = getModifiedFilter(ast.value); + } } } @@ -155,8 +164,16 @@ export const modifyFilterKueryNode = ({ // Replace the ast.value for params value to the modified mapped_params value if (firstAttribute === 'params' && ast.value) { - const attribute = getFieldNameAttribute(localFieldName, ['alert', 'attributes', 'params']); - ast.value = getModifiedValue(attribute, ast.value); + const attributeAfterParams = getFieldNameAttribute(localFieldName, [ + 'alert', + 'attributes', + 'params', + ]); + if ( + MAPPED_PARAMS_PROPERTIES.includes(attributeAfterParams as keyof MappedParamsProperties) + ) { + ast.value = getModifiedValue(attributeAfterParams, ast.value); + } } } }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.tsx index bf2a122ead42b..b0519d16bab10 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.tsx @@ -5,17 +5,7 @@ * 2.0. */ -import React from 'react'; - -import { EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; - -import { META_ENGINES_DOCS_URL } from '../../routes'; -import { - META_ENGINE_CREATION_FORM_META_ENGINE_DESCRIPTION, - META_ENGINE_CREATION_FORM_DOCUMENTATION_LINK, -} from '../meta_engine_creation/constants'; export const ENGINES_TITLE = i18n.translate('xpack.enterpriseSearch.appSearch.engines.title', { defaultMessage: 'Engines', @@ -31,24 +21,6 @@ export const META_ENGINES_TITLE = i18n.translate( { defaultMessage: 'Meta Engines' } ); -export const META_ENGINES_DESCRIPTION = ( - <> - {META_ENGINE_CREATION_FORM_META_ENGINE_DESCRIPTION} -
- - {META_ENGINE_CREATION_FORM_DOCUMENTATION_LINK} - - ), - }} - /> - -); - export const SOURCE_ENGINES_TITLE = i18n.translate( 'xpack.enterpriseSearch.appSearch.enginesOverview.metaEnginesTable.sourceEngines.title', { defaultMessage: 'Source Engines' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx index 62cf0fd0f6edf..90fc47a8d493a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.test.tsx @@ -156,19 +156,6 @@ describe('EnginesOverview', () => { }); }); - describe('when an account does not have a platinum license', () => { - it('renders a license call to action in place of the meta engines table', () => { - setMockValues({ - ...valuesWithEngines, - hasPlatinumLicense: false, - }); - const wrapper = shallow(); - - expect(wrapper.find('[data-test-subj="metaEnginesLicenseCTA"]')).toHaveLength(1); - expect(wrapper.find('[data-test-subj="appSearchMetaEngines"]')).toHaveLength(0); - }); - }); - describe('pagination', () => { const getTablePagination = (wrapper: ShallowWrapper) => wrapper.find(EnginesTable).prop('pagination'); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx index 27cdff5d69812..ef58369ee39e3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx @@ -11,7 +11,6 @@ import { useValues, useActions } from 'kea'; import { EuiSpacer } from '@elastic/eui'; -import { LicensingLogic, ManageLicenseButton } from '../../../shared/licensing'; import { EuiButtonTo } from '../../../shared/react_router_helpers'; import { convertMetaToPagination, handlePageChange } from '../../../shared/table_pagination'; import { AppLogic } from '../../app_logic'; @@ -30,12 +29,10 @@ import { CREATE_A_META_ENGINE_BUTTON_LABEL, ENGINES_TITLE, META_ENGINES_TITLE, - META_ENGINES_DESCRIPTION, } from './constants'; import { EnginesLogic } from './engines_logic'; export const EnginesOverview: React.FC = () => { - const { hasPlatinumLicense } = useValues(LicensingLogic); const { myRole: { canManageEngines, canManageMetaEngines }, } = useValues(AppLogic); @@ -58,8 +55,8 @@ export const EnginesOverview: React.FC = () => { }, [enginesMeta.page.current]); useEffect(() => { - if (hasPlatinumLicense) loadMetaEngines(); - }, [hasPlatinumLicense, metaEnginesMeta.page.current]); + loadMetaEngines(); + }, [metaEnginesMeta.page.current]); return ( { /> - {hasPlatinumLicense ? ( - {META_ENGINES_TITLE}} - titleSize="s" - action={ - canManageMetaEngines && ( - - {CREATE_A_META_ENGINE_BUTTON_LABEL} - - ) - } - data-test-subj="appSearchMetaEngines" - > - } - onChange={handlePageChange(onMetaEnginesPagination)} - /> - - ) : ( - {META_ENGINES_TITLE}} - titleSize="s" - subtitle={META_ENGINES_DESCRIPTION} - action={} - data-test-subj="metaEnginesLicenseCTA" + {META_ENGINES_TITLE}} + titleSize="s" + action={ + canManageMetaEngines && ( + + {CREATE_A_META_ENGINE_BUTTON_LABEL} + + ) + } + data-test-subj="appSearchMetaEngines" + > + } + onChange={handlePageChange(onMetaEnginesPagination)} /> - )} + ); diff --git a/x-pack/plugins/file_upload/public/components/geo_upload_form/geo_file_picker.tsx b/x-pack/plugins/file_upload/public/components/geo_upload_form/geo_file_picker.tsx index a349a5b82d372..f813abb1510d7 100644 --- a/x-pack/plugins/file_upload/public/components/geo_upload_form/geo_file_picker.tsx +++ b/x-pack/plugins/file_upload/public/components/geo_upload_form/geo_file_picker.tsx @@ -9,7 +9,6 @@ import React, { Component } from 'react'; import { EuiFilePicker, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { MB } from '../../../common/constants'; -import { getMaxBytesFormatted } from '../../importer/get_max_bytes'; import { GEO_FILE_TYPES, geoImporterFactory } from '../../importer/geo'; import type { GeoFileImporter, GeoFilePreview } from '../../importer/geo'; @@ -135,11 +134,6 @@ export class GeoFilePicker extends Component { defaultMessage: 'Formats accepted: {fileTypes}', values: { fileTypes: GEO_FILE_TYPES.join(', ') }, })} -
- {i18n.translate('xpack.fileUpload.geoFilePicker.maxSize', { - defaultMessage: 'Max size: {maxFileSize}', - values: { maxFileSize: getMaxBytesFormatted() }, - })} ); } diff --git a/x-pack/plugins/file_upload/public/importer/geo/index.ts b/x-pack/plugins/file_upload/public/importer/geo/index.ts index fd75d17ec5e71..a88249194513d 100644 --- a/x-pack/plugins/file_upload/public/importer/geo/index.ts +++ b/x-pack/plugins/file_upload/public/importer/geo/index.ts @@ -11,9 +11,10 @@ import { ShapefileImporter, SHAPEFILE_TYPES } from './shapefile_importer'; import { getFileExtension, validateFile } from '../validate_file'; export const GEO_FILE_TYPES = [...GEOJSON_FILE_TYPES, ...SHAPEFILE_TYPES]; +const OPTIONS = { checkSizeLimit: false }; export function geoImporterFactory(file: File): GeoFileImporter { - validateFile(file, GEO_FILE_TYPES); + validateFile(file, GEO_FILE_TYPES, OPTIONS); const extension = getFileExtension(file); return GEOJSON_FILE_TYPES.includes(extension) diff --git a/x-pack/plugins/file_upload/public/importer/validate_file.ts b/x-pack/plugins/file_upload/public/importer/validate_file.ts index 0a087e91a63f3..b3f69c7b82921 100644 --- a/x-pack/plugins/file_upload/public/importer/validate_file.ts +++ b/x-pack/plugins/file_upload/public/importer/validate_file.ts @@ -16,8 +16,8 @@ export function getFileExtension(file: File) { return '.' + extension; } -export function validateFile(file: File, types: string[]) { - if (file.size > getMaxBytes()) { +export function validateFile(file: File, types: string[], options?: { checkSizeLimit?: boolean }) { + if (file.size > getMaxBytes() && options?.checkSizeLimit) { throw new Error( i18n.translate('xpack.fileUpload.fileSizeError', { defaultMessage: 'File size {fileSize} exceeds maximum file size of {maxFileSize}', diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts index 127d798cb76c2..23bdf33f035a3 100644 --- a/x-pack/plugins/lens/server/routes/field_stats.ts +++ b/x-pack/plugins/lens/server/routes/field_stats.ts @@ -80,14 +80,7 @@ export async function initFieldsRoute(setup: CoreSetup) { }, }; - const runtimeMappings = indexPattern.fields - .filter((f) => f.runtimeField) - .reduce((acc, f) => { - if (!f.runtimeField) return acc; - // @ts-expect-error The MappingRuntimeField from @elastic/elasticsearch does not expose the "composite" runtime type yet - acc[f.name] = f.runtimeField; - return acc; - }, {} as Record); + const runtimeMappings = indexPattern.getRuntimeMappings(); const search = async (aggs: Record) => { const result = await requestClient.search({ diff --git a/x-pack/plugins/monitoring/server/config.test.ts b/x-pack/plugins/monitoring/server/config.test.ts index 1e1d8c8f25602..699ea739d6a73 100644 --- a/x-pack/plugins/monitoring/server/config.test.ts +++ b/x-pack/plugins/monitoring/server/config.test.ts @@ -78,6 +78,7 @@ describe('config schema', () => { "ignoreVersionMismatch": false, "logFetchCount": 10, "logQueries": false, + "maxSockets": Infinity, "pingTimeout": "PT30S", "requestHeadersWhitelist": Array [ "authorization", diff --git a/x-pack/plugins/session_view/common/constants.ts b/x-pack/plugins/session_view/common/constants.ts index 4d0d475859d84..55126973fc08c 100644 --- a/x-pack/plugins/session_view/common/constants.ts +++ b/x-pack/plugins/session_view/common/constants.ts @@ -26,3 +26,5 @@ export const ENTRY_SESSION_ENTITY_ID_PROPERTY = 'process.entry_leader.entity_id' // 3. Fewer round trips to the backend! export const PROCESS_EVENTS_PER_PAGE = 1000; export const MOUSE_EVENT_PLACEHOLDER = { stopPropagation: () => undefined } as React.MouseEvent; + +export const DEBOUNCE_TIMEOUT = 500; diff --git a/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.test.tsx b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.test.tsx new file mode 100644 index 0000000000000..a75977535a12f --- /dev/null +++ b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.test.tsx @@ -0,0 +1,37 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { AppContextTestRender, createAppRootMockRenderer } from '../../test'; +import { BUTTON_TEST_ID, BackToInvestigatedAlert } from './index'; + +describe('BackToInvestigatedAlert component', () => { + let render: () => ReturnType; + let renderResult: ReturnType; + let mockedContext: AppContextTestRender; + + beforeEach(() => { + mockedContext = createAppRootMockRenderer(); + }); + + describe('When BackToInvestigatedAlert is mounted', () => { + it('should render basic back button', async () => { + renderResult = mockedContext.render(); + + expect(renderResult.queryByTestId(BUTTON_TEST_ID)).toBeTruthy(); + }); + it('should call onClick function by clicking the badge', async () => { + const onClick = jest.fn(); + renderResult = mockedContext.render(); + + const badgeButton = renderResult.queryByTestId(BUTTON_TEST_ID); + expect(badgeButton).toBeTruthy(); + badgeButton?.click(); + expect(onClick).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx new file mode 100644 index 0000000000000..6252335c29f61 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx @@ -0,0 +1,47 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiBadge } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useStyles } from './styles'; + +interface BackInvestigatedAlertDeps { + isDisplayedAbove?: boolean; + onClick: () => void; +} + +export const BUTTON_TEST_ID = 'sessionView:backToInvestigatedAlert'; + +/** + * Jump back to investigated alert button, should appear + * when user scrolls the investigated event out of view. + */ +export const BackToInvestigatedAlert = ({ + isDisplayedAbove = false, + onClick, +}: BackInvestigatedAlertDeps) => { + const styles = useStyles({ isDisplayedAbove }); + + return ( +

+ ); +}; diff --git a/x-pack/plugins/session_view/public/components/back_to_investigated_alert/styles.ts b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/styles.ts new file mode 100644 index 0000000000000..205f116e01e75 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/styles.ts @@ -0,0 +1,58 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import { useEuiTheme } from '@elastic/eui'; +import { euiLightVars as theme } from '@kbn/ui-theme'; +import { CSSObject } from '@emotion/react'; + +interface StylesDeps { + isDisplayedAbove: boolean; +} + +export const useStyles = ({ isDisplayedAbove }: StylesDeps) => { + const { euiTheme } = useEuiTheme(); + + const cached = useMemo(() => { + const { size, colors, font } = euiTheme; + + const buttonBackgroundColor = colors.primary; + + const container: CSSObject = { + position: 'absolute', + height: size.xxxxl, + width: '100%', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }; + + const jumpBackBadge: CSSObject = { + position: 'sticky', + pointerEvents: 'auto', + cursor: 'pointer', + fontWeight: font.weight.regular, + }; + + if (isDisplayedAbove) { + container.top = 0; + container.background = `linear-gradient(180deg, ${theme.euiColorLightestShade} 0%, transparent 100%)`; + } else { + container.bottom = 0; + container.background = `linear-gradient(360deg, ${theme.euiColorLightestShade} 0%, transparent 100%)`; + } + + return { + container, + jumpBackBadge, + buttonBackgroundColor, + }; + }, [isDisplayedAbove, euiTheme]); + + return cached; +}; diff --git a/x-pack/plugins/session_view/public/components/process_tree/index.test.tsx b/x-pack/plugins/session_view/public/components/process_tree/index.test.tsx index ac6807984ba83..71ee20e07d642 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree/index.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { mockData } from '../../../common/mocks/constants/session_view_process.mock'; +import { Process } from '../../../common/types/process_tree'; import { AppContextTestRender, createAppRootMockRenderer } from '../../test'; import { ProcessImpl } from './hooks'; import { ProcessTree } from './index'; @@ -15,6 +16,7 @@ describe('ProcessTree component', () => { let render: () => ReturnType; let renderResult: ReturnType; let mockedContext: AppContextTestRender; + const sessionLeader = mockData[0].events[0]; beforeEach(() => { mockedContext = createAppRootMockRenderer(); @@ -24,7 +26,7 @@ describe('ProcessTree component', () => { it('should render given a valid sessionEntityId and data', () => { renderResult = mockedContext.render( true} @@ -38,12 +40,58 @@ describe('ProcessTree component', () => { expect(renderResult.queryAllByTestId('sessionView:processTreeNode')).toBeTruthy(); }); + it('should auto select jumpToEvent when it exists and without selectedProcess', () => { + const jumpToEvent = mockData[0].events[2]; + const onProcessSelected = jest.fn((process: Process | null) => { + expect(process?.id).toBe(jumpToEvent.process.entity_id); + }); + renderResult = mockedContext.render( + true} + hasNextPage={false} + fetchPreviousPage={() => true} + hasPreviousPage={false} + jumpToEvent={jumpToEvent} + onProcessSelected={onProcessSelected} + /> + ); + expect(renderResult.queryByTestId('sessionView:sessionViewProcessTree')).toBeTruthy(); + expect(renderResult.queryAllByTestId('sessionView:processTreeNode')).toBeTruthy(); + + expect(onProcessSelected).toHaveBeenCalled(); + }); + + it('should auto select session leader without selectedProcess', () => { + const onProcessSelected = jest.fn((process: Process | null) => { + expect(process?.id).toBe(sessionLeader.process.entity_id); + }); + renderResult = mockedContext.render( + true} + hasNextPage={false} + fetchPreviousPage={() => true} + hasPreviousPage={false} + onProcessSelected={onProcessSelected} + /> + ); + expect(renderResult.queryByTestId('sessionView:sessionViewProcessTree')).toBeTruthy(); + expect(renderResult.queryAllByTestId('sessionView:processTreeNode')).toBeTruthy(); + + expect(onProcessSelected).toHaveBeenCalled(); + }); + it('should insert a DOM element used to highlight a process when selectedProcess is set', () => { const mockSelectedProcess = new ProcessImpl(mockData[0].events[0].process.entity_id); renderResult = mockedContext.render( true} @@ -69,7 +117,7 @@ describe('ProcessTree component', () => { renderResult.rerender( true} diff --git a/x-pack/plugins/session_view/public/components/process_tree/index.tsx b/x-pack/plugins/session_view/public/components/process_tree/index.tsx index 171836b510815..652d2209790b0 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree/index.tsx @@ -4,10 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useRef, useEffect, useLayoutEffect, useCallback } from 'react'; +import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react'; import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { ProcessTreeNode } from '../process_tree_node'; +import { BackToInvestigatedAlert } from '../back_to_investigated_alert'; import { useProcessTree } from './hooks'; import { Process, ProcessEventsPage, ProcessEvent } from '../../../common/types/process_tree'; import { useScroll } from '../../hooks/use_scroll'; @@ -33,7 +34,7 @@ interface ProcessTreeDeps { // currently selected process selectedProcess?: Process | null; - onProcessSelected: (process: Process) => void; + onProcessSelected: (process: Process | null) => void; setSearchResults?: (results: Process[]) => void; } @@ -51,6 +52,8 @@ export const ProcessTree = ({ onProcessSelected, setSearchResults, }: ProcessTreeDeps) => { + const [isInvestigatedEventVisible, setIsInvestigatedEventVisible] = useState(true); + const [isInvestigatedEventAbove, setIsInvestigatedEventAbove] = useState(false); const styles = useStyles(); const { sessionLeader, processMap, searchResults } = useProcessTree({ @@ -62,6 +65,23 @@ export const ProcessTree = ({ const scrollerRef = useRef(null); const selectionAreaRef = useRef(null); + const onChangeJumpToEventVisibility = useCallback( + (isVisible: boolean, isAbove: boolean) => { + if (isVisible !== isInvestigatedEventVisible) { + setIsInvestigatedEventVisible(isVisible); + } + if (!isVisible && isAbove !== isInvestigatedEventAbove) { + setIsInvestigatedEventAbove(isAbove); + } + }, + [isInvestigatedEventVisible, isInvestigatedEventAbove] + ); + + const handleBackToInvestigatedAlert = useCallback(() => { + onProcessSelected(null); + setIsInvestigatedEventVisible(true); + }, [onProcessSelected]); + useEffect(() => { if (setSearchResults) { setSearchResults(searchResults); @@ -144,35 +164,46 @@ export const ProcessTree = ({ }, [jumpToEvent, processMap, onProcessSelected, selectProcess, selectedProcess, sessionLeader]); return ( -
- {hasPreviousPage && ( - - - - )} - {sessionLeader && ( - - )} + <>
- {hasNextPage && ( - - - + ref={scrollerRef} + css={styles.scroller} + data-test-subj="sessionView:sessionViewProcessTree" + > + {hasPreviousPage && ( + + + + )} + {sessionLeader && ( + + )} +
+ {hasNextPage && ( + + + + )} +
+ {!isInvestigatedEventVisible && ( + )} -
+ ); }; diff --git a/x-pack/plugins/session_view/public/components/process_tree/styles.ts b/x-pack/plugins/session_view/public/components/process_tree/styles.ts index 618285d88be7a..207cc55e49582 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree/styles.ts @@ -13,14 +13,15 @@ export const useStyles = () => { const { euiTheme } = useEuiTheme(); const cached = useMemo(() => { - const defaultSelectionColor = euiTheme.colors.primary; + const { colors, font, size } = euiTheme; + const defaultSelectionColor = colors.primary; const scroller: CSSObject = { position: 'relative', - fontFamily: euiTheme.font.familyCode, + fontFamily: font.familyCode, overflow: 'auto', height: '100%', - backgroundColor: euiTheme.colors.lightestShade, + backgroundColor: colors.lightestShade, }; const selectionArea: CSSObject = { @@ -32,10 +33,11 @@ export const useStyles = () => { backgroundColor: defaultSelectionColor, pointerEvents: 'none', opacity: 0.1, + transform: `translateY(-${size.xs})`, }; - const defaultSelected = transparentize(euiTheme.colors.primary, 0.008); - const alertSelected = transparentize(euiTheme.colors.danger, 0.008); + const defaultSelected = transparentize(colors.primary, 0.008); + const alertSelected = transparentize(colors.danger, 0.008); return { scroller, diff --git a/x-pack/plugins/session_view/public/components/process_tree_alerts/styles.ts b/x-pack/plugins/session_view/public/components/process_tree_alerts/styles.ts index ddd4a04d2f991..68928e137f798 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alerts/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_alerts/styles.ts @@ -16,8 +16,7 @@ export const useStyles = () => { const { size, colors, border } = euiTheme; const container: CSSObject = { - marginTop: size.s, - marginRight: size.s, + margin: `${size.xs} ${size.s} 0 ${size.xs}`, color: colors.text, padding: `${size.s} 0`, borderStyle: 'solid', diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx index 6b85ca1d208e1..de73111053618 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { RefObject } from 'react'; import userEvent from '@testing-library/user-event'; import { processMock, @@ -13,12 +13,30 @@ import { sessionViewAlertProcessMock, } from '../../../common/mocks/constants/session_view_process.mock'; import { AppContextTestRender, createAppRootMockRenderer } from '../../test'; -import { ProcessTreeNode } from './index'; +import { ProcessDeps, ProcessTreeNode } from './index'; +import { Cancelable } from 'lodash'; +import { DEBOUNCE_TIMEOUT } from '../../../common/constants'; + +jest.useFakeTimers('modern'); describe('ProcessTreeNode component', () => { let render: () => ReturnType; let renderResult: ReturnType; let mockedContext: AppContextTestRender; + const props: ProcessDeps = { + process: processMock, + scrollerRef: { + current: { + getBoundingClientRect: () => ({ + y: 0, + }), + clientHeight: 500, + addEventListener: () => {}, + removeEventListener: () => {}, + }, + } as unknown as RefObject, + onChangeJumpToEventVisibility: jest.fn(), + }; beforeEach(() => { mockedContext = createAppRootMockRenderer(); @@ -26,15 +44,13 @@ describe('ProcessTreeNode component', () => { describe('When ProcessTreeNode is mounted', () => { it('should render given a valid process', async () => { - renderResult = mockedContext.render(); + renderResult = mockedContext.render(); expect(renderResult.queryByTestId('sessionView:processTreeNode')).toBeTruthy(); }); it('should have an alternate rendering for a session leader', async () => { - renderResult = mockedContext.render( - - ); + renderResult = mockedContext.render(); expect(renderResult.container.textContent).toEqual(' bash started by vagrant'); }); @@ -51,7 +67,9 @@ describe('ProcessTreeNode component', () => { hasExec: () => true, }; - renderResult = mockedContext.render(); + renderResult = mockedContext.render( + + ); expect(renderResult.queryByTestId('sessionView:processTreeNodeExecIcon')).toBeTruthy(); expect(renderResult.queryByTestId('sessionView:processTreeNodeExitCode')).toBeTruthy(); @@ -70,10 +88,38 @@ describe('ProcessTreeNode component', () => { }), }; - renderResult = mockedContext.render(); + renderResult = mockedContext.render( + + ); expect(renderResult.queryByTestId('sessionView:processTreeNodeExitCode')).toBeFalsy(); }); + it('calls onChangeJumpToEventVisibility with isVisible false if jumpToEvent is not visible', async () => { + const onChangeJumpToEventVisibility = jest.fn(); + const scrollerRef = { + current: { + ...props.scrollerRef.current, + clientHeight: -500, + addEventListener: (_event: string, scrollFn: (() => void) & Cancelable) => { + scrollFn(); + }, + removeEventListener: (_event: string, _fn: (() => void) & Cancelable) => {}, + }, + } as RefObject; + + renderResult = mockedContext.render( + + ); + + jest.advanceTimersByTime(DEBOUNCE_TIMEOUT); + expect(onChangeJumpToEventVisibility).toHaveBeenCalled(); + }); + it('renders Root Escalation flag properly', async () => { const rootEscalationProcessMock: typeof processMock = { ...processMock, @@ -96,7 +142,9 @@ describe('ProcessTreeNode component', () => { }), }; - renderResult = mockedContext.render(); + renderResult = mockedContext.render( + + ); expect( renderResult.queryByTestId('sessionView:processTreeNodeRootEscalationFlag') @@ -107,7 +155,7 @@ describe('ProcessTreeNode component', () => { const onProcessSelected = jest.fn(); renderResult = mockedContext.render( - + ); userEvent.click(renderResult.getByTestId('sessionView:processTreeNodeRow')); @@ -120,7 +168,7 @@ describe('ProcessTreeNode component', () => { const onProcessSelected = jest.fn(); renderResult = mockedContext.render( - + ); // @ts-ignore @@ -142,14 +190,16 @@ describe('ProcessTreeNode component', () => { ), getAlerts: () => [sessionViewAlertProcessMock.getAlerts()[0]], }; - renderResult = mockedContext.render(); + renderResult = mockedContext.render( + + ); expect(renderResult.queryByTestId('processTreeNodeAlertButton')).toBeTruthy(); expect(renderResult.queryByTestId('processTreeNodeAlertButton')?.textContent).toBe('Alert'); }); it('renders Alerts button when process has more than one alerts', async () => { renderResult = mockedContext.render( - + ); expect(renderResult.queryByTestId('processTreeNodeAlertButton')).toBeTruthy(); @@ -166,7 +216,9 @@ describe('ProcessTreeNode component', () => { (item) => (item = sessionViewAlertProcessMock.getAlerts()[0]) ), }; - renderResult = mockedContext.render(); + renderResult = mockedContext.render( + + ); expect(renderResult.queryByTestId('processTreeNodeAlertButton')).toBeTruthy(); expect(renderResult.queryByTestId('processTreeNodeAlertButton')?.textContent).toBe( @@ -175,7 +227,7 @@ describe('ProcessTreeNode component', () => { }); it('toggle Alert Details button when Alert button is clicked', async () => { renderResult = mockedContext.render( - + ); userEvent.click(renderResult.getByTestId('processTreeNodeAlertButton')); expect(renderResult.queryByTestId('sessionView:sessionViewAlertDetails')).toBeTruthy(); @@ -190,7 +242,9 @@ describe('ProcessTreeNode component', () => { getChildren: () => [childProcessMock], }; - renderResult = mockedContext.render(); + renderResult = mockedContext.render( + + ); expect( renderResult.queryByTestId('sessionView:processTreeNodeChildProcessesButton') @@ -202,7 +256,9 @@ describe('ProcessTreeNode component', () => { getChildren: () => [childProcessMock], }; - renderResult = mockedContext.render(); + renderResult = mockedContext.render( + + ); expect(renderResult.getAllByTestId('sessionView:processTreeNode')).toHaveLength(1); @@ -222,7 +278,7 @@ describe('ProcessTreeNode component', () => { // set a mock search matched indicator for the process (typically done by ProcessTree/helpers.ts) processMock.searchMatched = '/vagrant'; - renderResult = mockedContext.render(); + renderResult = mockedContext.render(); expect( renderResult.getByTestId('sessionView:processNodeSearchHighlight').textContent diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx index f73cc706fd398..2b7a4f8c79469 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx @@ -19,21 +19,27 @@ import React, { MouseEvent, useCallback, useMemo, + RefObject, } from 'react'; import { EuiButton, EuiIcon } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { Process } from '../../../common/types/process_tree'; -import { useStyles } from './styles'; +import { useVisible } from '../../hooks/use_visible'; import { ProcessTreeAlerts } from '../process_tree_alerts'; import { SessionLeaderButton, AlertButton, ChildrenProcessesButton } from './buttons'; import { useButtonStyles } from './use_button_styles'; -interface ProcessDeps { +import { useStyles } from './styles'; + +export interface ProcessDeps { process: Process; isSessionLeader?: boolean; depth?: number; onProcessSelected?: (process: Process) => void; + jumpToEventID?: string; jumpToAlertID?: string; selectedProcessId?: string; + scrollerRef: RefObject; + onChangeJumpToEventVisibility: (isVisible: boolean, isAbove: boolean) => void; } /** @@ -44,8 +50,11 @@ export function ProcessTreeNode({ isSessionLeader = false, depth = 0, onProcessSelected, + jumpToEventID, jumpToAlertID, selectedProcessId, + scrollerRef, + onChangeJumpToEventVisibility, }: ProcessDeps) { const textRef = useRef(null); @@ -71,6 +80,14 @@ export function ProcessTreeNode({ const styles = useStyles({ depth, hasAlerts, hasInvestigatedAlert }); const buttonStyles = useButtonStyles({}); + const nodeRef = useVisible({ + viewPortEl: scrollerRef.current, + visibleCallback: (isVisible, isAbove) => { + onChangeJumpToEventVisibility(isVisible, isAbove); + }, + shouldAddListener: jumpToEventID === process.id, + }); + // Automatically expand alerts list when investigating an alert useEffect(() => { if (hasInvestigatedAlert) { @@ -157,6 +174,7 @@ export function ProcessTreeNode({ key={id + searchMatched} css={styles.processNode} data-test-subj="sessionView:processTreeNode" + ref={nodeRef} > {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
); })} diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts b/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts index 8c077b6dfbbd7..bfe247d6ce4b5 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts @@ -39,7 +39,6 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps marginLeft: size.base, paddingLeft: size.s, borderLeft: border.editable, - marginTop: size.s, }; /** @@ -67,12 +66,10 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps display: 'block', cursor: 'pointer', position: 'relative', - margin: `${size.s} 0px`, - '&:not(:first-of-type)': { - marginTop: size.s, - }, + padding: `${size.xs} 0px`, '&:hover:before': { backgroundColor: hoverColor, + transform: `translateY(-${size.xs})`, }, '&:before': { position: 'absolute', @@ -83,6 +80,7 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps borderLeft: `${size.xs} solid ${borderColor}`, backgroundColor: bgColor, width: `calc(100% + ${depth} * ${TREE_INDENT})`, + transform: `translateY(-${size.xs})`, }, }; diff --git a/x-pack/plugins/session_view/public/components/session_view/index.tsx b/x-pack/plugins/session_view/public/components/session_view/index.tsx index 9535a604167cc..58899b2bf5c75 100644 --- a/x-pack/plugins/session_view/public/components/session_view/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view/index.tsx @@ -31,7 +31,7 @@ export const SessionView = ({ sessionEntityId, height, jumpToEvent }: SessionVie const styles = useStyles({ height }); - const onProcessSelected = useCallback((process: Process) => { + const onProcessSelected = useCallback((process: Process | null) => { setSelectedProcess(process); }, []); diff --git a/x-pack/plugins/session_view/public/components/session_view/styles.ts b/x-pack/plugins/session_view/public/components/session_view/styles.ts index d7159ec5b1b39..76348b9d37b6e 100644 --- a/x-pack/plugins/session_view/public/components/session_view/styles.ts +++ b/x-pack/plugins/session_view/public/components/session_view/styles.ts @@ -20,6 +20,7 @@ export const useStyles = ({ height = 500 }: StylesDeps) => { const processTree: CSSObject = { height: `${height}px`, paddingTop: euiTheme.size.s, + position: 'relative', }; const detailPanel: CSSObject = { diff --git a/x-pack/plugins/session_view/public/hooks/use_scroll.ts b/x-pack/plugins/session_view/public/hooks/use_scroll.ts index 716e35dbb0987..7fa5004ff4822 100644 --- a/x-pack/plugins/session_view/public/hooks/use_scroll.ts +++ b/x-pack/plugins/session_view/public/hooks/use_scroll.ts @@ -7,9 +7,9 @@ import { useEffect } from 'react'; import _ from 'lodash'; +import { DEBOUNCE_TIMEOUT } from '../../common/constants'; const SCROLL_END_BUFFER_HEIGHT = 20; -const DEBOUNCE_TIMEOUT = 500; function getScrollPosition(div: HTMLElement) { if (div) { diff --git a/x-pack/plugins/session_view/public/hooks/use_visible.ts b/x-pack/plugins/session_view/public/hooks/use_visible.ts new file mode 100644 index 0000000000000..19f628819fb56 --- /dev/null +++ b/x-pack/plugins/session_view/public/hooks/use_visible.ts @@ -0,0 +1,65 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useRef, MutableRefObject, useEffect } from 'react'; +import { debounce } from 'lodash'; +import { DEBOUNCE_TIMEOUT } from '../../common/constants'; + +interface IUseVisibleDeps { + viewPortEl: HTMLDivElement | null; + visibleCallback: (isVisible: boolean, isAbove: boolean) => void; + shouldAddListener?: boolean; + offset?: number; + debounceTimeout?: number; +} + +/** + * Check if an element is in viewport + + * @param {HTMLDivElement} viewPortEl - View port element of where the scroll takes place + * @param {function} visibleCallback - callback called onScroll, expects (isVisbile: boolean, isAbove: boolean) as params + * @param {boolean} shouldAddListener - if useVisible hook should add the scroll listener + * @param {number} offset - Number of pixels up to the observable element from the top + * @param {number} debounceTimeout - debounce timeout, in ms + */ +export function useVisible({ + viewPortEl, + visibleCallback, + shouldAddListener = false, + offset = 0, + debounceTimeout = DEBOUNCE_TIMEOUT, +}: IUseVisibleDeps): MutableRefObject { + const currentElement = useRef(null); + + const onScroll = debounce(() => { + if (!currentElement.current || !viewPortEl) { + return; + } + + const { height: elHeight, y: elTop } = currentElement.current.getBoundingClientRect(); + const { y: viewPortElTop } = viewPortEl.getBoundingClientRect(); + + const viewPortElBottom = viewPortElTop + viewPortEl.clientHeight; + const elBottom = elTop + elHeight; + const isVisible = elBottom + offset >= viewPortElTop && elTop - offset <= viewPortElBottom; + + // if elBottom + offset < viewPortElTop, the currentElement is above the current scroll window + visibleCallback(isVisible, elBottom + offset < viewPortElTop); + }, debounceTimeout); + + useEffect(() => { + if (shouldAddListener) { + viewPortEl?.addEventListener('scroll', onScroll); + } + return () => { + if (shouldAddListener) { + viewPortEl?.removeEventListener('scroll', onScroll); + } + }; + }); + + return currentElement; +} diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ea47bedb67152..864a4a4fc5ce7 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -9122,7 +9122,6 @@ "xpack.enterpriseSearch.appSearch.metaEngineCreation.successMessage": "Le métamoteur \"{name}\" a été créé", "xpack.enterpriseSearch.appSearch.metaEngineCreation.title": "Créer un métamoteur", "xpack.enterpriseSearch.appSearch.metaEngines.title": "Métamoteurs", - "xpack.enterpriseSearch.appSearch.metaEngines.upgradeDescription": "Veuillez {readDocumentationLink} pour en savoir plus ou effectuer une mise à niveau vers une licence Platinum pour commencer.", "xpack.enterpriseSearch.appSearch.multiInputRows.addValueButtonLabel": "Ajouter une valeur", "xpack.enterpriseSearch.appSearch.multiInputRows.inputRowPlaceholder": "Entrer une valeur", "xpack.enterpriseSearch.appSearch.multiInputRows.removeValueButtonLabel": "Retirer une valeur", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8f96343daced1..6f386dfeccdd2 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5099,8 +5099,8 @@ "share.urlService.redirect.RedirectManager.missingParamLocator": "ロケーターIDが指定されていません。URLで「l」検索パラメーターを指定します。これは既存のロケーターIDにしてください。", "share.urlService.redirect.RedirectManager.missingParamParams": "ロケーターパラメーターが指定されていません。URLで「p」検索パラメーターを指定します。これはロケーターパラメーターのJSONシリアル化オブジェクトにしてください。", "share.urlService.redirect.RedirectManager.missingParamVersion": "ロケーターパラメーターバージョンが指定されていません。URLで「v」検索パラメーターを指定します。これはロケーターパラメーターが生成されたときのKibanaのリリースバージョンです。", - "sharedUX.exitFullScreenButton.exitFullScreenModeButtonText": "全画面を終了", - "sharedUX.exitFullScreenButton.fullScreenModeDescription": "ESC キーで全画面モードを終了します。", + "sharedUXComponents.exitFullScreenButton.exitFullScreenModeButtonText": "全画面を終了", + "sharedUXComponents.exitFullScreenButton.fullScreenModeDescription": "ESC キーで全画面モードを終了します。", "telemetry.callout.appliesSettingTitle": "この設定に加えた変更は {allOfKibanaText} に適用され、自動的に保存されます。", "telemetry.callout.appliesSettingTitle.allOfKibanaText": "Kibana のすべて", "telemetry.callout.clusterStatisticsDescription": "これは収集される基本的なクラスター統計の例です。インデックス、シャード、ノードの数が含まれます。監視がオンになっているかどうかなどのハイレベルの使用統計も含まれます。", @@ -10750,7 +10750,6 @@ "xpack.enterpriseSearch.appSearch.metaEngineCreation.successMessage": "メタエンジン'{name}'が作成されました", "xpack.enterpriseSearch.appSearch.metaEngineCreation.title": "メタエンジンを作成", "xpack.enterpriseSearch.appSearch.metaEngines.title": "メタエンジン", - "xpack.enterpriseSearch.appSearch.metaEngines.upgradeDescription": "詳細またはPlatinumライセンスにアップグレードして開始するには、{readDocumentationLink}。", "xpack.enterpriseSearch.appSearch.multiInputRows.addValueButtonLabel": "値を追加", "xpack.enterpriseSearch.appSearch.multiInputRows.inputRowPlaceholder": "値を入力", "xpack.enterpriseSearch.appSearch.multiInputRows.removeValueButtonLabel": "値を削除", @@ -11629,7 +11628,6 @@ "xpack.fileUpload.fileTypeError": "ファイルは使用可能なタイプのいずれかではありません。{types}", "xpack.fileUpload.geoFilePicker.acceptedFormats": "使用可能な形式:{fileTypes}", "xpack.fileUpload.geoFilePicker.filePicker": "ファイルを選択するかドラッグ &amp; ドロップしてください", - "xpack.fileUpload.geoFilePicker.maxSize": "最大サイズ:{maxFileSize}", "xpack.fileUpload.geoFilePicker.noFeaturesDetected": "選択したファイルには特徴量がありません。", "xpack.fileUpload.geoFilePicker.previewSummary": "{numFeatures}個の特徴量。ファイルの{previewCoverage}%。", "xpack.fileUpload.geojsonImporter.noGeometry": "特長量には必須フィールド「ジオメトリ」が含まれていません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f49f943fbd2e3..6a8d154b5b6c0 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5108,8 +5108,8 @@ "share.urlService.redirect.RedirectManager.missingParamLocator": "未指定定位器 ID。在 URL 中指定“l”搜索参数,其应为现有定位器 ID。", "share.urlService.redirect.RedirectManager.missingParamParams": "定位器参数未指定。在 URL 中指定“p”搜索参数,其应为定位器参数的 JSON 序列化对象。", "share.urlService.redirect.RedirectManager.missingParamVersion": "定位器参数版本未指定。在 URL 中指定“v”搜索参数,其应为生成定位器参数时 Kibana 的版本。", - "sharedUX.exitFullScreenButton.exitFullScreenModeButtonText": "退出全屏", - "sharedUX.exitFullScreenButton.fullScreenModeDescription": "在全屏模式下,按 ESC 键可退出。", + "sharedUXComponents.exitFullScreenButton.exitFullScreenModeButtonText": "退出全屏", + "sharedUXComponents.exitFullScreenButton.fullScreenModeDescription": "在全屏模式下,按 ESC 键可退出。", "telemetry.callout.appliesSettingTitle": "对此设置的更改将应用到{allOfKibanaText} 且会自动保存。", "telemetry.callout.appliesSettingTitle.allOfKibanaText": "整个 Kibana", "telemetry.callout.clusterStatisticsDescription": "这是我们将收集的基本集群统计信息的示例。其包括索引、分片和节点的数目。还包括概括性的使用情况统计信息,例如监测是否打开。", @@ -10771,7 +10771,6 @@ "xpack.enterpriseSearch.appSearch.metaEngineCreation.successMessage": "元引擎“{name}”已创建", "xpack.enterpriseSearch.appSearch.metaEngineCreation.title": "创建元引擎", "xpack.enterpriseSearch.appSearch.metaEngines.title": "元引擎", - "xpack.enterpriseSearch.appSearch.metaEngines.upgradeDescription": "{readDocumentationLink}以了解更多信息,或升级到白金级许可证以开始。", "xpack.enterpriseSearch.appSearch.multiInputRows.addValueButtonLabel": "添加值", "xpack.enterpriseSearch.appSearch.multiInputRows.inputRowPlaceholder": "输入值", "xpack.enterpriseSearch.appSearch.multiInputRows.removeValueButtonLabel": "删除值", @@ -11650,7 +11649,6 @@ "xpack.fileUpload.fileTypeError": "文件不是可接受类型之一:{types}", "xpack.fileUpload.geoFilePicker.acceptedFormats": "接受的格式:{fileTypes}", "xpack.fileUpload.geoFilePicker.filePicker": "选择或拖放文件", - "xpack.fileUpload.geoFilePicker.maxSize": "最大大小:{maxFileSize}", "xpack.fileUpload.geoFilePicker.noFeaturesDetected": "选定文件中未找到任何特征。", "xpack.fileUpload.geoFilePicker.previewSummary": "正在预览 {numFeatures} 个特征、{previewCoverage}% 的文件。", "xpack.fileUpload.geojsonImporter.noGeometry": "特征不包含必需的字段“geometry”", diff --git a/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts b/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts new file mode 100644 index 0000000000000..fa848d56e0113 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts @@ -0,0 +1,53 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type ExperimentalFeatures = typeof allowedExperimentalValues; + +/** + * A list of allowed values that can be used in `xpack.triggersActionsUi.enableExperimental`. + * This object is then used to validate and parse the value entered. + */ +export const allowedExperimentalValues = Object.freeze({ + rulesListDatagrid: true, + rulesDetailLogs: false, +}); + +type ExperimentalConfigKeys = Array; +type Mutable = { -readonly [P in keyof T]: T[P] }; + +const TriggersActionsUIInvalidExperimentalValue = class extends Error {}; +const allowedKeys = Object.keys(allowedExperimentalValues) as Readonly; + +/** + * Parses the string value used in `xpack.triggersActionsUi.enableExperimental` kibana configuration, + * which should be a string of values delimited by a comma (`,`) + * + * @param configValue + * @throws TriggersActionsUIInvalidExperimentalValue + */ +export const parseExperimentalConfigValue = (configValue: string[]): ExperimentalFeatures => { + const enabledFeatures: Mutable> = {}; + + for (const value of configValue) { + if (!isValidExperimentalValue(value)) { + throw new TriggersActionsUIInvalidExperimentalValue(`[${value}] is not valid.`); + } + + enabledFeatures[value as keyof ExperimentalFeatures] = true; + } + + return { + ...allowedExperimentalValues, + ...enabledFeatures, + }; +}; + +export const isValidExperimentalValue = (value: string): boolean => { + return allowedKeys.includes(value as keyof ExperimentalFeatures); +}; + +export const getExperimentalAllowedValues = (): string[] => [...allowedKeys]; diff --git a/x-pack/plugins/triggers_actions_ui/common/types.ts b/x-pack/plugins/triggers_actions_ui/common/types.ts new file mode 100644 index 0000000000000..c6c767c9bc40f --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/common/types.ts @@ -0,0 +1,10 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface TriggersActionsUiConfigType { + enableExperimental: string[]; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/common/experimental_features_service.ts b/x-pack/plugins/triggers_actions_ui/public/common/experimental_features_service.ts new file mode 100644 index 0000000000000..563c2e103e76e --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/experimental_features_service.ts @@ -0,0 +1,30 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExperimentalFeatures } from '../../common/experimental_features'; + +export class ExperimentalFeaturesService { + private static experimentalFeatures?: ExperimentalFeatures; + + public static init({ experimentalFeatures }: { experimentalFeatures: ExperimentalFeatures }) { + this.experimentalFeatures = experimentalFeatures; + } + + public static get(): ExperimentalFeatures { + if (!this.experimentalFeatures) { + this.throwUninitializedError(); + } + + return this.experimentalFeatures; + } + + private static throwUninitializedError(): never { + throw new Error( + 'Experimental features services not initialized - are you trying to import this module from outside of the triggers actions UI app?' + ); + } +} diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.test.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.test.tsx new file mode 100644 index 0000000000000..23d77b6e07a9b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.test.tsx @@ -0,0 +1,32 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExperimentalFeaturesService } from './experimental_features_service'; +import { getIsExperimentalFeatureEnabled } from './get_experimental_features'; + +describe('getIsExperimentalFeatureEnabled', () => { + it('getIsExperimentalFeatureEnabled returns the flag enablement', async () => { + ExperimentalFeaturesService.init({ + experimentalFeatures: { + rulesListDatagrid: true, + rulesDetailLogs: false, + }, + }); + + let result = getIsExperimentalFeatureEnabled('rulesListDatagrid'); + + expect(result).toEqual(true); + + result = getIsExperimentalFeatureEnabled('rulesDetailLogs'); + + expect(result).toEqual(false); + + expect(() => getIsExperimentalFeatureEnabled('doesNotExist' as any)).toThrowError( + 'Invalid enable value doesNotExist. Allowed values are: rulesListDatagrid, rulesDetailLogs' + ); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.ts b/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.ts new file mode 100644 index 0000000000000..706af60579711 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.ts @@ -0,0 +1,27 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ExperimentalFeatures, + allowedExperimentalValues, + isValidExperimentalValue, + getExperimentalAllowedValues, +} from '../../common/experimental_features'; + +const allowedExperimentalValueKeys = getExperimentalAllowedValues(); + +export const getIsExperimentalFeatureEnabled = (feature: keyof ExperimentalFeatures): boolean => { + if (!isValidExperimentalValue(feature)) { + throw new Error( + `Invalid enable value ${feature}. Allowed values are: ${allowedExperimentalValueKeys.join( + ', ' + )}` + ); + } + + return allowedExperimentalValues[feature]; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 473fc7e42497b..eb346e43cfbc9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -8,6 +8,7 @@ // TODO: https://github.com/elastic/kibana/issues/110895 /* eslint-disable @kbn/eslint/no_export_all */ +import { PluginInitializerContext } from 'kibana/server'; import { Plugin } from './plugin'; export type { @@ -42,8 +43,8 @@ export { AlertConditions, AlertConditionsGroup } from './application/sections'; export * from './common'; -export function plugin() { - return new Plugin(); +export function plugin(context: PluginInitializerContext) { + return new Plugin(context); } export { Plugin }; diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index e11a1d7e61a1d..afeff4ba8364e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -9,6 +9,7 @@ import { CoreSetup, CoreStart, Plugin as CorePlugin } from 'src/core/public'; import { i18n } from '@kbn/i18n'; import { ReactElement } from 'react'; +import { PluginInitializerContext } from 'kibana/public'; import { FeaturesPluginStart } from '../../features/public'; import { KibanaFeature } from '../../features/common'; import { registerBuiltInActionTypes } from './application/components/builtin_action_types'; @@ -31,6 +32,11 @@ import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout'; import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout'; import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout'; +import { ExperimentalFeaturesService } from './common/experimental_features_service'; +import { + ExperimentalFeatures, + parseExperimentalConfigValue, +} from '../common/experimental_features'; import type { ActionTypeModel, @@ -40,6 +46,7 @@ import type { ConnectorAddFlyoutProps, ConnectorEditFlyoutProps, } from './types'; +import { TriggersActionsUiConfigType } from '../common/types'; export interface TriggersAndActionsUIPublicPluginSetup { actionTypeRegistry: TypeRegistry; @@ -89,16 +96,22 @@ export class Plugin { private actionTypeRegistry: TypeRegistry; private ruleTypeRegistry: TypeRegistry; + private config: TriggersActionsUiConfigType; + readonly experimentalFeatures: ExperimentalFeatures; - constructor() { + constructor(ctx: PluginInitializerContext) { this.actionTypeRegistry = new TypeRegistry(); this.ruleTypeRegistry = new TypeRegistry(); + this.config = ctx.config.get(); + this.experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental || []); } public setup(core: CoreSetup, plugins: PluginsSetup): TriggersAndActionsUIPublicPluginSetup { const actionTypeRegistry = this.actionTypeRegistry; const ruleTypeRegistry = this.ruleTypeRegistry; + ExperimentalFeaturesService.init({ experimentalFeatures: this.experimentalFeatures }); + const featureTitle = i18n.translate('xpack.triggersActionsUI.managementSection.displayName', { defaultMessage: 'Rules and Connectors', }); diff --git a/x-pack/plugins/triggers_actions_ui/server/config.ts b/x-pack/plugins/triggers_actions_ui/server/config.ts new file mode 100644 index 0000000000000..46593fff21340 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/server/config.ts @@ -0,0 +1,50 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginInitializerContext } from '../../../../src/core/server'; + +import { + ExperimentalFeatures, + getExperimentalAllowedValues, + isValidExperimentalValue, + parseExperimentalConfigValue, +} from '../common/experimental_features'; + +const allowedExperimentalValues = getExperimentalAllowedValues(); + +export const configSchema = schema.object({ + enableGeoTrackingThresholdAlert: schema.maybe(schema.boolean({ defaultValue: false })), + enableExperimental: schema.arrayOf(schema.string(), { + defaultValue: () => [], + validate(list) { + for (const key of list) { + if (!isValidExperimentalValue(key)) { + return `[${key}] is not allowed. Allowed values are: ${allowedExperimentalValues.join( + ', ' + )}`; + } + } + }, + }), +}); + +export type ConfigSchema = TypeOf; + +export type ConfigType = ConfigSchema & { + experimentalFeatures: ExperimentalFeatures; +}; + +export const createConfig = (context: PluginInitializerContext): ConfigType => { + const pluginConfig = context.config.get>(); + const experimentalFeatures = parseExperimentalConfigValue(pluginConfig.enableExperimental); + + return { + ...pluginConfig, + experimentalFeatures, + }; +}; diff --git a/x-pack/plugins/triggers_actions_ui/server/index.ts b/x-pack/plugins/triggers_actions_ui/server/index.ts index 2f33f3bd77cc0..800eacedab8f3 100644 --- a/x-pack/plugins/triggers_actions_ui/server/index.ts +++ b/x-pack/plugins/triggers_actions_ui/server/index.ts @@ -5,7 +5,7 @@ * 2.0. */ import { PluginConfigDescriptor, PluginInitializerContext } from 'kibana/server'; -import { configSchema, ConfigSchema } from '../config'; +import { configSchema, ConfigSchema } from './config'; import { TriggersActionsPlugin } from './plugin'; export type { PluginStartContract } from './plugin'; @@ -22,6 +22,7 @@ export { export const config: PluginConfigDescriptor = { exposeToBrowser: { enableGeoTrackingThresholdAlert: true, + enableExperimental: true, }, schema: configSchema, }; diff --git a/x-pack/plugins/upgrade_assistant/README.md b/x-pack/plugins/upgrade_assistant/README.md index 6570e7f8d7617..31d7ba2823be5 100644 --- a/x-pack/plugins/upgrade_assistant/README.md +++ b/x-pack/plugins/upgrade_assistant/README.md @@ -35,8 +35,7 @@ Elasticsearch deprecations can be handled in a number of ways: * Create an index alias pointing from the original index name to the prefixed index name. * Reindex from the original index into the prefixed index. * Delete the old index and rename the prefixed index. -* **Updating index settings.** Some index settings will need to be updated, which doesn't require a -reindex. An example of this is the "Remove deprecated settings" button, which is shown when [deprecated translog settings](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html#index-modules-translog-retention) are detected ([#93293](https://github.com/elastic/kibana/pull/93293)). +* **Removing settings.** Some index and cluser settings are deprecated and need to be removed. The Upgrade Assistant provides a way to auto-resolve these settings via a "Remove deprecated settings" button. * **Upgrading or deleting snapshots**. This is specific to Machine Learning. If a user has old Machine Learning job model snapshots, they will need to be upgraded or deleted. The Upgrade Assistant provides a way to resolve this automatically for the user ([#100066](https://github.com/elastic/kibana/pull/100066)). * **Following the docs.** The Deprecation Info API provides links to the deprecation docs. Users will follow these docs to address the problem and make these warnings or errors disappear in the @@ -107,17 +106,66 @@ To test the Elasticsearch deprecations page ([#107053](https://github.com/elasti yarn es snapshot --license trial -E path.data=./path_to_6.x_ml_snapshots ``` -**3. Removing deprecated index settings** +**3. Removing deprecated settings** - The Upgrade Assistant currently only supports fixing deprecated translog index settings. However [the code](https://github.com/elastic/kibana/blob/master/x-pack/plugins/upgrade_assistant/common/constants.ts#L22) is written in a way to add support for more if necessary. Run the following Console command to trigger the deprecation warning: + The Upgrade Assistant supports removing deprecated index and cluster settings. This is determined based on the `actions` array returned from the deprecation info API. It currently does not support removing affix settings. See https://github.com/elastic/elasticsearch/pull/84246 for more details. + + Run the following Console commands to trigger deprecation issues for cluster and index settings: + +``` +// Can be set as persistent or transient +PUT /_cluster/settings +{ + "persistent" : { + "script.context.filter.cache_max_size": 10, + "script.context.update.cache_max_size": 10, + "script.context.update.max_compilations_rate": "10/1m", + "discovery.zen.minimum_master_nodes": 10, + "discovery.zen.commit_timeout": "10s", + "discovery.zen.no_master_block": "all", + "discovery.zen.publish_diff.enable": true, + "discovery.zen.publish_timeout": "10s", + "indices.lifecycle.step.master_timeout": "10s", + "script.context.field.max_compilations_rate": "10/1m", + "script.context.score.max_compilations_rate": "10/1m", + "script.context.interval.cache_expire": "10s", + "script.context.moving-function.cache_expire": "10s", + "xpack.watcher.history.cleaner_service.enabled": true, + "cluster.routing.allocation.exclude._tier": "data_warm", + "cluster.routing.allocation.include._tier": "data_cold", + "cluster.routing.allocation.require._tier": "data_hot", + "xpack.monitoring.elasticsearch.collection.enabled": true, + "xpack.monitoring.collection.enabled": true, + "xpack.monitoring.collection.interval": "20s", + "xpack.monitoring.collection.ccr.stats.timeout": "20s", + "xpack.monitoring.collection.cluster.stats.timeout": "20s", + "xpack.monitoring.collection.enrich.stats.timeout": "20s", + "xpack.monitoring.collection.index.recovery.timeout": "20s", + "xpack.monitoring.collection.index.stats.timeout": "20s", + "xpack.monitoring.collection.ml.job.stats.timeout": "20s", + "xpack.monitoring.collection.node.stats.timeout": "20s", + "xpack.monitoring.collection.index.recovery.active_only": true, + "xpack.monitoring.history.duration": "2d", + "xpack.monitoring.migration.decommission_alerts": true, + "cluster.routing.allocation.shard_state.reroute.priority": "HIGH", + "cluster.routing.allocation.disk.include_relocations": true + } +} +``` ``` PUT deprecated_settings { "settings": { - "translog.retention.size": "1b", - "translog.retention.age": "5m", + "index.indexing.slowlog.level": "warn", + "index.max_adjacency_matrix_filters": 10, + "index.routing.allocation.exclude._tier": "data_warm", + "index.routing.allocation.include._tier": "data_hot", + "index.routing.allocation.require._tier": "data_cold", + "index.search.slowlog.level": "warn", "index.soft_deletes.enabled": true, + "index.translog.retention.size": "1mb", + "index.translog.retention.age": "5s" } } ``` diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/cluster_settings_deprecation_flyout.test.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/cluster_settings_deprecation_flyout.test.ts new file mode 100644 index 0000000000000..20554c546af68 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/cluster_settings_deprecation_flyout.test.ts @@ -0,0 +1,128 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; + +import { setupEnvironment } from '../helpers'; +import { ElasticsearchTestBed, setupElasticsearchPage } from './es_deprecations.helpers'; +import { esDeprecationsMockResponse } from './mocked_responses'; +import { MOCK_REINDEX_DEPRECATION } from './mocked_responses'; + +describe('Cluster settings deprecation flyout', () => { + let testBed: ElasticsearchTestBed; + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + let httpSetup: ReturnType['httpSetup']; + const clusterSettingDeprecation = esDeprecationsMockResponse.deprecations[4]; + + beforeEach(async () => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + + httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); + httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { + reindexOp: null, + warnings: [], + hasRequiredPrivileges: true, + meta: { + indexName: 'foo', + reindexName: 'reindexed-foo', + aliases: [], + }, + }); + + await act(async () => { + testBed = await setupElasticsearchPage(httpSetup, { isReadOnlyMode: false }); + }); + + const { actions, component } = testBed; + component.update(); + await actions.table.clickDeprecationRowAt('clusterSetting', 0); + }); + + test('renders a flyout with deprecation details', async () => { + const { find, exists } = testBed; + + expect(exists('clusterSettingsDetails')).toBe(true); + expect(find('clusterSettingsDetails.flyoutTitle').text()).toContain( + clusterSettingDeprecation.message + ); + expect(find('clusterSettingsDetails.documentationLink').props().href).toBe( + clusterSettingDeprecation.url + ); + expect(exists('removeClusterSettingsPrompt')).toBe(true); + }); + + it('removes deprecated cluster settings', async () => { + const { find, actions, exists } = testBed; + + httpRequestsMockHelpers.setClusterSettingsResponse({ + acknowledged: true, + persistent: {}, + transietn: {}, + }); + + expect(exists('clusterSettingsDetails.warningDeprecationBadge')).toBe(true); + + await actions.clusterSettingsDeprecationFlyout.clickDeleteSettingsButton(); + + expect(httpSetup.post).toHaveBeenLastCalledWith( + `/api/upgrade_assistant/cluster_settings`, + expect.anything() + ); + + // Verify the "Resolution" column of the table is updated + expect(find('clusterSettingsResolutionStatusCell').at(0).text()).toEqual( + 'Deprecated settings removed' + ); + + // Reopen the flyout + await actions.table.clickDeprecationRowAt('clusterSetting', 0); + + // Verify prompt to remove setting no longer displays + expect(find('removeSettingsPrompt').length).toEqual(0); + // Verify the action button no longer displays + expect(find('clusterSettingsDetails.deleteSettingsButton').length).toEqual(0); + // Verify the badge got marked as resolved + expect(exists('clusterSettingsDetails.resolvedDeprecationBadge')).toBe(true); + }); + + it('handles failure', async () => { + const { find, actions } = testBed; + const error = { + statusCode: 500, + error: 'Remove cluster settings error', + message: 'Remove cluster settings error', + }; + + httpRequestsMockHelpers.setClusterSettingsResponse(undefined, error); + + await actions.clusterSettingsDeprecationFlyout.clickDeleteSettingsButton(); + + expect(httpSetup.post).toHaveBeenLastCalledWith( + `/api/upgrade_assistant/cluster_settings`, + expect.anything() + ); + + // Verify the "Resolution" column of the table is updated + expect(find('clusterSettingsResolutionStatusCell').at(0).text()).toEqual( + 'Settings removal failed' + ); + + // Reopen the flyout + await actions.table.clickDeprecationRowAt('clusterSetting', 0); + + // Verify the flyout shows an error message + expect(find('clusterSettingsDetails.deleteClusterSettingsError').text()).toContain( + 'Error deleting cluster settings' + ); + // Verify the remove settings button text changes + expect(find('clusterSettingsDetails.deleteClusterSettingsButton').text()).toEqual( + 'Retry removing deprecated settings' + ); + }); +}); diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.ts index 02fe72883c34f..033b42b1bf973 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.ts @@ -39,7 +39,7 @@ const createActions = (testBed: TestBed) => { component.update(); }, clickDeprecationRowAt: async ( - deprecationType: 'mlSnapshot' | 'indexSetting' | 'reindex' | 'default', + deprecationType: 'mlSnapshot' | 'indexSetting' | 'reindex' | 'default' | 'clusterSetting', index: number ) => { await act(async () => { @@ -126,6 +126,16 @@ const createActions = (testBed: TestBed) => { }, }; + const clusterSettingsDeprecationFlyout = { + clickDeleteSettingsButton: async () => { + await act(async () => { + find('deleteClusterSettingsButton').simulate('click'); + }); + + component.update(); + }, + }; + const reindexDeprecationFlyout = { clickReindexButton: async () => { await act(async () => { @@ -143,6 +153,7 @@ const createActions = (testBed: TestBed) => { mlDeprecationFlyout, reindexDeprecationFlyout, indexSettingsDeprecationFlyout, + clusterSettingsDeprecationFlyout, }; }; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts index 09f60a8dd3074..2f3bece2043a8 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts @@ -6,7 +6,6 @@ */ import type { ESUpgradeStatus, EnrichedDeprecationInfo } from '../../../common/types'; -import { indexSettingDeprecations } from '../../../common/constants'; export const MOCK_SNAPSHOT_ID = '1'; export const MOCK_JOB_ID = 'deprecation_check_job'; @@ -43,13 +42,26 @@ const MOCK_INDEX_SETTING_DEPRECATION: EnrichedDeprecationInfo = { isCritical: false, resolveDuringUpgrade: false, type: 'index_settings', - message: indexSettingDeprecations.translog.deprecationMessage, + message: 'Setting [index.routing.allocation.include._tier] is deprecated', details: 'deprecation details', url: 'doc_url', index: 'my_index', correctiveAction: { type: 'indexSetting', - deprecatedSettings: indexSettingDeprecations.translog.settings, + deprecatedSettings: ['index.routing.allocation.include._tier'], + }, +}; + +const MOCK_CLUSTER_SETTING_DEPRECATION: EnrichedDeprecationInfo = { + isCritical: false, + resolveDuringUpgrade: false, + type: 'cluster_settings', + message: 'Setting [cluster.routing.allocation.require._tier] is deprecated', + details: 'deprecation details', + url: 'doc_url', + correctiveAction: { + type: 'clusterSetting', + deprecatedSettings: ['cluster.routing.allocation.require._tier'], }, }; @@ -70,6 +82,7 @@ export const esDeprecationsMockResponse: ESUpgradeStatus = { MOCK_INDEX_SETTING_DEPRECATION, MOCK_DEFAULT_DEPRECATION, MOCK_REINDEX_DEPRECATION, + MOCK_CLUSTER_SETTING_DEPRECATION, ], }; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts index 70a7efecd7183..61f30e6cab73a 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/http_requests.ts @@ -142,6 +142,10 @@ const registerHttpRequestMockHelpers = ( const setLoadRemoteClustersResponse = (response?: object, error?: ResponseError) => mockResponse('GET', `${API_BASE_PATH}/remote_clusters`, response, error); + const setClusterSettingsResponse = (response?: object, error?: ResponseError) => { + mockResponse('POST', `${API_BASE_PATH}/cluster_settings`, response, error); + }; + return { setLoadCloudBackupStatusResponse, setLoadEsDeprecationsResponse, @@ -160,6 +164,7 @@ const registerHttpRequestMockHelpers = ( setLoadMlUpgradeModeResponse, setGetUpgradeStatusResponse, setLoadRemoteClustersResponse, + setClusterSettingsResponse, }; }; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/mock_es_issues.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/mock_es_issues.ts index a5106ccc314e9..99c0f4892a4ef 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/mock_es_issues.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/fix_issues_step/mock_es_issues.ts @@ -23,11 +23,15 @@ export const esCriticalAndWarningDeprecations: ESUpgradeStatus = { isCritical: false, type: 'index_settings', resolveDuringUpgrade: false, - message: 'Translog retention settings are deprecated', - url: 'https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html', + message: 'Setting [index.routing.allocation.include._tier] is deprecated', + url: 'https://www.elastic.co/guide/en/elasticsearch/reference/7.16/migrating-7.13.html#deprecate-tier-filter-setting', details: - 'translog retention settings [index.translog.retention.size] and [index.translog.retention.age] are ignored because translog is no longer used in peer recoveries with soft-deletes enabled (default in 7.0 or later)', + 'Remove the [index.routing.allocation.include._tier] setting. Use [index.routing.allocation.include._tier_preference] to control allocation to data tiers.', index: 'settings', + correctiveAction: { + type: 'indexSetting', + deprecatedSettings: ['index.routing.allocation.include._tier'], + }, }, ], }; diff --git a/x-pack/plugins/upgrade_assistant/common/constants.ts b/x-pack/plugins/upgrade_assistant/common/constants.ts index 06659ad073302..32ccb73d63400 100644 --- a/x-pack/plugins/upgrade_assistant/common/constants.ts +++ b/x-pack/plugins/upgrade_assistant/common/constants.ts @@ -10,18 +10,6 @@ */ export const MAJOR_VERSION = '8.0.0'; -/* - * Map of 7.0 --> 8.0 index setting deprecation log messages and associated settings - * We currently only support one setting deprecation (translog retention), but the code is written - * in a way to be able to support any number of deprecated index settings defined here - */ -export const indexSettingDeprecations = { - translog: { - deprecationMessage: 'Translog retention settings are deprecated', // expected message from ES deprecation info API - settings: ['translog.retention.size', 'translog.retention.age'], - }, -}; - export const API_BASE_PATH = '/api/upgrade_assistant'; // Telemetry constants diff --git a/x-pack/plugins/upgrade_assistant/common/types.ts b/x-pack/plugins/upgrade_assistant/common/types.ts index 02e8880d117f1..5993d351a8c75 100644 --- a/x-pack/plugins/upgrade_assistant/common/types.ts +++ b/x-pack/plugins/upgrade_assistant/common/types.ts @@ -199,12 +199,18 @@ export interface IndexSettingAction { type: 'indexSetting'; deprecatedSettings: string[]; } + +export interface ClusterSettingAction { + type: 'clusterSetting'; + deprecatedSettings: string[]; +} + export interface EnrichedDeprecationInfo extends Omit { type: keyof estypes.MigrationDeprecationsResponse; isCritical: boolean; index?: string; - correctiveAction?: ReindexAction | MlAction | IndexSettingAction; + correctiveAction?: ReindexAction | MlAction | IndexSettingAction | ClusterSettingAction; resolveDuringUpgrade: boolean; } diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.tsx new file mode 100644 index 0000000000000..c0bf30724a0cc --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.tsx @@ -0,0 +1,192 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { METRIC_TYPE } from '@kbn/analytics'; +import { + EuiButton, + EuiButtonEmpty, + EuiCode, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiText, + EuiSpacer, + EuiCallOut, +} from '@elastic/eui'; + +import { + EnrichedDeprecationInfo, + ClusterSettingAction, + ResponseError, +} from '../../../../../../common/types'; +import { uiMetricService, UIM_CLUSTER_SETTINGS_DELETE_CLICK } from '../../../../lib/ui_metric'; +import type { Status } from '../../../types'; +import { DeprecationFlyoutLearnMoreLink, DeprecationBadge } from '../../../shared'; + +export interface RemoveClusterSettingsFlyoutProps { + deprecation: EnrichedDeprecationInfo; + closeFlyout: () => void; + removeClusterSettings: (settings: string[]) => Promise; + status: { + statusType: Status; + details?: ResponseError; + }; +} + +const i18nTexts = { + removeButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeClusterSettingsFlyout.removeButtonLabel', + { + defaultMessage: 'Remove deprecated settings', + } + ), + retryRemoveButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeClusterSettingsFlyout.retryRemoveButtonLabel', + { + defaultMessage: 'Retry removing deprecated settings', + } + ), + resolvedButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeClusterSettingsFlyout.resolvedButtonLabel', + { + defaultMessage: 'Resolved', + } + ), + closeButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeClusterSettingsFlyout.closeButtonLabel', + { + defaultMessage: 'Close', + } + ), + getConfirmationText: (clusterSettingsCount: number) => + i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeClusterSettingsFlyout.description', + { + defaultMessage: + 'Remove the following deprecated cluster {clusterSettingsCount, plural, one {setting} other {settings}}?', + values: { + clusterSettingsCount, + }, + } + ), + errorTitle: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.removeClusterSettingsFlyout.deleteErrorTitle', + { + defaultMessage: 'Error deleting cluster settings', + } + ), +}; + +export const RemoveClusterSettingsFlyout = ({ + deprecation, + closeFlyout, + removeClusterSettings, + status, +}: RemoveClusterSettingsFlyoutProps) => { + const { message, details, url, correctiveAction } = deprecation; + const { statusType, details: statusDetails } = status; + + // Flag used to hide certain parts of the UI if the deprecation has been resolved or is in progress + const isResolvable = ['idle', 'error'].includes(statusType); + + const onRemoveSettings = useCallback(() => { + uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_CLUSTER_SETTINGS_DELETE_CLICK); + removeClusterSettings((correctiveAction as ClusterSettingAction).deprecatedSettings); + }, [correctiveAction, removeClusterSettings]); + + return ( + <> + + + + +

{message}

+
+
+ + {statusType === 'error' && ( + <> + + {statusDetails!.message} + + + + )} + + +

{details}

+

+ +

+
+ + {isResolvable && ( +
+ + + +

+ {i18nTexts.getConfirmationText( + (correctiveAction as ClusterSettingAction).deprecatedSettings.length + )} +

+
+ + + + +
    + {(correctiveAction as ClusterSettingAction).deprecatedSettings.map((setting) => ( +
  • + {setting} +
  • + ))} +
+
+
+ )} +
+ + + + + {i18nTexts.closeButtonLabel} + + + + {isResolvable && ( + + + {statusType === 'error' + ? i18nTexts.retryRemoveButtonLabel + : i18nTexts.removeButtonLabel} + + + )} + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/index.ts new file mode 100644 index 0000000000000..5cd1e2d31e9f0 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/index.ts @@ -0,0 +1,8 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { ClusterSettingsTableRow } from './table_row'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/resolution_table_cell.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/resolution_table_cell.tsx new file mode 100644 index 0000000000000..117cf693fe98f --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/resolution_table_cell.tsx @@ -0,0 +1,130 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { + EuiFlexItem, + EuiText, + EuiFlexGroup, + EuiIcon, + EuiLoadingSpinner, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Status } from '../../../types'; + +const i18nTexts = { + deleteInProgressText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.clusterSettings.deletingButtonLabel', + { + defaultMessage: 'Settings removal in progress…', + } + ), + deleteCompleteText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.clusterSettings.deleteCompleteText', + { + defaultMessage: 'Deprecated settings removed', + } + ), + deleteFailedText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.clusterSettings.deleteFailedText', + { + defaultMessage: 'Settings removal failed', + } + ), + resolutionText: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.clusterSettings.resolutionText', + { + defaultMessage: 'Remove settings', + } + ), + resolutionTooltipLabel: i18n.translate( + 'xpack.upgradeAssistant.esDeprecations.clusterSettings.resolutionTooltipLabel', + { + defaultMessage: + 'Resolve this issue by removing settings from this cluster. This issue can be resolved automatically.', + } + ), +}; + +interface Props { + status: { + statusType: Status; + }; +} + +export const ClusterSettingsResolutionCell: React.FunctionComponent = ({ status }) => { + const { statusType } = status; + if (statusType === 'in_progress') { + return ( + + + + + + {i18nTexts.deleteInProgressText} + + + ); + } + + if (statusType === 'complete') { + return ( + + + + + + {i18nTexts.deleteCompleteText} + + + ); + } + + if (statusType === 'error') { + return ( + + + + + + {i18nTexts.deleteFailedText} + + + ); + } + + return ( + + + + + + + {i18nTexts.resolutionText} + + + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.tsx new file mode 100644 index 0000000000000..f335b723c1ffc --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.tsx @@ -0,0 +1,110 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { EuiTableRowCell } from '@elastic/eui'; +import { EnrichedDeprecationInfo, ResponseError } from '../../../../../../common/types'; +import { GlobalFlyout } from '../../../../../shared_imports'; +import { useAppContext } from '../../../../app_context'; +import { EsDeprecationsTableCells } from '../../es_deprecations_table_cells'; +import { DeprecationTableColumns, Status } from '../../../types'; +import { ClusterSettingsResolutionCell } from './resolution_table_cell'; +import { RemoveClusterSettingsFlyout, RemoveClusterSettingsFlyoutProps } from './flyout'; + +const { useGlobalFlyout } = GlobalFlyout; + +interface Props { + deprecation: EnrichedDeprecationInfo; + rowFieldNames: DeprecationTableColumns[]; +} + +export const ClusterSettingsTableRow: React.FunctionComponent = ({ + rowFieldNames, + deprecation, +}) => { + const [showFlyout, setShowFlyout] = useState(false); + const [status, setStatus] = useState<{ + statusType: Status; + details?: ResponseError; + }>({ statusType: 'idle' }); + + const { + services: { api }, + } = useAppContext(); + + const { addContent: addContentToGlobalFlyout, removeContent: removeContentFromGlobalFlyout } = + useGlobalFlyout(); + + const closeFlyout = useCallback(() => { + setShowFlyout(false); + removeContentFromGlobalFlyout('clusterSettingsFlyout'); + }, [removeContentFromGlobalFlyout]); + + const removeClusterSettings = useCallback( + async (settings: string[]) => { + setStatus({ statusType: 'in_progress' }); + + const { error } = await api.updateClusterSettings(settings); + + setStatus({ + statusType: error ? 'error' : 'complete', + details: error ?? undefined, + }); + closeFlyout(); + }, + [api, closeFlyout] + ); + + useEffect(() => { + if (showFlyout) { + addContentToGlobalFlyout({ + id: 'clusterSettingsFlyout', + Component: RemoveClusterSettingsFlyout, + props: { + closeFlyout, + deprecation, + removeClusterSettings, + status, + }, + flyoutProps: { + onClose: closeFlyout, + className: 'eui-textBreakWord', + 'data-test-subj': 'clusterSettingsDetails', + 'aria-labelledby': 'removeClusterSettingsDetailsFlyoutTitle', + }, + }); + } + }, [ + addContentToGlobalFlyout, + deprecation, + removeClusterSettings, + showFlyout, + closeFlyout, + status, + ]); + + return ( + <> + {rowFieldNames.map((field: DeprecationTableColumns) => { + return ( + + setShowFlyout(true)} + deprecation={deprecation} + resolutionTableCell={} + /> + + ); + })} + + ); +}; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index.tsx index eb0221a722a30..074151731a5bc 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index.tsx @@ -9,3 +9,4 @@ export { MlSnapshotsTableRow } from './ml_snapshots'; export { IndexSettingsTableRow } from './index_settings'; export { DefaultTableRow } from './default'; export { ReindexTableRow } from './reindex'; +export { ClusterSettingsTableRow } from './cluster_settings'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.tsx index 58271642da020..8c03bda5b5489 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.tsx @@ -32,6 +32,7 @@ import { DefaultTableRow, IndexSettingsTableRow, ReindexTableRow, + ClusterSettingsTableRow, } from './deprecation_types'; import { DeprecationTableColumns } from '../types'; import { DEPRECATION_TYPE_MAP, PAGINATION_CONFIG } from '../constants'; @@ -119,6 +120,9 @@ const renderTableRowCells = ( case 'indexSetting': return ; + case 'clusterSetting': + return ; + case 'reindex': return ; diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts index 49be16b44236d..cdf5cb5fa57dc 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/api.ts @@ -240,6 +240,16 @@ export class ApiService { }); } + public async updateClusterSettings(settings: string[]) { + return await this.sendRequest({ + path: `${API_BASE_PATH}/cluster_settings`, + method: 'post', + body: { + settings: JSON.stringify(settings), + }, + }); + } + public useLoadRemoteClusters() { return this.useRequest({ path: `${API_BASE_PATH}/remote_clusters`, diff --git a/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts b/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts index 1ac34ae53194d..078e3e641569e 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/lib/ui_metric.ts @@ -26,6 +26,7 @@ export const UIM_ML_SNAPSHOT_UPGRADE_CLICK = 'ml_snapshot_upgrade_click'; export const UIM_ML_SNAPSHOT_DELETE_CLICK = 'ml_snapshot_delete_click'; export const UIM_INDEX_SETTINGS_DELETE_CLICK = 'index_settings_delete_click'; export const UIM_KIBANA_QUICK_RESOLVE_CLICK = 'kibana_quick_resolve_click'; +export const UIM_CLUSTER_SETTINGS_DELETE_CLICK = 'cluster_settings_delete_click'; export class UiMetricService { private usageCollection: UsageCollectionSetup | undefined; diff --git a/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json b/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json index 00ae96ba33f7a..a83d8d231d142 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json +++ b/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json @@ -85,12 +85,22 @@ "deprecated_settings": [ { "level": "warning", - "message": "Translog retention settings are deprecated", + "message": "Setting [index.routing.allocation.include._tier] is deprecated", "url": - "https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html", + "https://www.elastic.co/guide/en/elasticsearch/reference/7.16/migrating-7.13.html#deprecate-tier-filter-setting", "details": - "translog retention settings [index.translog.retention.size] and [index.translog.retention.age] are ignored because translog is no longer used in peer recoveries with soft-deletes enabled (default in 7.0 or later)", - "resolve_during_rolling_upgrade": false + "Remove the [index.routing.allocation.include._tier] setting. Use [index.routing.allocation.include._tier_preference] to control allocation to data tiers.", + "resolve_during_rolling_upgrade": false, + "_meta": { + "actions": [ + { + "action_type": "remove_settings", + "objects": [ + "index.routing.allocation.include._tier" + ] + } + ] + } } ], ".kibana": [ diff --git a/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_deprecations_status.test.ts.snap b/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_deprecations_status.test.ts.snap index 23cbf7aa265e7..de0154037f9ba 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_deprecations_status.test.ts.snap +++ b/x-pack/plugins/upgrade_assistant/server/lib/__snapshots__/es_deprecations_status.test.ts.snap @@ -101,18 +101,17 @@ Object { Object { "correctiveAction": Object { "deprecatedSettings": Array [ - "translog.retention.size", - "translog.retention.age", + "index.routing.allocation.include._tier", ], "type": "indexSetting", }, - "details": "translog retention settings [index.translog.retention.size] and [index.translog.retention.age] are ignored because translog is no longer used in peer recoveries with soft-deletes enabled (default in 7.0 or later)", + "details": "Remove the [index.routing.allocation.include._tier] setting. Use [index.routing.allocation.include._tier_preference] to control allocation to data tiers.", "index": "deprecated_settings", "isCritical": false, - "message": "Translog retention settings are deprecated", + "message": "Setting [index.routing.allocation.include._tier] is deprecated", "resolveDuringUpgrade": false, "type": "index_settings", - "url": "https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html", + "url": "https://www.elastic.co/guide/en/elasticsearch/reference/7.16/migrating-7.13.html#deprecate-tier-filter-setting", }, Object { "correctiveAction": undefined, diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.ts index 610b43d247da9..d4e9a164efa1c 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecations_status.ts @@ -7,7 +7,6 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IScopedClusterClient } from 'src/core/server'; -import { indexSettingDeprecations } from '../../common/constants'; import { EnrichedDeprecationInfo, ESUpgradeStatus } from '../../common/types'; import { esIndicesStateCheck } from './es_indices_state_check'; @@ -94,6 +93,8 @@ const getCombinedIndexInfos = async ( message, url, level, + // @ts-expect-error @elastic/elasticsearch _meta not available yet in MigrationDeprecationInfoResponse + _meta: metadata, // @ts-expect-error @elastic/elasticsearch resolve_during_rolling_upgrade not available yet in MigrationDeprecationInfoResponse resolve_during_rolling_upgrade: resolveDuringUpgrade, }) => @@ -104,7 +105,7 @@ const getCombinedIndexInfos = async ( index: indexName, type: 'index_settings', isCritical: level === 'critical', - correctiveAction: getCorrectiveAction(message), + correctiveAction: getCorrectiveAction(message, metadata, indexName), resolveDuringUpgrade, } as EnrichedDeprecationInfo) ) @@ -130,15 +131,33 @@ const getCombinedIndexInfos = async ( return indices as EnrichedDeprecationInfo[]; }; +interface Action { + action_type: 'remove_settings'; + objects: string[]; +} + +interface Actions { + actions: Action[]; +} + +type EsMetadata = Actions & { + [key: string]: string; +}; + const getCorrectiveAction = ( message: string, - metadata?: { [key: string]: string } + metadata: EsMetadata, + indexName?: string ): EnrichedDeprecationInfo['correctiveAction'] => { - const indexSettingDeprecation = Object.values(indexSettingDeprecations).find( - ({ deprecationMessage }) => deprecationMessage === message + const indexSettingDeprecation = metadata?.actions?.find( + (action) => action.action_type === 'remove_settings' && indexName + ); + const clusterSettingDeprecation = metadata?.actions?.find( + (action) => action.action_type === 'remove_settings' && typeof indexName === 'undefined' ); const requiresReindexAction = /Index created before/.test(message); const requiresIndexSettingsAction = Boolean(indexSettingDeprecation); + const requiresClusterSettingsAction = Boolean(clusterSettingDeprecation); const requiresMlAction = /[Mm]odel snapshot/.test(message); if (requiresReindexAction) { @@ -150,7 +169,14 @@ const getCorrectiveAction = ( if (requiresIndexSettingsAction) { return { type: 'indexSetting', - deprecatedSettings: indexSettingDeprecation!.settings, + deprecatedSettings: indexSettingDeprecation!.objects, + }; + } + + if (requiresClusterSettingsAction) { + return { + type: 'clusterSetting', + deprecatedSettings: clusterSettingDeprecation!.objects, }; } diff --git a/x-pack/plugins/upgrade_assistant/server/routes/cluster_settings.ts b/x-pack/plugins/upgrade_assistant/server/routes/cluster_settings.ts new file mode 100644 index 0000000000000..f64c3a383781f --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/routes/cluster_settings.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { API_BASE_PATH } from '../../common/constants'; +import { versionCheckHandlerWrapper } from '../lib/es_version_precheck'; +import { RouteDependencies } from '../types'; + +export function registerClusterSettingsRoute({ + router, + lib: { handleEsError }, +}: RouteDependencies) { + router.post( + { + path: `${API_BASE_PATH}/cluster_settings`, + validate: { + body: schema.object({ + settings: schema.arrayOf(schema.string()), + }), + }, + }, + versionCheckHandlerWrapper( + async ( + { + core: { + elasticsearch: { client }, + }, + }, + request, + response + ) => { + try { + const { settings } = request.body; + + // We need to fetch the current cluster settings in order to determine + // if the settings to delete were set as transient or persistent settings + const currentClusterSettings = await client.asCurrentUser.cluster.getSettings({ + flat_settings: true, + }); + + const settingsToDelete = settings.reduce( + (settingsBody, currentSetting) => { + if ( + Object.keys(currentClusterSettings.persistent).find((key) => key === currentSetting) + ) { + settingsBody.persistent[currentSetting] = null; + } + + if ( + Object.keys(currentClusterSettings.transient).find((key) => key === currentSetting) + ) { + settingsBody.transient[currentSetting] = null; + } + + return settingsBody; + }, + { persistent: {}, transient: {} } as { + persistent: { [key: string]: null }; + transient: { [key: string]: null }; + } + ); + + const settingsResponse = await client.asCurrentUser.cluster.putSettings({ + body: settingsToDelete, + flat_settings: true, + }); + + return response.ok({ + body: settingsResponse, + }); + } catch (error) { + return handleEsError({ error, response }); + } + } + ) + ); +} diff --git a/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts b/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts index c0f422711901b..2be0d1fafc6ae 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/register_routes.ts @@ -19,6 +19,7 @@ import { registerMlSnapshotRoutes } from './ml_snapshots'; import { ReindexWorker } from '../lib/reindexing'; import { registerUpgradeStatusRoute } from './status'; import { registerRemoteClustersRoute } from './remote_clusters'; +import { registerClusterSettingsRoute } from './cluster_settings'; export function registerRoutes(dependencies: RouteDependencies, getWorker: () => ReindexWorker) { registerAppRoutes(dependencies); @@ -34,4 +35,5 @@ export function registerRoutes(dependencies: RouteDependencies, getWorker: () => // Route for cloud to retrieve the upgrade status for ES and Kibana registerUpgradeStatusRoute(dependencies); registerRemoteClustersRoute(dependencies); + registerClusterSettingsRoute(dependencies); } diff --git a/x-pack/test/api_integration/apis/upgrade_assistant/cluster_settings.ts b/x-pack/test/api_integration/apis/upgrade_assistant/cluster_settings.ts new file mode 100644 index 0000000000000..b221e5e315952 --- /dev/null +++ b/x-pack/test/api_integration/apis/upgrade_assistant/cluster_settings.ts @@ -0,0 +1,60 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; +import { API_BASE_PATH } from '../../../../plugins/upgrade_assistant/common/constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const log = getService('log'); + + describe.skip('Cluster settings', () => { + describe('POST /api/upgrade_assistant/cluster_settings', () => { + before(async () => { + try { + // Configure some deprecated cluster settings + await es.cluster.putSettings({ + body: { + persistent: { + 'cluster.routing.allocation.exclude._tier': 'data_cold', + }, + transient: { + 'cluster.routing.allocation.include._tier': 'data_hot', + }, + }, + }); + } catch (e) { + log.debug('Error updating cluster settings'); + throw e; + } + }); + + it('removes cluster setting', async () => { + const { body: apiRequestResponse } = await supertest + .post(`${API_BASE_PATH}/cluster_settings`) + .set('kbn-xsrf', 'xxx') + .send({ + settings: [ + 'cluster.routing.allocation.exclude._tier', + 'cluster.routing.allocation.include._tier', + ], // cluster settings to remove + }) + .expect(200); + + expect(apiRequestResponse.persistent['cluster.routing.allocation.exclude._tier']).be( + undefined + ); + expect(apiRequestResponse.transient['cluster.routing.allocation.include._tier']).be( + undefined + ); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/upgrade_assistant/es_deprecations.ts b/x-pack/test/api_integration/apis/upgrade_assistant/es_deprecations.ts index b05cf8b901b5b..33706ae42e4c5 100644 --- a/x-pack/test/api_integration/apis/upgrade_assistant/es_deprecations.ts +++ b/x-pack/test/api_integration/apis/upgrade_assistant/es_deprecations.ts @@ -5,34 +5,12 @@ * 2.0. */ -import type { IndicesCreateRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import expect from '@kbn/expect'; - import { FtrProviderContext } from '../../ftr_provider_context'; -import { - API_BASE_PATH, - indexSettingDeprecations, -} from '../../../../plugins/upgrade_assistant/common/constants'; -import { EnrichedDeprecationInfo } from '../../../../plugins/upgrade_assistant/common/types'; - -const translogSettingsIndexDeprecation: IndicesCreateRequest = { - index: 'deprecated_settings', - body: { - settings: { - // @ts-expect-error setting is removed in 8.0 - 'translog.retention.size': '1b', - 'translog.retention.age': '5m', - 'index.soft_deletes.enabled': true, - }, - }, -}; +import { API_BASE_PATH } from '../../../../plugins/upgrade_assistant/common/constants'; export default function ({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); - const supertest = getService('supertest'); const security = getService('security'); - const es = getService('es'); - const log = getService('log'); describe('Elasticsearch deprecations', () => { describe('GET /api/upgrade_assistant/es_deprecations', () => { @@ -61,46 +39,6 @@ export default function ({ getService }: FtrProviderContext) { } }); }); - - // Only applicable on 7.x - describe.skip('index setting deprecation', () => { - before(async () => { - try { - // Create index that will trigger deprecation warning - await es.indices.create(translogSettingsIndexDeprecation); - } catch (e) { - log.debug('Error creating test index'); - throw e; - } - }); - - after(async () => { - try { - await es.indices.delete({ - index: [translogSettingsIndexDeprecation.index], - }); - } catch (e) { - log.debug('Error deleting text index'); - throw e; - } - }); - - it('returns the expected deprecation message for deprecated translog index settings', async () => { - const { body: apiRequestResponse } = await supertest - .get(`${API_BASE_PATH}/es_deprecations`) - .set('kbn-xsrf', 'xxx') - .expect(200); - - const indexSettingDeprecation = apiRequestResponse.deprecations.find( - (deprecation: EnrichedDeprecationInfo) => - deprecation.index === translogSettingsIndexDeprecation.index - ); - - expect(indexSettingDeprecation.message).to.equal( - indexSettingDeprecations.translog.deprecationMessage - ); - }); - }); }); }); } diff --git a/x-pack/test/api_integration/apis/upgrade_assistant/index.ts b/x-pack/test/api_integration/apis/upgrade_assistant/index.ts index 20d6e57a71c12..961f95714eaa0 100644 --- a/x-pack/test/api_integration/apis/upgrade_assistant/index.ts +++ b/x-pack/test/api_integration/apis/upgrade_assistant/index.ts @@ -15,5 +15,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./es_deprecations')); loadTestFile(require.resolve('./es_deprecation_logs')); loadTestFile(require.resolve('./remote_clusters')); + loadTestFile(require.resolve('./cluster_settings')); }); } diff --git a/yarn.lock b/yarn.lock index 774c17f195064..4059f0b228ccf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3117,6 +3117,22 @@ version "0.0.0" uid "" +"@kbn/shared-ux-components@link:bazel-bin/packages/kbn-shared-ux-components": + version "0.0.0" + uid "" + +"@kbn/shared-ux-services@link:bazel-bin/packages/kbn-shared-ux-services": + version "0.0.0" + uid "" + +"@kbn/shared-ux-storybook@link:bazel-bin/packages/kbn-shared-ux-storybook": + version "0.0.0" + uid "" + +"@kbn/shared-ux-utility@link:bazel-bin/packages/kbn-shared-ux-utility": + version "0.0.0" + uid "" + "@kbn/spec-to-console@link:bazel-bin/packages/kbn-spec-to-console": version "0.0.0" uid "" @@ -6086,6 +6102,22 @@ version "0.0.0" uid "" +"@types/kbn__shared-ux-components@link:bazel-bin/packages/kbn-shared-ux-components/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__shared-ux-services@link:bazel-bin/packages/kbn-shared-ux-services/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__shared-ux-storybook@link:bazel-bin/packages/kbn-shared-ux-storybook/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__shared-ux-utility@link:bazel-bin/packages/kbn-shared-ux-utility/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__std@link:bazel-bin/packages/kbn-std/npm_module_types": version "0.0.0" uid "" @@ -10409,6 +10441,11 @@ commander@2.17.x: resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== +commander@7, commander@^7.0.0, commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^4.0.1, commander@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -10429,11 +10466,6 @@ commander@^6.1.0, commander@^6.2.1: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== -commander@^7.0.0, commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - commander@^8.2.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -11454,15 +11486,17 @@ cytoscape@^3.10.0: heap "^0.2.6" lodash.debounce "^4.0.8" -d3-array@1, "d3-array@1 - 2", d3-array@1.2.4, d3-array@^1.1.1, d3-array@^1.2.0, d3-array@^1.2.4: +d3-array@1, d3-array@1.2.4, d3-array@^1.1.1, d3-array@^1.2.0, d3-array@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== -d3-array@>=2.5, d3-array@^2.3.0, d3-array@^2.7.1: - version "2.8.0" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.8.0.tgz#f76e10ad47f1f4f75f33db5fc322eb9ffde5ef23" - integrity sha512-6V272gsOeg7+9pTW1jSYOR1QE37g95I3my1hBmY+vOUNHRrk9yt4OTz/gK7PMkVAVDrYYq4mq3grTiZ8iJdNIw== +"d3-array@1 - 3", "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.1.1.tgz#7797eb53ead6b9083c75a45a681e93fc41bc468c" + integrity sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ== + dependencies: + internmap "1 - 2" d3-cloud@1.2.5, d3-cloud@^1.2.5: version "1.2.5" @@ -11476,21 +11510,16 @@ d3-collection@1, d3-collection@^1.0.3, d3-collection@^1.0.7: resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== -d3-color@1, "d3-color@1 - 2", d3-color@^1.0.3: +d3-color@1, d3-color@^1.0.3: version "1.4.1" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== -"d3-color@1 - 3": +"d3-color@1 - 3", d3-color@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.0.1.tgz#03316e595955d1fcd39d9f3610ad41bb90194d0a" integrity sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw== -d3-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" - integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== - d3-contour@^1.1.0: version "1.3.2" resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.3.2.tgz#652aacd500d2264cb3423cee10db69f6f59bead3" @@ -11498,14 +11527,14 @@ d3-contour@^1.1.0: dependencies: d3-array "^1.1.1" -d3-delaunay@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-5.3.0.tgz#b47f05c38f854a4e7b3cea80e0bb12e57398772d" - integrity sha512-amALSrOllWVLaHTnDLHwMIiz0d1bBu9gZXd1FiLfXf8sHcX9jrcj81TVZOqD4UX7MgBZZ07c8GxzEgBpJqc74w== +d3-delaunay@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.2.tgz#7fd3717ad0eade2fc9939f4260acfb503f984e92" + integrity sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ== dependencies: - delaunator "4" + delaunator "5" -d3-dispatch@1, "d3-dispatch@1 - 2", d3-dispatch@^1.0.3: +d3-dispatch@1, "d3-dispatch@1 - 3", d3-dispatch@^1.0.3: version "1.0.6" resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58" integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA== @@ -11519,13 +11548,13 @@ d3-dsv@^1.2.0: iconv-lite "0.4" rw "1" -d3-dsv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-2.0.0.tgz#b37b194b6df42da513a120d913ad1be22b5fe7c5" - integrity sha512-E+Pn8UJYx9mViuIUkoc93gJGGYut6mSDKy2+XaPwccwkRGlR+LO97L2VCCRjQivTwLHkSnAJG7yo00BWY6QM+w== +d3-dsv@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73" + integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== dependencies: - commander "2" - iconv-lite "0.4" + commander "7" + iconv-lite "0.6" rw "1" d3-ease@1: @@ -11533,41 +11562,45 @@ d3-ease@1: resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.6.tgz#ebdb6da22dfac0a22222f2d4da06f66c416a0ec0" integrity sha512-SZ/lVU7LRXafqp7XtIcBdxnWl8yyLpgOmzAk0mWBI9gXNzLDx5ybZgnRbH9dN/yY5tzVBqCQ9avltSnqVwessQ== -d3-force@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-2.1.1.tgz#f20ccbf1e6c9e80add1926f09b51f686a8bc0937" - integrity sha512-nAuHEzBqMvpFVMf9OX75d00OxvOXdxY+xECIXjW6Gv8BRrXu6gAWbv/9XKrvfJ5i5DCokDW7RYE50LRoK092ew== +d3-force@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4" + integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== dependencies: - d3-dispatch "1 - 2" - d3-quadtree "1 - 2" - d3-timer "1 - 2" + d3-dispatch "1 - 3" + d3-quadtree "1 - 3" + d3-timer "1 - 3" -d3-format@1, "d3-format@1 - 2", d3-format@^1.2.0: +d3-format@1, d3-format@^1.2.0: version "1.4.4" resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.4.tgz#356925f28d0fd7c7983bfad593726fce46844030" integrity sha512-TWks25e7t8/cqctxCmxpUuzZN11QxIA7YrMbram94zMQ0PXjE4LVIMe/f6a4+xxL8HQ3OsAFULOINQi1pE62Aw== -d3-format@^2.0.0: +"d3-format@1 - 3": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-2.0.0.tgz#a10bcc0f986c372b729ba447382413aabf5b0767" integrity sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA== -d3-geo-projection@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-geo-projection/-/d3-geo-projection-3.0.0.tgz#45ad8ce756cdbfa8340b11b2988644d8e1fa42e4" - integrity sha512-1JE+filVbkEX2bT25dJdQ05iA4QHvUwev6o0nIQHOSrNlHCAKfVss/U10vEM3pA4j5v7uQoFdQ4KLbx9BlEbWA== +d3-format@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +d3-geo-projection@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz#dc229e5ead78d31869a4e87cf1f45bd2716c48ca" + integrity sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg== dependencies: - commander "2" - d3-array "1 - 2" - d3-geo "1.12.0 - 2" - resolve "^1.1.10" + commander "7" + d3-array "1 - 3" + d3-geo "1.12.0 - 3" -"d3-geo@1.12.0 - 2", d3-geo@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-2.0.1.tgz#2437fdfed3fe3aba2812bd8f30609cac83a7ee39" - integrity sha512-M6yzGbFRfxzNrVhxDJXzJqSLQ90q1cCyb3EWFZ1LF4eWOBYxFypw7I/NFVBNXKNqxv1bqLathhYvdJ6DC+th3A== +"d3-geo@1.12.0 - 3", d3-geo@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.0.1.tgz#4f92362fd8685d93e3b1fae0fd97dc8980b1ed7e" + integrity sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA== dependencies: - d3-array ">=2.5" + d3-array "2.5.0 - 3" d3-geo@^1.6.4: version "1.12.1" @@ -11581,26 +11614,19 @@ d3-hierarchy@^1.1.4: resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83" integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ== -d3-hierarchy@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-2.0.0.tgz#dab88a58ca3e7a1bc6cab390e89667fcc6d20218" - integrity sha512-SwIdqM3HxQX2214EG9GTjgmCc/mbSx4mQBn+DuEETubhOw6/U3fmnji4uCVrmzOydMHSO1nZle5gh6HB/wdOzw== +d3-hierarchy@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz#9cbb0ffd2375137a351e6cfeed344a06d4ff4597" + integrity sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA== -d3-interpolate@1, "d3-interpolate@1.2.0 - 2", d3-interpolate@^1.1.4, d3-interpolate@^1.4.0: +d3-interpolate@1, d3-interpolate@^1.1.4, d3-interpolate@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== dependencies: d3-color "1" -d3-interpolate@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163" - integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ== - dependencies: - d3-color "1 - 2" - -d3-interpolate@^3.0.1: +"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== @@ -11612,12 +11638,17 @@ d3-path@1: resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== -"d3-path@1 - 2", d3-path@^2.0.0: +"d3-path@1 - 2", "d3-path@1 - 3": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-2.0.0.tgz#55d86ac131a0548adae241eebfb56b4582dd09d8" integrity sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA== -"d3-quadtree@1 - 2": +d3-path@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.0.1.tgz#f09dec0aaffd770b7995f1a399152bf93052321e" + integrity sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w== + +"d3-quadtree@1 - 3": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-2.0.0.tgz#edbad045cef88701f6fee3aee8e93fb332d30f9d" integrity sha512-b0Ed2t1UUalJpc3qXzKi+cPGxeXRr4KU9YSlocN74aTzp6R/Ud43t79yLLqxHRWZfsvWXmbDWPpoENK1K539xw== @@ -11644,16 +11675,16 @@ d3-scale@1.0.7, d3-scale@^1.0.5, d3-scale@^1.0.7: d3-time "1" d3-time-format "2" -d3-scale@^3.2.2: - version "3.2.3" - resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-3.2.3.tgz#be380f57f1f61d4ff2e6cbb65a40593a51649cfd" - integrity sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g== +d3-scale@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== dependencies: - d3-array "^2.3.0" - d3-format "1 - 2" - d3-interpolate "1.2.0 - 2" - d3-time "1 - 2" - d3-time-format "2 - 3" + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" d3-selection@^1.0.2, d3-selection@^1.1.0: version "1.4.1" @@ -11674,6 +11705,13 @@ d3-shape@^2.0.0: dependencies: d3-path "1 - 2" +d3-shape@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.1.0.tgz#c8a495652d83ea6f524e482fca57aa3f8bc32556" + integrity sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ== + dependencies: + d3-path "1 - 3" + d3-time-format@2: version "2.2.3" resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.2.3.tgz#0c9a12ee28342b2037e5ea1cf0b9eb4dd75f29cb" @@ -11681,33 +11719,52 @@ d3-time-format@2: dependencies: d3-time "1" -"d3-time-format@2 - 3", d3-time-format@^3.0.0: +"d3-time-format@2 - 4": version "3.0.0" resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6" integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag== dependencies: d3-time "1 - 2" +d3-time-format@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + d3-time@1, d3-time@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== -"d3-time@1 - 2", d3-time@^2.0.0: +"d3-time@1 - 2", "d3-time@1 - 3": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.0.0.tgz#ad7c127d17c67bd57a4c61f3eaecb81108b1e0ab" integrity sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q== +"d3-time@2.1.1 - 3", d3-time@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.0.0.tgz#65972cb98ae2d4954ef5c932e8704061335d4975" + integrity sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ== + dependencies: + d3-array "2 - 3" + d3-timer@1: version "1.0.10" resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5" integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw== -"d3-timer@1 - 2", d3-timer@^2.0.0: +"d3-timer@1 - 3": version "2.0.0" resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-2.0.0.tgz#055edb1d170cfe31ab2da8968deee940b56623e6" integrity sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA== +d3-timer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + d3-transition@^1.0.1: version "1.3.2" resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.3.2.tgz#a98ef2151be8d8600543434c1ca80140ae23b398" @@ -12139,10 +12196,12 @@ del@^6.0.0: rimraf "^3.0.2" slash "^3.0.0" -delaunator@4: - version "4.0.1" - resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-4.0.1.tgz#3d779687f57919a7a418f8ab947d3bddb6846957" - integrity sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag== +delaunator@5: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.0.tgz#60f052b28bd91c9b4566850ebf7756efe821d81b" + integrity sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw== + dependencies: + robust-predicates "^3.0.0" delayed-stream@~1.0.0: version "1.0.0" @@ -16321,7 +16380,7 @@ iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2, iconv-lite@^0.6.3: +iconv-lite@0.6, iconv-lite@^0.6.2, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -16641,6 +16700,11 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + interpret@^1.0.0, interpret@^1.1.0, interpret@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -25053,7 +25117,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.1.10, resolve@^1.1.4, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.3.3, resolve@^1.4.0, resolve@^1.7.1: +resolve@^1.1.4, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.3.3, resolve@^1.4.0, resolve@^1.7.1: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -25221,6 +25285,11 @@ robust-predicates@^2.0.4: resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-2.0.4.tgz#0a2367a93abd99676d075981707f29cfb402248b" integrity sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg== +robust-predicates@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a" + integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g== + rollup@^0.25.8: version "0.25.8" resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.25.8.tgz#bf6ce83b87510d163446eeaa577ed6a6fc5835e0" @@ -29036,17 +29105,17 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -vega-canvas@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.2.5.tgz#c332bad588893391b583fbeb0c95e1143127f797" - integrity sha512-6+jFEd6s7G088npH0+YuN1JTRxMYsjb1uXbwqcJ8zSP7gFR4f3tg/yA/BHU0+JbvLnxnScr61HYpDXAQ59YW3w== +vega-canvas@^1.2.5, vega-canvas@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.2.6.tgz#55e032ce9a62acd17229f6bac66d99db3d6879cd" + integrity sha512-rgeYUpslYn/amIfnuv3Sw6n4BGns94OjjZNtUc9IDji6b+K8LGS/kW+Lvay8JX/oFqtulBp8RLcHN6QjqPLA9Q== -vega-crossfilter@~4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/vega-crossfilter/-/vega-crossfilter-4.0.5.tgz#cf6a5fca60821928f976b32f22cf66cfd9cbeeae" - integrity sha512-yF+iyGP+ZxU7Tcj5yBsMfoUHTCebTALTXIkBNA99RKdaIHp1E690UaGVLZe6xde2n5WaYpho6I/I6wdAW3NXcg== +vega-crossfilter@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vega-crossfilter/-/vega-crossfilter-4.1.0.tgz#b6c5a728ce987f2514074adb22cf86b9bc63e0c8" + integrity sha512-aiOJcvVpiEDIu5uNc4Kf1hakkkPaVOO5fw5T4RSFAw6GEDbdqcB6eZ1xePcsLVic1hxYD5SGiUPdiiIs0SMh2g== dependencies: - d3-array "^2.7.1" + d3-array "^3.1.1" vega-dataflow "^5.7.3" vega-util "^1.15.2" @@ -29059,13 +29128,13 @@ vega-dataflow@^5.7.3, vega-dataflow@^5.7.4, vega-dataflow@~5.7.4: vega-loader "^4.3.2" vega-util "^1.16.1" -vega-encode@~4.8.3: - version "4.8.3" - resolved "https://registry.yarnpkg.com/vega-encode/-/vega-encode-4.8.3.tgz#b3048fb39845d72f18d8dc302ad697f826e0ff83" - integrity sha512-JoRYtaV2Hs8spWLzTu/IjR7J9jqRmuIOEicAaWj6T9NSZrNWQzu2zF3IVsX85WnrIDIRUDaehXaFZvy9uv9RQg== +vega-encode@~4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/vega-encode/-/vega-encode-4.9.0.tgz#3dd1031056bb8029f262afc4b4d58372c8f073a6" + integrity sha512-etv2BHuCn9bzEc0cxyA2TnbtcAFQGVFmsaqmB4sgBCaqTSEfXMoX68LK3yxBrsdm5LU+y3otJVoewi3qWYCx2g== dependencies: - d3-array "^2.7.1" - d3-interpolate "^2.0.1" + d3-array "^3.1.1" + d3-interpolate "^3.0.1" vega-dataflow "^5.7.3" vega-scale "^7.0.3" vega-util "^1.15.2" @@ -29083,63 +29152,63 @@ vega-expression@^5.0.0, vega-expression@~5.0.0: "@types/estree" "^0.0.50" vega-util "^1.16.0" -vega-force@~4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-4.0.7.tgz#6dc39ecb7889d9102661244d62fbc8d8714162ee" - integrity sha512-pyLKdwXSZ9C1dVIqdJOobvBY29rLvZjvRRTla9BU/nMwAiAGlGi6WKUFdRGdneyGe3zo2nSZDTZlZM/Z5VaQNA== +vega-force@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-4.1.0.tgz#cc8dea972baa52adc60840ff744ebb9e57d8f1f5" + integrity sha512-Sssf8iH48vYlz+E7/RpU+SUaJbuLoIL87U4tG2Av4gf/hRiImU49x2TI3EuhFWg1zpaCFxlz0CAaX++Oh/gjdw== dependencies: - d3-force "^2.1.1" + d3-force "^3.0.0" vega-dataflow "^5.7.3" vega-util "^1.15.2" -vega-format@^1.0.4, vega-format@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vega-format/-/vega-format-1.0.4.tgz#40c0c252d11128738b845ee73d8173f8064d6626" - integrity sha512-oTAeub3KWm6nKhXoYCx1q9G3K43R6/pDMXvqDlTSUtjoY7b/Gixm8iLcir5S9bPjvH40n4AcbZsPmNfL/Up77A== +vega-format@^1.0.4, vega-format@^1.1.0, vega-format@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vega-format/-/vega-format-1.1.0.tgz#b9d81cf1bcf222ae5cbd94357ae70245d2c7b18d" + integrity sha512-6mgpeWw8yGdG0Zdi8aVkx5oUrpJGOpNxqazC2858RSDPvChM/jDFlgRMTYw52qk7cxU0L08ARp4BwmXaI75j0w== dependencies: - d3-array "^2.7.1" - d3-format "^2.0.0" - d3-time-format "^3.0.0" + d3-array "^3.1.1" + d3-format "^3.1.0" + d3-time-format "^4.1.0" vega-time "^2.0.3" vega-util "^1.15.2" -vega-functions@^5.10.0, vega-functions@^5.12.1, vega-functions@~5.12.1: - version "5.12.1" - resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.12.1.tgz#b69f9ad4cd9f777dbc942587c02261b2f4cdba2c" - integrity sha512-7cHfcjXOj27qEbh2FTzWDl7FJK4xGcMFF7+oiyqa0fp7BU/wNT5YdNV0t5kCX9WjV7mfJWACKV74usLJbyM6GA== +vega-functions@^5.12.1, vega-functions@^5.13.0, vega-functions@~5.13.0: + version "5.13.0" + resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.13.0.tgz#c9ab8c6eedbf39f75b424cca6776b1d0b8c74b32" + integrity sha512-Mf53zNyx+c9fFqagEI0T8zc9nMlx0zozOngr8oOpG1tZDKOgwOnUgN99zQKbLHjyv+UzWrq3LYTnSLyVe0ZmhQ== dependencies: - d3-array "^2.7.1" - d3-color "^2.0.0" - d3-geo "^2.0.1" + d3-array "^3.1.1" + d3-color "^3.0.1" + d3-geo "^3.0.1" vega-dataflow "^5.7.3" vega-expression "^5.0.0" - vega-scale "^7.1.1" + vega-scale "^7.2.0" vega-scenegraph "^4.9.3" vega-selections "^5.3.1" vega-statistics "^1.7.9" - vega-time "^2.0.4" + vega-time "^2.1.0" vega-util "^1.16.0" -vega-geo@~4.3.8: - version "4.3.8" - resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-4.3.8.tgz#5629d18327bb4f3700cdf05db4aced0a43abbf4a" - integrity sha512-fsGxV96Q/QRgPqOPtMBZdI+DneIiROKTG3YDZvGn0EdV16OG5LzFhbNgLT5GPzI+kTwgLpAsucBHklexlB4kfg== +vega-geo@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-4.4.0.tgz#ce792df57f8ca4c54a7a1a29467cfa24bc53f573" + integrity sha512-3YX41y+J5pu0PMjvBCASg0/lgvu9+QXWJZ+vl6FFKa8AlsIopQ67ZL7ObwqjZcoZMolJ4q0rc+ZO8aj1pXCYcw== dependencies: - d3-array "^2.7.1" - d3-color "^2.0.0" - d3-geo "^2.0.1" + d3-array "^3.1.1" + d3-color "^3.0.1" + d3-geo "^3.0.1" vega-canvas "^1.2.5" vega-dataflow "^5.7.3" vega-projection "^1.4.5" vega-statistics "^1.7.9" vega-util "^1.15.2" -vega-hierarchy@~4.0.9: - version "4.0.9" - resolved "https://registry.yarnpkg.com/vega-hierarchy/-/vega-hierarchy-4.0.9.tgz#4b4bafbc181a14a280ecdbee8874c0db7e369f47" - integrity sha512-4XaWK6V38/QOZ+vllKKTafiwL25m8Kd+ebHmDV+Q236ONHmqc/gv82wwn9nBeXPEfPv4FyJw2SRoqa2Jol6fug== +vega-hierarchy@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vega-hierarchy/-/vega-hierarchy-4.1.0.tgz#605edbe3a6232853f9e8b57e3b905471d33b1a48" + integrity sha512-DWBK39IEt4FiQru12twzKSFUvFFZ7KtlH9+lAaqrJnKuIZFCyQ1XOUfKScfbKIlk4KS+DuCTNLI/pxC/f7Sk9Q== dependencies: - d3-hierarchy "^2.0.0" + d3-hierarchy "^3.1.0" vega-dataflow "^5.7.3" vega-util "^1.15.2" @@ -29148,12 +29217,12 @@ vega-interpreter@^1.0.4: resolved "https://registry.yarnpkg.com/vega-interpreter/-/vega-interpreter-1.0.4.tgz#291ebf85bc2d1c3550a3da22ff75b3ba0d326a39" integrity sha512-6tpYIa/pJz0cZo5fSxDSkZkAA51pID2LjOtQkOQvbzn+sJiCaWKPFhur8MBqbcmYZ9bnap1OYNwlrvpd2qBLvg== -vega-label@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vega-label/-/vega-label-1.1.0.tgz#0a11ae3ba18d7aed909c51ec67c2a9dde4426c6f" - integrity sha512-LAThIiDEsZxYvbSkvPLJ93eJF+Ts8RXv1IpBh8gmew8XGmaLJvVkzdsMe7WJJwuaVEsK7ZZFyB/Inkp842GW6w== +vega-label@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vega-label/-/vega-label-1.2.0.tgz#bcb2659aec54f890f9debab3e41ab87a58292dce" + integrity sha512-1prOqkCAfXaUvMqavbGI0nbYGqV8UQR9qvuVwrPJ6Yxm3GIUIOA/JRqNY8eZR8USwMP/kzsqlfVEixj9+Y75VQ== dependencies: - vega-canvas "^1.2.5" + vega-canvas "^1.2.6" vega-dataflow "^5.7.3" vega-scenegraph "^4.9.2" vega-util "^1.15.2" @@ -29175,15 +29244,15 @@ vega-lite@^5.2.0: vega-util "~1.17.0" yargs "~17.2.1" -vega-loader@^4.3.2, vega-loader@^4.3.3, vega-loader@~4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.4.1.tgz#8f9de46202f33659d1a2737f6e322a9fc3364275" - integrity sha512-dj65i4qlNhK0mOmjuchHgUrF5YUaWrYpx0A8kXA68lBk5Hkx8FNRztkcl07CZJ1+8V81ymEyJii9jzGbhEX0ag== +vega-loader@^4.3.2, vega-loader@^4.4.0, vega-loader@~4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.5.0.tgz#b15acc4c8f84191f500e94d35ddfb9721ac943ad" + integrity sha512-EkAyzbx0pCYxH3v3wghGVCaKINWxHfgbQ2pYDiYv0yo8e04S8Mv/IlRGTt6BAe7cLhrk1WZ4zh20QOppnGG05w== dependencies: - d3-dsv "^2.0.0" - node-fetch "^2.6.1" + d3-dsv "^3.0.1" + node-fetch "^2.6.7" topojson-client "^3.1.0" - vega-format "^1.0.4" + vega-format "^1.1.0" vega-util "^1.16.0" vega-parser@~6.1.4: @@ -29197,20 +29266,20 @@ vega-parser@~6.1.4: vega-scale "^7.1.1" vega-util "^1.16.0" -vega-projection@^1.4.5, vega-projection@~1.4.5: - version "1.4.5" - resolved "https://registry.yarnpkg.com/vega-projection/-/vega-projection-1.4.5.tgz#020cb646b4eaae535359da25f4f48eef8d324af2" - integrity sha512-85kWcPv0zrrNfxescqHtSYpRknilrS0K3CVRZc7IYQxnLtL1oma9WEbrSr1LCmDoCP5hl2Z1kKbomPXkrQX5Ag== +vega-projection@^1.4.5, vega-projection@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/vega-projection/-/vega-projection-1.5.0.tgz#51c5f0455170cd35b3c5f3e653e99c3616520640" + integrity sha512-aob7qojh555x3hQWZ/tr8cIJNSWQbm6EoWTJaheZgFOY2x3cDa4Qrg3RJbGw6KwVj/IQk2p40paRzixKZ2kr+A== dependencies: - d3-geo "^2.0.1" - d3-geo-projection "^3.0.0" + d3-geo "^3.0.1" + d3-geo-projection "^4.0.0" -vega-regression@~1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/vega-regression/-/vega-regression-1.0.9.tgz#f33da47fe457e03ad134782c11414bcef7b1da82" - integrity sha512-KSr3QbCF0vJEAWFVY2MA9X786oiJncTTr3gqRMPoaLr/Yo3f7OPKXRoUcw36RiWa0WCOEMgTYtM28iK6ZuSgaA== +vega-regression@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vega-regression/-/vega-regression-1.1.0.tgz#b4394db403ada93de52bb4536d04be336c983a8c" + integrity sha512-09K0RemY6cdaXBAyakDUNFfEkRcLkGjkDJyWQPAUqGK59hV2J+G3i4uxkZp18Vu0t8oqU7CgzwWim1s5uEpOcA== dependencies: - d3-array "^2.7.1" + d3-array "^3.1.1" vega-dataflow "^5.7.3" vega-statistics "^1.7.9" vega-util "^1.15.2" @@ -29223,27 +29292,27 @@ vega-runtime@^6.1.3, vega-runtime@~6.1.3: vega-dataflow "^5.7.3" vega-util "^1.15.2" -vega-scale@^7.0.3, vega-scale@^7.1.1, vega-scale@~7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/vega-scale/-/vega-scale-7.1.1.tgz#b69a38d1980f6fc1093390f796e556be63fdc808" - integrity sha512-yE0to0prA9E5PBJ/XP77TO0BMkzyUVyt7TH5PAwj+CZT7PMsMO6ozihelRhoIiVcP0Ae/ByCEQBUQkzN5zJ0ZA== +vega-scale@^7.0.3, vega-scale@^7.1.1, vega-scale@^7.2.0, vega-scale@~7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/vega-scale/-/vega-scale-7.2.0.tgz#9e298cc02ad340498cb56847436b19439911f0fc" + integrity sha512-QYltO/otrZHLrCGGf06Y99XtPtqWXITr6rw7rO9oL+l3d9o5RFl9sjHrVxiM7v+vGoZVWbBd5IPbFhPsXZ6+TA== dependencies: - d3-array "^2.7.1" - d3-interpolate "^2.0.1" - d3-scale "^3.2.2" - vega-time "^2.0.4" - vega-util "^1.15.2" + d3-array "^3.1.1" + d3-interpolate "^3.0.1" + d3-scale "^4.0.2" + vega-time "^2.1.0" + vega-util "^1.17.0" -vega-scenegraph@^4.9.2, vega-scenegraph@^4.9.3, vega-scenegraph@^4.9.4, vega-scenegraph@~4.9.4: - version "4.9.4" - resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.9.4.tgz#468408c1e89703fa9d3450445daabff623de2757" - integrity sha512-QaegQzbFE2yhYLNWAmHwAuguW3yTtQrmwvfxYT8tk0g+KKodrQ5WSmNrphWXhqwtsgVSvtdZkfp2IPeumcOQJg== +vega-scenegraph@^4.10.0, vega-scenegraph@^4.9.2, vega-scenegraph@^4.9.3, vega-scenegraph@~4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.10.0.tgz#232643372760ea081f2a899f640530777c2e2ba8" + integrity sha512-znUQAulNJnuXSza8+Qg1objNpXcHxP9KZwwp0XW4H/AHbzVhHEigZagb8xKDpQI1/8OSk2WZf9Bkr7CrsFC0hg== dependencies: - d3-path "^2.0.0" - d3-shape "^2.0.0" + d3-path "^3.0.1" + d3-shape "^3.1.0" vega-canvas "^1.2.5" - vega-loader "^4.3.3" - vega-scale "^7.1.1" + vega-loader "^4.4.0" + vega-scale "^7.2.0" vega-util "^1.15.2" vega-schema-url-parser@^2.2.0: @@ -29264,20 +29333,20 @@ vega-spec-injector@^0.0.2: resolved "https://registry.yarnpkg.com/vega-spec-injector/-/vega-spec-injector-0.0.2.tgz#f1d990109dd9d845c524738f818baa4b72a60ca6" integrity sha512-wOMMqmpssn0/ZFPW7wl1v26vbseRX7zHPWzEyS9TwNXTRCu1TcjIBIR+X23lCWocxhoBqFxmqyn8UowMhlGtAg== -vega-statistics@^1.7.9, vega-statistics@~1.7.10: - version "1.7.10" - resolved "https://registry.yarnpkg.com/vega-statistics/-/vega-statistics-1.7.10.tgz#4353637402e5e96bff2ebd16bd58e2c15cac3018" - integrity sha512-QLb12gcfpDZ9K5h3TLGrlz4UXDH9wSPyg9LLfOJZacxvvJEPohacUQNrGEAVtFO9ccUCerRfH9cs25ZtHsOZrw== +vega-statistics@^1.7.9, vega-statistics@^1.8.0, vega-statistics@~1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/vega-statistics/-/vega-statistics-1.8.0.tgz#ad66f7461473d58bc96671588981a059ffd60b59" + integrity sha512-dl+LCRS6qS4jWDme/NEdPVt5r649uB4IK6Kyr2/czmGA5JqjuFmtQ9lHQOnRu8945XLkqLf+JIQQo7vnw+nslA== dependencies: - d3-array "^2.7.1" + d3-array "^3.1.1" -vega-time@^2.0.3, vega-time@^2.0.4, vega-time@~2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/vega-time/-/vega-time-2.0.4.tgz#ff308358a831de927caa44e281cdc96f0863ba08" - integrity sha512-U314UDR9+ZlWrD3KBaeH+j/c2WSMdvcZq5yJfFT0yTg1jsBKAQBYFGvl+orackD8Zx3FveHOxx3XAObaQeDX+Q== +vega-time@^2.0.3, vega-time@^2.1.0, vega-time@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/vega-time/-/vega-time-2.1.0.tgz#acfbab88d7798b87ff63913b0dce2ca5eb0d46ca" + integrity sha512-Q9/l3S6Br1RPX5HZvyLD/cQ4K6K8DtpR09/1y7D66gxNorg2+HGzYZINH9nUvN3mxoXcBWg4cCUh3+JvmkDaEg== dependencies: - d3-array "^2.7.1" - d3-time "^2.0.0" + d3-array "^3.1.1" + d3-time "^3.0.0" vega-util "^1.15.2" vega-tooltip@^0.28.0: @@ -29287,15 +29356,15 @@ vega-tooltip@^0.28.0: dependencies: vega-util "^1.17.0" -vega-transforms@~4.9.4: - version "4.9.4" - resolved "https://registry.yarnpkg.com/vega-transforms/-/vega-transforms-4.9.4.tgz#5cf6b91bda9f184bbbaba63838be8e5e6a571235" - integrity sha512-JGBhm5Bf6fiGTUSB5Qr5ckw/KU9FJcSV5xIe/y4IobM/i/KNwI1i1fP45LzP4F4yZc0DMTwJod2UvFHGk9plKA== +vega-transforms@~4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/vega-transforms/-/vega-transforms-4.10.0.tgz#a1017ede13cf4e25499f588610a3be4da615d82d" + integrity sha512-Yk6ByzVq5F2niFfPlSsrU5wi+NZhsF7IBpJCcTfms4U7eoyNepUXagdFEJ3VWBD/Lit6GorLXFgO17NYcyS5gg== dependencies: - d3-array "^2.7.1" + d3-array "^3.1.1" vega-dataflow "^5.7.4" - vega-statistics "^1.7.9" - vega-time "^2.0.4" + vega-statistics "^1.8.0" + vega-time "^2.1.0" vega-util "^1.16.1" vega-typings@~0.22.0: @@ -29321,26 +29390,26 @@ vega-view-transforms@~4.5.8: vega-scenegraph "^4.9.2" vega-util "^1.15.2" -vega-view@~5.10.1: - version "5.10.1" - resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-5.10.1.tgz#b69348bb32a9845a1bd341fdd946df98684fadc3" - integrity sha512-4xvQ5KZcgKdZx1Z7jjenCUumvlyr/j4XcHLRf9gyeFrFvvS596dVpL92V8twhV6O++DmS2+fj+rHagO8Di4nMg== +vega-view@~5.11.0: + version "5.11.0" + resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-5.11.0.tgz#8a7b29a36776e43cc6599e087ed7f48a918b805d" + integrity sha512-MI9NTRFmtFX6ADk6KOHhi8bhHjC9pPm42Bj2+74c6l1d3NQZf9Jv7lkiGqKohdkQDNH9LPwz/6slhKwPU9JdkQ== dependencies: - d3-array "^2.7.1" - d3-timer "^2.0.0" + d3-array "^3.1.1" + d3-timer "^3.0.1" vega-dataflow "^5.7.3" - vega-format "^1.0.4" - vega-functions "^5.10.0" + vega-format "^1.1.0" + vega-functions "^5.13.0" vega-runtime "^6.1.3" - vega-scenegraph "^4.9.4" + vega-scenegraph "^4.10.0" vega-util "^1.16.1" -vega-voronoi@~4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/vega-voronoi/-/vega-voronoi-4.1.5.tgz#e7af574d4c27fd9cb12d70082f12c6f59b80b445" - integrity sha512-950IkgCFLj0zG33EWLAm1hZcp+FMqWcNQliMYt+MJzOD5S4MSpZpZ7K4wp2M1Jktjw/CLKFL9n38JCI0i3UonA== +vega-voronoi@~4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/vega-voronoi/-/vega-voronoi-4.2.0.tgz#14c74c84f52d9a16be2facd1bede879d32d988f2" + integrity sha512-1iuNAVZgUHRlBpdq4gSga3KlQmrgFfwy+KpyDgPLQ8HbLkhcVeT7RDh2L6naluqD7Op0xVLms3clR920WsYryQ== dependencies: - d3-delaunay "^5.3.0" + d3-delaunay "^6.0.2" vega-dataflow "^5.7.3" vega-util "^1.15.2" @@ -29355,37 +29424,37 @@ vega-wordcloud@~4.1.3: vega-statistics "^1.7.9" vega-util "^1.15.2" -vega@^5.21.0: - version "5.21.0" - resolved "https://registry.yarnpkg.com/vega/-/vega-5.21.0.tgz#f3d858d7544bfe4ffa3d8cd43d9ea978bf7391e8" - integrity sha512-yqqRa9nAqYoAxe7sVhRpsh0b001fly7Yx05klPkXmrvzjxXd07gClW1mOuGgSnVQqo7jTp/LYgbO1bD37FbEig== +vega@^5.22.0: + version "5.22.0" + resolved "https://registry.yarnpkg.com/vega/-/vega-5.22.0.tgz#9286832a0bc523ee39b1f3baff9a2ca1957a61f0" + integrity sha512-ZIehKTrMY93sWaWIn/2N2LwsCN8XymQthxQA5fQwTmefVl7OOvcYmsGFJ9nttXUF4n0z5WRXkSsPZcJHHBlOKw== dependencies: - vega-crossfilter "~4.0.5" + vega-crossfilter "~4.1.0" vega-dataflow "~5.7.4" - vega-encode "~4.8.3" + vega-encode "~4.9.0" vega-event-selector "~3.0.0" vega-expression "~5.0.0" - vega-force "~4.0.7" - vega-format "~1.0.4" - vega-functions "~5.12.1" - vega-geo "~4.3.8" - vega-hierarchy "~4.0.9" - vega-label "~1.1.0" - vega-loader "~4.4.1" + vega-force "~4.1.0" + vega-format "~1.1.0" + vega-functions "~5.13.0" + vega-geo "~4.4.0" + vega-hierarchy "~4.1.0" + vega-label "~1.2.0" + vega-loader "~4.5.0" vega-parser "~6.1.4" - vega-projection "~1.4.5" - vega-regression "~1.0.9" + vega-projection "~1.5.0" + vega-regression "~1.1.0" vega-runtime "~6.1.3" - vega-scale "~7.1.1" - vega-scenegraph "~4.9.4" - vega-statistics "~1.7.10" - vega-time "~2.0.4" - vega-transforms "~4.9.4" + vega-scale "~7.2.0" + vega-scenegraph "~4.10.0" + vega-statistics "~1.8.0" + vega-time "~2.1.0" + vega-transforms "~4.10.0" vega-typings "~0.22.0" vega-util "~1.17.0" - vega-view "~5.10.1" + vega-view "~5.11.0" vega-view-transforms "~4.5.8" - vega-voronoi "~4.1.5" + vega-voronoi "~4.2.0" vega-wordcloud "~4.1.3" vendors@^1.0.0: