diff --git a/.i18nrc.json b/.i18nrc.json index bdfe444bb99b5..36b3777fd1f5a 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -18,6 +18,7 @@ "devTools": "src/plugins/dev_tools", "expressions": "src/plugins/expressions", "expressionError": "src/plugins/expression_error", + "expressionImage": "src/plugins/expression_image", "expressionRepeatImage": "src/plugins/expression_repeat_image", "expressionRevealImage": "src/plugins/expression_reveal_image", "expressionShape": "src/plugins/expression_shape", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 77f16a9d69d46..d2b5f54a59383 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -76,6 +76,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a |Expression Error plugin adds an error renderer to the expression plugin. The renderer will display the error image. +|{kib-repo}blob/{branch}/src/plugins/expression_image/README.md[expressionImage] +|Expression Image plugin adds an image renderer to the expression plugin. The renderer will display the given image. + + |{kib-repo}blob/{branch}/src/plugins/expression_repeat_image/README.md[expressionRepeatImage] |Expression Repeat Image plugin adds a repeatImage function to the expression plugin and an associated renderer. The renderer will display the given image in mutliple instances. diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getallmigrations.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getallmigrations.md new file mode 100644 index 0000000000000..6613afb98efc2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getallmigrations.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [getAllMigrations](./kibana-plugin-plugins-expressions-public.executor.getallmigrations.md) + +## Executor.getAllMigrations() method + +Signature: + +```typescript +getAllMigrations(): MigrateFunctionsObject; +``` +Returns: + +`MigrateFunctionsObject` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md index 6835188c2fb04..61caebbd36368 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md @@ -34,12 +34,13 @@ export declare class Executor = Record -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [migrate](./kibana-plugin-plugins-expressions-public.executor.migrate.md) +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [migrateToLatest](./kibana-plugin-plugins-expressions-public.executor.migratetolatest.md) -## Executor.migrate() method +## Executor.migrateToLatest() method Signature: ```typescript -migrate(ast: SerializableState, version: string): ExpressionAstExpression; +migrateToLatest(state: VersionedState): ExpressionAstExpression; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| ast | SerializableState | | -| version | string | | +| state | VersionedState | | Returns: diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md new file mode 100644 index 0000000000000..b337d0dc21b4e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [getAllMigrations](./kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md) + +## ExpressionsService.getAllMigrations property + +gets an object with semver mapped to a migration function + +Signature: + +```typescript +getAllMigrations: () => import("../../../kibana_utils/common").MigrateFunctionsObject; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md index 9afd603bc4869..cde8c7c1a8f24 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md @@ -32,6 +32,7 @@ export declare class ExpressionsService implements PersistableStateServiceExecutor | | | [extract](./kibana-plugin-plugins-expressions-public.expressionsservice.extract.md) | | (state: ExpressionAstExpression) => {
state: ExpressionAstExpression;
references: SavedObjectReference[];
} | Extracts saved object references from expression AST | | [fork](./kibana-plugin-plugins-expressions-public.expressionsservice.fork.md) | | () => ExpressionsService | | +| [getAllMigrations](./kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md) | | () => import("../../../kibana_utils/common").MigrateFunctionsObject | gets an object with semver mapped to a migration function | | [getFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunction.md) | | ExpressionsServiceStart['getFunction'] | | | [getFunctions](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunctions.md) | | () => ReturnType<Executor['getFunctions']> | Returns POJO map of all registered expression functions, where keys are names of the functions and values are ExpressionFunction instances. | | [getRenderer](./kibana-plugin-plugins-expressions-public.expressionsservice.getrenderer.md) | | ExpressionsServiceStart['getRenderer'] | | @@ -39,7 +40,7 @@ export declare class ExpressionsService implements PersistableStateServiceExpressionsServiceStart['getType'] | | | [getTypes](./kibana-plugin-plugins-expressions-public.expressionsservice.gettypes.md) | | () => ReturnType<Executor['getTypes']> | Returns POJO map of all registered expression types, where keys are names of the types and values are ExpressionType instances. | | [inject](./kibana-plugin-plugins-expressions-public.expressionsservice.inject.md) | | (state: ExpressionAstExpression, references: SavedObjectReference[]) => ExpressionAstExpression | Injects saved object references into expression AST | -| [migrate](./kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md) | | (state: SerializableState, version: string) => ExpressionAstExpression | Runs the migration (if it exists) for specified version. This will run a single migration step (ie from 7.10.0 to 7.10.1) | +| [migrateToLatest](./kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md) | | (state: VersionedState) => ExpressionAstExpression | migrates an old expression to latest version | | [registerFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.registerfunction.md) | | (functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)) => void | Register an expression function, which will be possible to execute as part of the expression pipeline.Below we register a function which simply sleeps for given number of milliseconds to delay the execution and outputs its input as-is. ```ts expressions.registerFunction({ diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md deleted file mode 100644 index d1f24bfcfc0bb..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [migrate](./kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md) - -## ExpressionsService.migrate property - -Runs the migration (if it exists) for specified version. This will run a single migration step (ie from 7.10.0 to 7.10.1) - -Signature: - -```typescript -readonly migrate: (state: SerializableState, version: string) => ExpressionAstExpression; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md new file mode 100644 index 0000000000000..55efb8d5a8af3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [migrateToLatest](./kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md) + +## ExpressionsService.migrateToLatest property + +migrates an old expression to latest version + +Signature: + +```typescript +migrateToLatest: (state: VersionedState) => ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md index dcdbd663f84e4..a228628fece0f 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md @@ -28,7 +28,7 @@ export interface IExpressionLoaderParams | [searchContext](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md) | SerializableState | | | [searchSessionId](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchsessionid.md) | string | | | [syncColors](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md) | boolean | | -| [throttle](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md) | number | Throttling of partial results in milliseconds. By default, throttling is disabled. | +| [throttle](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md) | number | Throttling of partial results in milliseconds. 0 is disabling the throttling. By default, it equals 1000. | | [uiState](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.uistate.md) | unknown | | | [variables](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.variables.md) | Record<string, any> | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md index 3383bce879776..54949bbbd5dd6 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md @@ -4,7 +4,7 @@ ## IExpressionLoaderParams.throttle property -Throttling of partial results in milliseconds. By default, throttling is disabled. +Throttling of partial results in milliseconds. 0 is disabling the throttling. By default, it equals 1000. Signature: diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getallmigrations.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getallmigrations.md new file mode 100644 index 0000000000000..f397f3a9869b8 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getallmigrations.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [getAllMigrations](./kibana-plugin-plugins-expressions-server.executor.getallmigrations.md) + +## Executor.getAllMigrations() method + +Signature: + +```typescript +getAllMigrations(): MigrateFunctionsObject; +``` +Returns: + +`MigrateFunctionsObject` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md index 48002a9f986df..00b4300990ca8 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md @@ -34,12 +34,13 @@ export declare class Executor = Record -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [migrate](./kibana-plugin-plugins-expressions-server.executor.migrate.md) +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [migrateToLatest](./kibana-plugin-plugins-expressions-server.executor.migratetolatest.md) -## Executor.migrate() method +## Executor.migrateToLatest() method Signature: ```typescript -migrate(ast: SerializableState, version: string): ExpressionAstExpression; +migrateToLatest(state: VersionedState): ExpressionAstExpression; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| ast | SerializableState | | -| version | string | | +| state | VersionedState | | Returns: diff --git a/package.json b/package.json index f7856b9f92e74..3a16330bdf970 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,6 @@ "@elastic/request-crypto": "1.1.4", "@elastic/safer-lodash-set": "link:bazel-bin/packages/elastic-safer-lodash-set", "@elastic/search-ui-app-search-connector": "^1.6.0", - "@elastic/ui-ace": "0.2.3", "@emotion/react": "^11.4.0", "@hapi/accept": "^5.0.2", "@hapi/boom": "^9.1.1", @@ -194,7 +193,6 @@ "angular-route": "^1.8.0", "angular-sanitize": "^1.8.0", "angular-sortable-view": "^0.0.17", - "angular-ui-ace": "0.2.3", "antlr4ts": "^0.5.0-alpha.3", "archiver": "^5.2.0", "axios": "^0.21.1", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 0bf15d236bc9c..ef6ff52a6c8b8 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -114,5 +114,6 @@ pageLoadAssetSize: cases: 144442 expressionError: 22127 expressionRepeatImage: 22341 + expressionImage: 19288 expressionShape: 30033 userSetup: 18532 diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 7aca25d2013d2..b27ff86631725 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -18,6 +18,7 @@ export const storybookAliases = { data_enhanced: 'x-pack/plugins/data_enhanced/.storybook', embeddable: 'src/plugins/embeddable/.storybook', expression_error: 'src/plugins/expression_error/.storybook', + expression_image: 'src/plugins/expression_image/.storybook', expression_repeat_image: 'src/plugins/expression_repeat_image/.storybook', expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook', expression_shape: 'src/plugins/expression_shape/.storybook', diff --git a/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts b/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts index 11f6954af97b0..8dad40d373f31 100644 --- a/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts +++ b/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts @@ -202,5 +202,11 @@ describe('createSearchSessionRestorationDataProvider', () => { expect(initialState.timeRange).toBe(relativeTime); expect(restoreState.timeRange).toBe(absoluteTime); }); + + test('restoreState has paused autoRefresh', async () => { + const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData(); + expect(initialState.refreshInterval).toBe(undefined); + expect(restoreState.refreshInterval?.pause).toBe(true); + }); }); }); diff --git a/src/plugins/discover/public/application/apps/main/services/discover_state.ts b/src/plugins/discover/public/application/apps/main/services/discover_state.ts index 03705bac8f16c..e57e3bb029f31 100644 --- a/src/plugins/discover/public/application/apps/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/apps/main/services/discover_state.ts @@ -392,6 +392,12 @@ function createUrlGeneratorState({ sort: appState.sort, savedQuery: appState.savedQuery, interval: appState.interval, + refreshInterval: shouldRestoreSearchSession + ? { + pause: true, // force pause refresh interval when restoring a session + value: 0, + } + : undefined, useHash: false, }; } diff --git a/src/plugins/embeddable/public/__snapshots__/plugin.test.ts.snap b/src/plugins/embeddable/public/__snapshots__/plugin.test.ts.snap new file mode 100644 index 0000000000000..6ef25188283e5 --- /dev/null +++ b/src/plugins/embeddable/public/__snapshots__/plugin.test.ts.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`embeddable factory migrateToLatest returns list of all migrations 1`] = ` +Object { + "7.11.0": [Function], + "7.12.0": [Function], +} +`; diff --git a/src/plugins/embeddable/public/plugin.test.ts b/src/plugins/embeddable/public/plugin.test.ts index b93dc02ebb5a8..a616b559d3236 100644 --- a/src/plugins/embeddable/public/plugin.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -165,6 +165,19 @@ describe('embeddable factory', () => { start.getAllMigrations!()['7.11.0']!(containerState); expect(embeddableFactory.migrations['7.11.0']).toBeCalledWith(embeddableState); }); + + test('migrateToLatest returns list of all migrations', () => { + const migrations = start.getAllMigrations(); + expect(migrations).toMatchSnapshot(); + }); + + test('migrateToLatest calls correct migrate functions', () => { + start.migrateToLatest!({ + state: embeddableState, + version: '7.11.0', + }); + expect(embeddableFactory.migrations['7.11.0']).toBeCalledWith(embeddableState); + }); }); describe('embeddable enhancements', () => { diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index 62ec9e15f564c..cfb16da7b46b8 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -39,7 +39,11 @@ import { import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition'; import { EmbeddableStateTransfer } from './lib/state_transfer'; import { Storage } from '../../kibana_utils/public'; -import { PersistableStateService, SerializableState } from '../../kibana_utils/common'; +import { + migrateToLatest, + PersistableStateService, + SerializableState, +} from '../../kibana_utils/common'; import { ATTRIBUTE_SERVICE_KEY, AttributeService } from './lib/attribute_service'; import { AttributeServiceOptions } from './lib/attribute_service/attribute_service'; import { EmbeddableStateWithType } from '../common/types'; @@ -181,6 +185,13 @@ export class EmbeddablePublicPlugin implements Plugin + getAllMigrations( + Array.from(this.embeddableFactories.values()), + Array.from(this.enhancements.values()), + getMigrateFunction(commonContract) + ); + return { getEmbeddableFactory: this.getEmbeddableFactory, getEmbeddableFactories: this.getEmbeddableFactories, @@ -206,12 +217,10 @@ export class EmbeddablePublicPlugin implements Plugin - getAllMigrations( - Array.from(this.embeddableFactories.values()), - Array.from(this.enhancements.values()), - getMigrateFunction(commonContract) - ), + getAllMigrations: getAllMigrationsFn, + migrateToLatest: (state) => { + return migrateToLatest(getAllMigrationsFn(), state) as EmbeddableStateWithType; + }, }; } diff --git a/src/plugins/expression_image/.storybook/main.js b/src/plugins/expression_image/.storybook/main.js new file mode 100644 index 0000000000000..742239e638b8a --- /dev/null +++ b/src/plugins/expression_image/.storybook/main.js @@ -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 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. + */ + +// eslint-disable-next-line import/no-commonjs +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/src/plugins/expression_image/README.md b/src/plugins/expression_image/README.md new file mode 100755 index 0000000000000..b02c9fd39b3d2 --- /dev/null +++ b/src/plugins/expression_image/README.md @@ -0,0 +1,9 @@ +# expressionRevealImage + +Expression Image plugin adds an `image` renderer to the expression plugin. The renderer will display the given image. + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/expression_image/__fixtures__/index.ts b/src/plugins/expression_image/__fixtures__/index.ts new file mode 100644 index 0000000000000..279e8d87446bc --- /dev/null +++ b/src/plugins/expression_image/__fixtures__/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { imageFunction } from '../common/expression_functions'; diff --git a/src/plugins/expression_image/common/constants.ts b/src/plugins/expression_image/common/constants.ts new file mode 100644 index 0000000000000..0ac8bec2e1f7d --- /dev/null +++ b/src/plugins/expression_image/common/constants.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. + */ + +export const PLUGIN_ID = 'expressionImage'; +export const PLUGIN_NAME = 'expressionImage'; + +export const CONTEXT = '_context_'; +export const BASE64 = '`base64`'; +export const URL = 'URL'; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.test.js b/src/plugins/expression_image/common/expression_functions/image_function.test.ts similarity index 59% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.test.js rename to src/plugins/expression_image/common/expression_functions/image_function.test.ts index 862560e5643d7..7deaeb90b411b 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.test.js +++ b/src/plugins/expression_image/common/expression_functions/image_function.test.ts @@ -1,72 +1,78 @@ /* * 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. + * 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 expect from '@kbn/expect'; +import { ExecutionContext } from 'src/plugins/expressions'; import { + functionWrapper, getElasticLogo, getElasticOutline, - functionWrapper, -} from '../../../../../../src/plugins/presentation_util/common/lib'; -import { image } from './image'; +} from '../../../presentation_util/common/lib'; +import { imageFunction as image } from './image_function'; -// TODO: the test was not running and is not up to date describe('image', () => { const fn = functionWrapper(image); - let elasticLogo; - let elasticOutline; + let elasticLogo: string; + let elasticOutline: string; + beforeEach(async () => { - elasticLogo = (await getElasticLogo()).elasticLogo; - elasticOutline = (await getElasticOutline()).elasticOutline; + elasticLogo = (await getElasticLogo())?.elasticLogo; + elasticOutline = (await getElasticOutline())?.elasticOutline; }); it('returns an image object using a dataUrl', async () => { - const result = await fn(null, { dataurl: elasticOutline, mode: 'cover' }); + const result = await fn( + null, + { dataurl: elasticOutline, mode: 'cover' }, + {} as ExecutionContext + ); expect(result).to.have.property('type', 'image'); }); describe('args', () => { describe('dataurl', () => { it('sets the source of the image using dataurl', async () => { - const result = await fn(null, { dataurl: elasticOutline }); + const result = await fn(null, { dataurl: elasticOutline }, {} as ExecutionContext); expect(result).to.have.property('dataurl', elasticOutline); }); it.skip('sets the source of the image using url', async () => { // This is skipped because functionWrapper doesn't use the actual // interpreter and doesn't resolve aliases - const result = await fn(null, { url: elasticOutline }); + const result = await fn(null, { url: elasticOutline }, {} as ExecutionContext); expect(result).to.have.property('dataurl', elasticOutline); }); it('defaults to the elasticLogo if not provided', async () => { - const result = await fn(null); + const result = await fn(null, {}, {} as ExecutionContext); expect(result).to.have.property('dataurl', elasticLogo); }); }); describe('sets the mode', () => { it('to contain', async () => { - const result = await fn(null, { mode: 'contain' }); + const result = await fn(null, { mode: 'contain' }, {} as ExecutionContext); expect(result).to.have.property('mode', 'contain'); }); it('to cover', async () => { - const result = await fn(null, { mode: 'cover' }); + const result = await fn(null, { mode: 'cover' }, {} as ExecutionContext); expect(result).to.have.property('mode', 'cover'); }); it('to stretch', async () => { - const result = await fn(null, { mode: 'stretch' }); + const result = await fn(null, { mode: 'stretch' }, {} as ExecutionContext); expect(result).to.have.property('mode', '100% 100%'); }); it("defaults to 'contain' if not provided", async () => { - const result = await fn(null); + const result = await fn(null, {}, {} as ExecutionContext); expect(result).to.have.property('mode', 'contain'); }); }); diff --git a/src/plugins/expression_image/common/expression_functions/image_function.ts b/src/plugins/expression_image/common/expression_functions/image_function.ts new file mode 100644 index 0000000000000..8394681e1b10b --- /dev/null +++ b/src/plugins/expression_image/common/expression_functions/image_function.ts @@ -0,0 +1,96 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { getElasticLogo, resolveWithMissingImage } from '../../../presentation_util/common/lib'; +import { BASE64, URL } from '../constants'; +import { ExpressionImageFunction, ImageMode } from '../types'; + +export const strings = { + help: i18n.translate('expressionImage.functions.imageHelpText', { + defaultMessage: + 'Displays an image. Provide an image asset as a {BASE64} data {URL}, or pass in a sub-expression.', + values: { + BASE64, + URL, + }, + }), + args: { + dataurl: i18n.translate('expressionImage.functions.image.args.dataurlHelpText', { + defaultMessage: 'The {https} {URL} or {BASE64} data {URL} of an image.', + values: { + BASE64, + https: 'HTTP(S)', + URL, + }, + }), + mode: i18n.translate('expressionImage.functions.image.args.modeHelpText', { + defaultMessage: + '{contain} shows the entire image, scaled to fit. ' + + '{cover} fills the container with the image, cropping from the sides or bottom as needed. ' + + '{stretch} resizes the height and width of the image to 100% of the container.', + values: { + contain: `\`"${ImageMode.CONTAIN}"\``, + cover: `\`"${ImageMode.COVER}"\``, + stretch: `\`"${ImageMode.STRETCH}"\``, + }, + }), + }, +}; + +const errors = { + invalidImageMode: () => + i18n.translate('expressionImage.functions.image.invalidImageModeErrorMessage', { + defaultMessage: '"mode" must be "{contain}", "{cover}", or "{stretch}"', + values: { + contain: ImageMode.CONTAIN, + cover: ImageMode.COVER, + stretch: ImageMode.STRETCH, + }, + }), +}; + +export const imageFunction: ExpressionImageFunction = () => { + const { help, args: argHelp } = strings; + + return { + name: 'image', + aliases: [], + type: 'image', + inputTypes: ['null'], + help, + args: { + dataurl: { + // This was accepting dataurl, but there was no facility in fn for checking type and handling a dataurl type. + types: ['string', 'null'], + help: argHelp.dataurl, + aliases: ['_', 'url'], + default: null, + }, + mode: { + types: ['string'], + help: argHelp.mode, + default: 'contain', + options: Object.values(ImageMode), + }, + }, + fn: async (input, { dataurl, mode }) => { + if (!mode || !Object.values(ImageMode).includes(mode)) { + throw new Error(errors.invalidImageMode()); + } + + const modeStyle = mode === 'stretch' ? '100% 100%' : mode; + const { elasticLogo } = await getElasticLogo(); + return { + type: 'image', + mode: modeStyle, + dataurl: resolveWithMissingImage(dataurl, elasticLogo) as string, + }; + }, + }; +}; diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/default_index_context.ts b/src/plugins/expression_image/common/expression_functions/index.ts similarity index 67% rename from src/plugins/vis_type_timeseries/public/application/contexts/default_index_context.ts rename to src/plugins/expression_image/common/expression_functions/index.ts index a8770d86fba9b..5274069d3d17e 100644 --- a/src/plugins/vis_type_timeseries/public/application/contexts/default_index_context.ts +++ b/src/plugins/expression_image/common/expression_functions/index.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import React from 'react'; -import { IIndexPattern } from '../../../../data/public'; +import { imageFunction } from './image_function'; -export const DefaultIndexPatternContext = React.createContext(null); +export const functions = [imageFunction]; + +export { imageFunction }; diff --git a/src/plugins/expression_image/common/index.ts b/src/plugins/expression_image/common/index.ts new file mode 100755 index 0000000000000..f251b9cf01cb3 --- /dev/null +++ b/src/plugins/expression_image/common/index.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 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 * from './constants'; +export * from './types'; diff --git a/src/plugins/expression_image/common/types/expression_functions.ts b/src/plugins/expression_image/common/types/expression_functions.ts new file mode 100644 index 0000000000000..5ee9ed93cc87b --- /dev/null +++ b/src/plugins/expression_image/common/types/expression_functions.ts @@ -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 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 { ExpressionFunctionDefinition } from '../../../expressions'; + +export enum ImageMode { + CONTAIN = 'contain', + COVER = 'cover', + STRETCH = 'stretch', +} + +interface Arguments { + dataurl: string | null; + mode: ImageMode | null; +} + +export interface Return { + type: 'image'; + mode: string; + dataurl: string; +} + +export type ExpressionImageFunction = () => ExpressionFunctionDefinition< + 'image', + null, + Arguments, + Promise +>; diff --git a/src/plugins/expression_image/common/types/expression_renderers.ts b/src/plugins/expression_image/common/types/expression_renderers.ts new file mode 100644 index 0000000000000..c4ff7a7f18ae9 --- /dev/null +++ b/src/plugins/expression_image/common/types/expression_renderers.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 { ImageMode } from './expression_functions'; + +export type OriginString = 'bottom' | 'left' | 'top' | 'right'; + +export interface ImageRendererConfig { + dataurl: string | null; + mode: ImageMode | null; +} + +export interface NodeDimensions { + width: number; + height: number; +} diff --git a/src/plugins/expression_image/common/types/index.ts b/src/plugins/expression_image/common/types/index.ts new file mode 100644 index 0000000000000..ec934e7affe88 --- /dev/null +++ b/src/plugins/expression_image/common/types/index.ts @@ -0,0 +1,9 @@ +/* + * 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 * from './expression_functions'; +export * from './expression_renderers'; diff --git a/src/plugins/expression_image/jest.config.js b/src/plugins/expression_image/jest.config.js new file mode 100644 index 0000000000000..3d5bc9f184c6a --- /dev/null +++ b/src/plugins/expression_image/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: ['/src/plugins/expression_image'], +}; diff --git a/src/plugins/expression_image/kibana.json b/src/plugins/expression_image/kibana.json new file mode 100755 index 0000000000000..13b4e989b8f70 --- /dev/null +++ b/src/plugins/expression_image/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "expressionImage", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": true, + "requiredPlugins": ["expressions", "presentationUtil"], + "optionalPlugins": [] +} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/__snapshots__/image.stories.storyshot b/src/plugins/expression_image/public/expression_renderers/__stories__/__snapshots__/image.stories.storyshot similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/__snapshots__/image.stories.storyshot rename to src/plugins/expression_image/public/expression_renderers/__stories__/__snapshots__/image.stories.storyshot diff --git a/src/plugins/expression_image/public/expression_renderers/__stories__/image_renderer.stories.tsx b/src/plugins/expression_image/public/expression_renderers/__stories__/image_renderer.stories.tsx new file mode 100644 index 0000000000000..d75aa1a4263eb --- /dev/null +++ b/src/plugins/expression_image/public/expression_renderers/__stories__/image_renderer.stories.tsx @@ -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. + */ + +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { Render, waitFor } from '../../../../presentation_util/public/__stories__'; +import { imageRenderer } from '../image_renderer'; +import { getElasticLogo } from '../../../../../../src/plugins/presentation_util/common/lib'; +import { ImageMode } from '../../../common'; + +const Renderer = ({ elasticLogo }: { elasticLogo: string }) => { + const config = { + dataurl: elasticLogo, + mode: ImageMode.COVER, + }; + + return ; +}; + +storiesOf('renderers/image', module).add( + 'default', + (_, props) => { + return ; + }, + { decorators: [waitFor(getElasticLogo())] } +); diff --git a/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx b/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx new file mode 100644 index 0000000000000..3d542a9978a83 --- /dev/null +++ b/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx @@ -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 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 from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions'; +import { i18n } from '@kbn/i18n'; +import { getElasticLogo, isValidUrl } from '../../../presentation_util/public'; +import { ImageRendererConfig } from '../../common/types'; + +const strings = { + getDisplayName: () => + i18n.translate('expressionImage.renderer.image.displayName', { + defaultMessage: 'Image', + }), + getHelpDescription: () => + i18n.translate('expressionImage.renderer.image.helpDescription', { + defaultMessage: 'Render an image', + }), +}; + +export const imageRenderer = (): ExpressionRenderDefinition => ({ + name: 'image', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render: async ( + domNode: HTMLElement, + config: ImageRendererConfig, + handlers: IInterpreterRenderHandlers + ) => { + const { elasticLogo } = await getElasticLogo(); + const dataurl = isValidUrl(config.dataurl ?? '') ? config.dataurl : elasticLogo; + + const style = { + height: '100%', + backgroundImage: `url(${dataurl})`, + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center center', + backgroundSize: config.mode as string, + }; + + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + render(
, domNode, () => handlers.done()); + }, +}); diff --git a/src/plugins/expression_image/public/expression_renderers/index.ts b/src/plugins/expression_image/public/expression_renderers/index.ts new file mode 100644 index 0000000000000..96c274f05a7a9 --- /dev/null +++ b/src/plugins/expression_image/public/expression_renderers/index.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. + */ + +import { imageRenderer } from './image_renderer'; + +export const renderers = [imageRenderer]; + +export { imageRenderer }; diff --git a/src/plugins/expression_image/public/index.ts b/src/plugins/expression_image/public/index.ts new file mode 100755 index 0000000000000..522418640bd1f --- /dev/null +++ b/src/plugins/expression_image/public/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. + */ + +import { ExpressionImagePlugin } from './plugin'; + +export type { ExpressionImagePluginSetup, ExpressionImagePluginStart } from './plugin'; + +export function plugin() { + return new ExpressionImagePlugin(); +} + +export * from './expression_renderers'; diff --git a/src/plugins/expression_image/public/plugin.ts b/src/plugins/expression_image/public/plugin.ts new file mode 100755 index 0000000000000..44feea4121637 --- /dev/null +++ b/src/plugins/expression_image/public/plugin.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. + */ + +import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; +import { imageRenderer } from './expression_renderers'; +import { imageFunction } from '../common/expression_functions'; + +interface SetupDeps { + expressions: ExpressionsSetup; +} + +interface StartDeps { + expression: ExpressionsStart; +} + +export type ExpressionImagePluginSetup = void; +export type ExpressionImagePluginStart = void; + +export class ExpressionImagePlugin + implements Plugin { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionImagePluginSetup { + expressions.registerFunction(imageFunction); + expressions.registerRenderer(imageRenderer); + } + + public start(core: CoreStart): ExpressionImagePluginStart {} + + public stop() {} +} diff --git a/src/plugins/expression_image/server/index.ts b/src/plugins/expression_image/server/index.ts new file mode 100755 index 0000000000000..a4c6ee888d086 --- /dev/null +++ b/src/plugins/expression_image/server/index.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. + */ + +import { ExpressionImagePlugin } from './plugin'; + +export type { ExpressionImagePluginSetup, ExpressionImagePluginStart } from './plugin'; + +export function plugin() { + return new ExpressionImagePlugin(); +} diff --git a/src/plugins/expression_image/server/plugin.ts b/src/plugins/expression_image/server/plugin.ts new file mode 100755 index 0000000000000..d3259d45107e5 --- /dev/null +++ b/src/plugins/expression_image/server/plugin.ts @@ -0,0 +1,33 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { ExpressionsServerStart, ExpressionsServerSetup } from '../../expressions/server'; +import { imageFunction } from '../common/expression_functions'; + +interface SetupDeps { + expressions: ExpressionsServerSetup; +} + +interface StartDeps { + expression: ExpressionsServerStart; +} + +export type ExpressionImagePluginSetup = void; +export type ExpressionImagePluginStart = void; + +export class ExpressionImagePlugin + implements Plugin { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionImagePluginSetup { + expressions.registerFunction(imageFunction); + } + + public start(core: CoreStart): ExpressionImagePluginStart {} + + public stop() {} +} diff --git a/src/plugins/expression_image/tsconfig.json b/src/plugins/expression_image/tsconfig.json new file mode 100644 index 0000000000000..5fab51496c97e --- /dev/null +++ b/src/plugins/expression_image/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + "__fixtures__/**/*", + ], + "references": [ + { "path": "../../core/tsconfig.json" }, + { "path": "../presentation_util/tsconfig.json" }, + { "path": "../expressions/tsconfig.json" }, + ] +} diff --git a/src/plugins/expression_reveal_image/public/plugin.ts b/src/plugins/expression_reveal_image/public/plugin.ts index 5f6496a25f820..c3522b43ca0ca 100755 --- a/src/plugins/expression_reveal_image/public/plugin.ts +++ b/src/plugins/expression_reveal_image/public/plugin.ts @@ -9,6 +9,7 @@ import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; import { revealImageRenderer } from './expression_renderers'; +import { revealImageFunction } from '../common/expression_functions'; interface SetupDeps { expressions: ExpressionsSetup; @@ -30,6 +31,7 @@ export class ExpressionRevealImagePlugin StartDeps > { public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionRevealImagePluginSetup { + expressions.registerFunction(revealImageFunction); expressions.registerRenderer(revealImageRenderer); } diff --git a/src/plugins/expression_shape/public/plugin.ts b/src/plugins/expression_shape/public/plugin.ts index cb28f97acd697..b20f357d52a9b 100755 --- a/src/plugins/expression_shape/public/plugin.ts +++ b/src/plugins/expression_shape/public/plugin.ts @@ -9,6 +9,7 @@ import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; import { shapeRenderer } from './expression_renderers'; +import { shapeFunction } from '../common/expression_functions'; interface SetupDeps { expressions: ExpressionsSetup; @@ -24,6 +25,7 @@ export type ExpressionShapePluginStart = void; export class ExpressionShapePlugin implements Plugin { public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionShapePluginSetup { + expressions.registerFunction(shapeFunction); expressions.registerRenderer(shapeRenderer); } diff --git a/src/plugins/expressions/common/executor/__snapshots__/executor.test.ts.snap b/src/plugins/expressions/common/executor/__snapshots__/executor.test.ts.snap new file mode 100644 index 0000000000000..1d4a8614b2921 --- /dev/null +++ b/src/plugins/expressions/common/executor/__snapshots__/executor.test.ts.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Executor .inject .getAllMigrations returns list of all registered migrations 1`] = ` +Object { + "7.10.0": [Function], + "7.10.1": [Function], +} +`; diff --git a/src/plugins/expressions/common/executor/executor.test.ts b/src/plugins/expressions/common/executor/executor.test.ts index 3c24a3c24e01b..d3837272fb50f 100644 --- a/src/plugins/expressions/common/executor/executor.test.ts +++ b/src/plugins/expressions/common/executor/executor.test.ts @@ -200,12 +200,21 @@ describe('Executor', () => { }); }); - describe('.migrate', () => { + describe('.getAllMigrations', () => { + test('returns list of all registered migrations', () => { + const migrations = executor.getAllMigrations(); + expect(migrations).toMatchSnapshot(); + }); + }); + + describe('.migrateToLatest', () => { test('calls migrate function for every expression function in expression', () => { - executor.migrate( - parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}'), - '7.10.0' - ); + executor.migrateToLatest({ + state: parseExpression( + 'foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}' + ), + version: '7.10.0', + }); expect(migrateFn).toBeCalledTimes(5); }); }); diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts index 7ca5a005991bd..ec6ca0323ea5c 100644 --- a/src/plugins/expressions/common/executor/executor.ts +++ b/src/plugins/expressions/common/executor/executor.ts @@ -21,7 +21,13 @@ import { ExpressionAstExpression, ExpressionAstFunction } from '../ast'; import { ExpressionValueError, typeSpecs } from '../expression_types/specs'; import { getByAlias } from '../util'; import { SavedObjectReference } from '../../../../core/types'; -import { PersistableStateService, SerializableState } from '../../../kibana_utils/common'; +import { + MigrateFunctionsObject, + migrateToLatest, + PersistableStateService, + SerializableState, + VersionedState, +} from '../../../kibana_utils/common'; import { ExpressionExecutionParams } from '../service'; export interface ExpressionExecOptions { @@ -244,7 +250,28 @@ export class Executor = Record Object.keys(fn.migrations)) + .flat(1) + ); + + const migrations: MigrateFunctionsObject = {}; + uniqueVersions.forEach((version) => { + migrations[version] = (state) => ({ + ...this.migrate(state, version), + }); + }); + + return migrations; + } + + public migrateToLatest(state: VersionedState) { + return migrateToLatest(this.getAllMigrations(), state) as ExpressionAstExpression; + } + + private migrate(ast: SerializableState, version: string) { return this.walkAst(cloneDeep(ast) as ExpressionAstExpression, (fn, link) => { if (!fn.migrations[version]) return link; const updatedAst = fn.migrations[version](link) as ExpressionAstFunction; diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index 7802617f3a251..cd52c8c3239de 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -18,7 +18,11 @@ import { ExecutionContract, ExecutionResult } from '../execution'; import { AnyExpressionTypeDefinition, ExpressionValueError } from '../expression_types'; import { AnyExpressionFunctionDefinition } from '../expression_functions'; import { SavedObjectReference } from '../../../../core/types'; -import { PersistableStateService, SerializableState } from '../../../kibana_utils/common'; +import { + PersistableStateService, + SerializableState, + VersionedState, +} from '../../../kibana_utils/common'; import { Adapters } from '../../../inspector/common/adapters'; import { clog, @@ -323,13 +327,18 @@ export class ExpressionsService implements PersistableStateService { + return this.executor.getAllMigrations(); + }; + + /** + * migrates an old expression to latest version + * @param state */ - public readonly migrate = (state: SerializableState, version: string) => { - return this.executor.migrate(state, version); + public migrateToLatest = (state: VersionedState) => { + return this.executor.migrateToLatest(state); }; /** diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 91906b67615e3..67c69f76be914 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -213,6 +213,10 @@ export class Executor = Record; // @deprecated (undocumented) readonly functions: FunctionsRegistry; + // Warning: (ae-forgotten-export) The symbol "MigrateFunctionsObject" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getAllMigrations(): MigrateFunctionsObject; // (undocumented) getFunction(name: string): ExpressionFunction | undefined; // (undocumented) @@ -225,10 +229,10 @@ export class Executor = Record AnyExpressionFunctionDefinition)): void; // (undocumented) @@ -599,6 +603,7 @@ export class ExpressionsService implements PersistableStateService ExpressionsService; + getAllMigrations: () => import("../../../kibana_utils/common").MigrateFunctionsObject; // (undocumented) readonly getFunction: ExpressionsServiceStart['getFunction']; readonly getFunctions: () => ReturnType; @@ -609,7 +614,7 @@ export class ExpressionsService implements PersistableStateService ReturnType; readonly inject: (state: ExpressionAstExpression, references: SavedObjectReference[]) => ExpressionAstExpression; - readonly migrate: (state: SerializableState, version: string) => ExpressionAstExpression; + migrateToLatest: (state: VersionedState) => ExpressionAstExpression; readonly registerFunction: (functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)) => void; // (undocumented) readonly registerRenderer: (definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)) => void; @@ -1188,6 +1193,7 @@ export type UnmappedTypeStrings = 'date' | 'filter'; // Warnings were encountered during analysis: // // src/plugins/expressions/common/ast/types.ts:29:3 - (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts +// src/plugins/expressions/common/expression_functions/expression_function.ts:68:5 - (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts // src/plugins/expressions/common/expression_types/specs/error.ts:20:5 - (ae-forgotten-export) The symbol "ErrorLike" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index cbe134548f8e6..1783c6c72c686 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -195,6 +195,10 @@ export class Executor = Record; // @deprecated (undocumented) readonly functions: FunctionsRegistry; + // Warning: (ae-forgotten-export) The symbol "MigrateFunctionsObject" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getAllMigrations(): MigrateFunctionsObject; // (undocumented) getFunction(name: string): ExpressionFunction | undefined; // (undocumented) @@ -207,10 +211,10 @@ export class Executor = Record AnyExpressionFunctionDefinition)): void; // (undocumented) @@ -943,6 +947,7 @@ export type UnmappedTypeStrings = 'date' | 'filter'; // Warnings were encountered during analysis: // // src/plugins/expressions/common/ast/types.ts:29:3 - (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts +// src/plugins/expressions/common/expression_functions/expression_function.ts:68:5 - (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts // src/plugins/expressions/common/expression_types/specs/error.ts:20:5 - (ae-forgotten-export) The symbol "ErrorLike" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.test.ts b/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.test.ts index 2ae376e787d2f..32fb652d41632 100644 --- a/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.test.ts +++ b/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.test.ts @@ -50,14 +50,11 @@ test('returns the same object if there are no migrations to be applied', () => { } ); - expect(migrated).toEqual({ - state: { name: 'Foo' }, - version: '0.0.1', - }); + expect(migrated).toEqual({ name: 'Foo' }); }); test('applies a single migration', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '0.0.2': (migrationV2 as unknown) as MigrateFunction, }, @@ -71,11 +68,10 @@ test('applies a single migration', () => { firstName: 'Foo', lastName: '', }); - expect(newVersion).toEqual('0.0.2'); }); test('does not apply migration if it has the same version as state', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '0.0.54': (migrationV2 as unknown) as MigrateFunction, }, @@ -88,11 +84,10 @@ test('does not apply migration if it has the same version as state', () => { expect(newState).toEqual({ name: 'Foo', }); - expect(newVersion).toEqual('0.0.54'); }); test('does not apply migration if it has lower version', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '0.2.2': (migrationV2 as unknown) as MigrateFunction, }, @@ -105,11 +100,10 @@ test('does not apply migration if it has lower version', () => { expect(newState).toEqual({ name: 'Foo', }); - expect(newVersion).toEqual('0.3.1'); }); test('applies two migrations consecutively', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '7.14.0': (migrationV2 as unknown) as MigrateFunction, '7.14.2': (migrationV3 as unknown) as MigrateFunction, @@ -126,11 +120,10 @@ test('applies two migrations consecutively', () => { isAdmin: false, age: 0, }); - expect(newVersion).toEqual('7.14.2'); }); test('applies only migrations which are have higher semver version', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '7.14.0': (migrationV2 as unknown) as MigrateFunction, // not applied '7.14.1': (() => ({})) as MigrateFunction, // not applied @@ -148,5 +141,4 @@ test('applies only migrations which are have higher semver version', () => { isAdmin: false, age: 0, }); - expect(newVersion).toEqual('7.14.2'); }); diff --git a/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.ts b/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.ts index c16392164e3e4..6f81d0a7b9b63 100644 --- a/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.ts +++ b/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.ts @@ -12,19 +12,16 @@ import { SerializableState, VersionedState, MigrateFunctionsObject } from './typ export function migrateToLatest( migrations: MigrateFunctionsObject, { state, version: oldVersion }: VersionedState -): VersionedState { +): S { const versions = Object.keys(migrations || {}) .filter((v) => compare(v, oldVersion) > 0) .sort(compare); - if (!versions.length) return { state, version: oldVersion } as VersionedState; + if (!versions.length) return state as S; for (const version of versions) { state = migrations[version]!(state); } - return { - state: state as S, - version: versions[versions.length - 1], - }; + return state as S; } diff --git a/src/plugins/kibana_utils/common/persistable_state/types.ts b/src/plugins/kibana_utils/common/persistable_state/types.ts index 8d808dcfec2a4..a2d1751297a9f 100644 --- a/src/plugins/kibana_utils/common/persistable_state/types.ts +++ b/src/plugins/kibana_utils/common/persistable_state/types.ts @@ -174,5 +174,5 @@ export interface PersistableStateService

MigrateFunctionsObject; + getAllMigrations: () => MigrateFunctionsObject; } diff --git a/src/plugins/share/public/url_service/redirect/redirect_manager.ts b/src/plugins/share/public/url_service/redirect/redirect_manager.ts index 6148249f5a047..ad99be43f678a 100644 --- a/src/plugins/share/public/url_service/redirect/redirect_manager.ts +++ b/src/plugins/share/public/url_service/redirect/redirect_manager.ts @@ -68,7 +68,7 @@ export class RedirectManager { throw error; } - const { state: migratedParams } = migrateToLatest(locator.migrations, { + const migratedParams = migrateToLatest(locator.migrations, { state: options.params, version: options.version, }); diff --git a/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx b/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx index 9f1d5e0db4583..19164d0aefe52 100644 --- a/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx +++ b/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx @@ -81,6 +81,9 @@ export const getLegendActions = ( const Button = (

undefined} + onKeyPress={() => setPopoverOpen(!popoverOpen)} onClick={() => setPopoverOpen(!popoverOpen)} > diff --git a/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts b/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts index a601da234e078..f8dd206f8f4d5 100644 --- a/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts +++ b/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts @@ -44,7 +44,7 @@ describe('extractIndexPatterns', () => { }); test('should return index patterns', () => { - expect(extractIndexPatternValues(panel, null)).toEqual([ + expect(extractIndexPatternValues(panel, undefined)).toEqual([ '*', 'example-1-*', 'example-2-*', diff --git a/src/plugins/vis_type_timeseries/common/index_patterns_utils.ts b/src/plugins/vis_type_timeseries/common/index_patterns_utils.ts index 1224fd33daee3..1a8c277efbf7c 100644 --- a/src/plugins/vis_type_timeseries/common/index_patterns_utils.ts +++ b/src/plugins/vis_type_timeseries/common/index_patterns_utils.ts @@ -8,7 +8,7 @@ import { uniq } from 'lodash'; import type { Panel, IndexPatternValue, FetchedIndexPattern } from '../common/types'; -import { IIndexPattern, IndexPatternsService } from '../../data/common'; +import { IndexPatternsService } from '../../data/common'; export const isStringTypeIndexPattern = ( indexPatternValue: IndexPatternValue @@ -17,31 +17,27 @@ export const isStringTypeIndexPattern = ( export const getIndexPatternKey = (indexPatternValue: IndexPatternValue) => isStringTypeIndexPattern(indexPatternValue) ? indexPatternValue : indexPatternValue?.id ?? ''; -export const extractIndexPatternValues = (panel: Panel, defaultIndex: IIndexPattern | null) => { +export const extractIndexPatternValues = (panel: Panel, defaultIndexId?: string) => { const patterns: IndexPatternValue[] = []; - if (panel.index_pattern) { - patterns.push(panel.index_pattern); - } + const addIndex = (value?: IndexPatternValue) => { + if (value) { + patterns.push(value); + } else if (defaultIndexId) { + patterns.push({ id: defaultIndexId }); + } + }; + + addIndex(panel.index_pattern); panel.series.forEach((series) => { - const indexPattern = series.series_index_pattern; - if (indexPattern && series.override_index_pattern) { - patterns.push(indexPattern); + if (series.override_index_pattern) { + addIndex(series.series_index_pattern); } }); if (panel.annotations) { - panel.annotations.forEach((item) => { - const indexPattern = item.index_pattern; - if (indexPattern) { - patterns.push(indexPattern); - } - }); - } - - if (patterns.length === 0 && defaultIndex?.id) { - patterns.push({ id: defaultIndex.id }); + panel.annotations.forEach((item) => addIndex(item.index_pattern)); } return uniq(patterns).sort(); diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx index 715cf4d6709da..1dfb96b419d49 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx @@ -26,7 +26,7 @@ import { KBN_FIELD_TYPES, Query } from '../../../../../plugins/data/public'; import { AddDeleteButtons } from './add_delete_buttons'; import { ColorPicker } from './color_picker'; import { FieldSelect } from './aggs/field_select'; -import { IndexPatternSelect } from './lib/index_pattern_select'; +import { IndexPatternSelect, IndexPatternSelectProps } from './lib/index_pattern_select'; import { QueryBarWrapper } from './query_bar_wrapper'; import { YesNo } from './yes_no'; import { fetchIndexPattern } from '../../../common/index_patterns_utils'; @@ -35,7 +35,7 @@ import { getDefaultQueryLanguage } from './lib/get_default_query_language'; // @ts-expect-error not typed yet import { IconSelect } from './icon_select/icon_select'; -import type { Annotation, FetchedIndexPattern, IndexPatternValue } from '../../../common/types'; +import type { Annotation, IndexPatternValue } from '../../../common/types'; import type { VisFields } from '../lib/fetch_fields'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; @@ -68,20 +68,28 @@ export const AnnotationRow = ({ const model = useMemo(() => ({ ...getAnnotationDefaults(), ...annotation }), [annotation]); const htmlId = htmlIdGenerator(model.id); - const [fetchedIndex, setFetchedIndex] = useState(null); + const [fetchedIndex, setFetchedIndex] = useState(null); useEffect(() => { const updateFetchedIndex = async (index: IndexPatternValue) => { const { indexPatterns } = getDataStart(); + let fetchedIndexPattern: IndexPatternSelectProps['fetchedIndex'] = { + indexPattern: undefined, + indexPatternString: undefined, + }; - setFetchedIndex( - index + try { + fetchedIndexPattern = index ? await fetchIndexPattern(index, indexPatterns) : { - indexPattern: undefined, - indexPatternString: undefined, - } - ); + ...fetchedIndexPattern, + defaultIndex: await indexPatterns.getDefault(), + }; + } catch { + // nothing to be here + } + + setFetchedIndex(fetchedIndexPattern); }; updateFetchedIndex(model.index_pattern); @@ -124,7 +132,6 @@ export const AnnotationRow = ({ { @@ -144,17 +139,25 @@ export const IndexPattern = ({ useEffect(() => { async function fetchIndex() { const { indexPatterns } = getDataStart(); + let fetchedIndexPattern = { + indexPattern: undefined, + indexPatternString: undefined, + }; - setFetchedIndex( - index + try { + fetchedIndexPattern = index ? await fetchIndexPattern(index, indexPatterns, { fetchKibanaIndexForStringIndexes: true, }) : { - indexPattern: undefined, - indexPatternString: undefined, - } - ); + ...fetchedIndexPattern, + defaultIndex: await indexPatterns.getDefault(), + }; + } catch { + // nothing to be here + } + + setFetchedIndex(fetchedIndexPattern); } fetchIndex(); @@ -165,16 +168,6 @@ export const IndexPattern = ({ [model.hide_last_value_indicator, onChange] ); - const getTimefieldPlaceholder = () => { - if (!model[indexPatternName]) { - return defaultIndex?.timeFieldName; - } - - if (useKibanaIndices) { - return fetchedIndex?.indexPattern?.timeFieldName ?? undefined; - } - }; - return (
{!isTimeSeries && ( @@ -245,7 +238,6 @@ export const IndexPattern = ({ diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index.ts index 584f13e7a025b..4920677a04a2e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index.ts +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { IndexPatternSelect } from './index_pattern_select'; +export { IndexPatternSelect, IndexPatternSelectProps } from './index_pattern_select'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx b/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx index 07edfc2e6e0d7..927b3c608c16c 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx @@ -14,22 +14,23 @@ import { EuiFormRow, EuiText, EuiLink, htmlIdGenerator } from '@elastic/eui'; import { getCoreStart } from '../../../../services'; import { PanelModelContext } from '../../../contexts/panel_model_context'; -import { isStringTypeIndexPattern } from '../../../../../common/index_patterns_utils'; - import { FieldTextSelect } from './field_text_select'; import { ComboBoxSelect } from './combo_box_select'; import type { IndexPatternValue, FetchedIndexPattern } from '../../../../../common/types'; -import { DefaultIndexPatternContext } from '../../../contexts/default_index_context'; import { USE_KIBANA_INDEXES_KEY } from '../../../../../common/constants'; +import { IndexPattern } from '../../../../../../data/common'; -interface IndexPatternSelectProps { - value: IndexPatternValue; +export interface IndexPatternSelectProps { indexPatternName: string; onChange: Function; disabled?: boolean; allowIndexSwitchingMode?: boolean; - fetchedIndex: FetchedIndexPattern | null; + fetchedIndex: + | (FetchedIndexPattern & { + defaultIndex?: IndexPattern | null; + }) + | null; } const defaultIndexPatternHelpText = i18n.translate( @@ -51,7 +52,6 @@ const indexPatternLabel = i18n.translate('visTypeTimeseries.indexPatternSelect.l }); export const IndexPatternSelect = ({ - value, indexPatternName, onChange, disabled, @@ -60,7 +60,6 @@ export const IndexPatternSelect = ({ }: IndexPatternSelectProps) => { const htmlId = htmlIdGenerator(); const panelModel = useContext(PanelModelContext); - const defaultIndex = useContext(DefaultIndexPatternContext); const useKibanaIndices = Boolean(panelModel?.[USE_KIBANA_INDEXES_KEY]); const Component = useKibanaIndices ? ComboBoxSelect : FieldTextSelect; @@ -105,13 +104,11 @@ export const IndexPatternSelect = ({ id={htmlId('indexPattern')} label={indexPatternLabel} helpText={ - !value && defaultIndexPatternHelpText + (!useKibanaIndices ? queryAllIndexesHelpText : '') + fetchedIndex.defaultIndex && + defaultIndexPatternHelpText + (!useKibanaIndices ? queryAllIndexesHelpText : '') } labelAppend={ - value && - allowIndexSwitchingMode && - isStringTypeIndexPattern(value) && - !fetchedIndex.indexPattern ? ( + fetchedIndex.indexPatternString && !fetchedIndex.indexPattern ? ( diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts index 93b15402e3c24..18288f75d4c90 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts @@ -7,10 +7,13 @@ */ import type { Assign } from '@kbn/utility-types'; import type { FetchedIndexPattern, IndexPatternValue } from '../../../../../common/types'; +import type { IndexPattern } from '../../../../../../data/common'; /** @internal **/ export interface SelectIndexComponentProps { - fetchedIndex: FetchedIndexPattern; + fetchedIndex: FetchedIndexPattern & { + defaultIndex?: IndexPattern | null; + }; onIndexChange: (value: IndexPatternValue) => void; onModeChange: (useKibanaIndexes: boolean, index?: FetchedIndexPattern) => void; 'data-test-subj': string; diff --git a/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx b/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx index 215640c30f3c4..d3b249f54fe34 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx @@ -9,7 +9,6 @@ import React, { useContext, useEffect, useState } from 'react'; import { CoreStartContext } from '../contexts/query_input_bar_context'; -import { DefaultIndexPatternContext } from '../contexts/default_index_context'; import type { IndexPatternValue } from '../../../common/types'; import { QueryStringInput, QueryStringInputProps } from '../../../../../plugins/data/public'; @@ -31,7 +30,6 @@ export function QueryBarWrapper({ const [indexes, setIndexes] = useState([]); const coreStartContext = useContext(CoreStartContext); - const defaultIndex = useContext(DefaultIndexPatternContext); useEffect(() => { async function fetchIndexes() { @@ -48,15 +46,19 @@ export function QueryBarWrapper({ i.push(indexPattern); } } - } else if (defaultIndex) { - i.push(defaultIndex); + } else { + const defaultIndex = await indexPatternsService.getDefault(); + + if (defaultIndex) { + i.push(defaultIndex); + } } } setIndexes(i); } fetchIndexes(); - }, [indexPatterns, indexPatternsService, defaultIndex]); + }, [indexPatterns, indexPatternsService]); return ( { this.setState({ @@ -180,53 +179,46 @@ export class VisEditor extends Component - -
- {!this.props.vis.params.use_kibana_indexes && } -
- -
- + {!this.props.vis.params.use_kibana_indexes && } +
+ +
+ +
+ -
- -
- +
); } - componentDidMount() { - const dataStart = getDataStart(); - - dataStart.indexPatterns.getDefault().then(async (index) => { - const indexPatterns = extractIndexPatternValues(this.props.vis.params, index); - const visFields = await fetchFields(indexPatterns); + async componentDidMount() { + const indexPatterns = extractIndexPatternValues(this.props.vis.params, this.getDefaultIndex()); + const visFields = await fetchFields(indexPatterns); - this.setState({ - defaultIndex: index, - visFields, - }); + this.setState({ + visFields, }); this.props.eventEmitter.on('updateEditor', this.updateModel); @@ -236,6 +228,10 @@ export class VisEditor extends Component('defaultIndex') ?? ''; + } } // default export required for React.Lazy diff --git a/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts b/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts index 7b31ff3296425..71e38be302579 100644 --- a/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts +++ b/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts @@ -22,9 +22,9 @@ export async function fetchFields( const patterns = Array.isArray(indexes) ? indexes : [indexes]; const coreStart = getCoreStart(); const dataStart = getDataStart(); + const defaultIndex = coreStart.uiSettings.get('defaultIndex'); try { - const defaultIndexPattern = await dataStart.indexPatterns.getDefault(); const indexFields = await Promise.all( patterns.map(async (pattern) => { if (typeof pattern !== 'string' && pattern?.id) { @@ -42,17 +42,14 @@ export async function fetchFields( }) ); - const fields: VisFields = patterns.reduce( - (cumulatedFields, currentPattern, index) => ({ + const fields: VisFields = patterns.reduce((cumulatedFields, currentPattern, index) => { + const key = getIndexPatternKey(currentPattern); + return { ...cumulatedFields, - [getIndexPatternKey(currentPattern)]: indexFields[index], - }), - {} - ); - - if (defaultIndexPattern) { - fields[''] = toSanitizedFieldType(await defaultIndexPattern.getNonScriptedFields()); - } + [key]: indexFields[index], + ...(key === defaultIndex ? { '': indexFields[index] } : {}), + }; + }, {}); return fields; } catch (error) { diff --git a/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx b/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx index 5c28ca77da0c4..2ea55cf58359b 100644 --- a/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx +++ b/src/plugins/vis_type_xy/public/utils/get_legend_actions.tsx @@ -66,6 +66,9 @@ export const getLegendActions = ( const Button = (
undefined} + onKeyPress={() => setPopoverOpen(!popoverOpen)} onClick={() => setPopoverOpen(!popoverOpen)} > diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index c82036449d173..9a0adf265e5a2 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -136,6 +136,9 @@ export class VisualizeEmbeddable this.deps = deps; this.timefilter = timefilter; this.syncColors = this.input.syncColors; + this.searchSessionId = this.input.searchSessionId; + this.query = this.input.query; + this.vis = vis; this.vis.uiState.on('change', this.uiStateChangeHandler); this.vis.uiState.on('reload', this.reload); @@ -149,7 +152,7 @@ export class VisualizeEmbeddable } this.subscriptions.push( - this.getUpdated$().subscribe((value) => { + this.getInput$().subscribe(() => { const isDirty = this.handleChanges(); if (isDirty && this.handler) { diff --git a/test/functional/config.js b/test/functional/config.js index 1c0c519f21e4c..19a628be10f52 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -106,6 +106,12 @@ export default async function ({ readConfigFile }) { observabilityCases: { pathname: '/app/observability/cases', }, + fleet: { + pathname: '/app/fleet', + }, + integrations: { + pathname: '/app/integrations', + }, }, junit: { reportName: 'Chrome UI Functional Tests', diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts deleted file mode 100644 index e661a15cea3ae..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts +++ /dev/null @@ -1,75 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; -import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; - -import { - getElasticLogo, - resolveWithMissingImage, -} from '../../../../../../src/plugins/presentation_util/common/lib'; - -export enum ImageMode { - CONTAIN = 'contain', - COVER = 'cover', - STRETCH = 'stretch', -} - -interface Arguments { - dataurl: string | null; - mode: ImageMode | null; -} - -export interface Return { - type: 'image'; - mode: string; - dataurl: string; -} - -export function image(): ExpressionFunctionDefinition<'image', null, Arguments, Promise> { - const { help, args: argHelp } = getFunctionHelp().image; - const errors = getFunctionErrors().image; - return { - name: 'image', - aliases: [], - type: 'image', - inputTypes: ['null'], - help, - args: { - dataurl: { - // This was accepting dataurl, but there was no facility in fn for checking type and handling a dataurl type. - types: ['string', 'null'], - help: argHelp.dataurl, - aliases: ['_', 'url'], - default: null, - }, - mode: { - types: ['string'], - help: argHelp.mode, - default: 'contain', - options: Object.values(ImageMode), - }, - }, - fn: async (input, { dataurl, mode }) => { - if (!mode || !Object.values(ImageMode).includes(mode)) { - throw errors.invalidImageMode(); - } - const { elasticLogo } = await getElasticLogo(); - - if (dataurl === null) { - dataurl = elasticLogo; - } - - const modeStyle = mode === 'stretch' ? '100% 100%' : mode; - return { - type: 'image', - mode: modeStyle, - dataurl: resolveWithMissingImage(dataurl, elasticLogo) as string, - }; - }, - }; -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts index 6ab7abac985cc..3de3275ebb231 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts @@ -29,7 +29,6 @@ import { gt } from './gt'; import { gte } from './gte'; import { head } from './head'; import { ifFn } from './if'; -import { image } from './image'; import { joinRows } from './join_rows'; import { lt } from './lt'; import { lte } from './lte'; @@ -79,7 +78,6 @@ export const functions = [ gte, head, ifFn, - image, lt, lte, joinRows, diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/image.stories.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/image.stories.tsx deleted file mode 100644 index a8ac14768bfa5..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/image.stories.tsx +++ /dev/null @@ -1,29 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { storiesOf } from '@storybook/react'; -import { image } from '../image'; -import { getElasticLogo } from '../../../../../../src/plugins/presentation_util/common/lib'; -import { waitFor } from '../../../../../../src/plugins/presentation_util/public/__stories__'; -import { Render } from './render'; - -const Renderer = ({ elasticLogo }: { elasticLogo: string }) => { - const config = { - type: 'image' as 'image', - mode: 'cover', - dataurl: elasticLogo, - }; - - return ; -}; - -storiesOf('renderers/image', module).add( - 'default', - (_, props) => , - { decorators: [waitFor(getElasticLogo())] } -); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts index 8eabae4c661d2..80ca5e68860b4 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { image } from './image'; import { markdown } from './markdown'; import { metric } from './metric'; import { pie } from './pie'; @@ -14,6 +13,6 @@ import { progress } from './progress'; import { text } from './text'; import { table } from './table'; -export const renderFunctions = [image, markdown, metric, pie, plot, progress, table, text]; +export const renderFunctions = [markdown, metric, pie, plot, progress, table, text]; export const renderFunctionFactories = []; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts index 0c824fb3dd25e..eab3b88b0fe26 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { imageRenderer } from '../../../../../src/plugins/expression_image/public'; import { errorRenderer, debugRenderer } from '../../../../../src/plugins/expression_error/public'; import { repeatImageRenderer } from '../../../../../src/plugins/expression_repeat_image/public'; import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_image/public'; @@ -14,6 +15,7 @@ export const renderFunctions = [ revealImageRenderer, debugRenderer, errorRenderer, + imageRenderer, shapeRenderer, repeatImageRenderer, ]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/image.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/image.tsx deleted file mode 100644 index 78e3ecb7a4c95..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/image.tsx +++ /dev/null @@ -1,41 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import ReactDOM from 'react-dom'; -import React from 'react'; -import { - getElasticLogo, - isValidUrl, -} from '../../../../../src/plugins/presentation_util/common/lib'; -import { Return as Arguments } from '../functions/common/image'; -import { RendererStrings } from '../../i18n'; -import { RendererFactory } from '../../types'; - -const { image: strings } = RendererStrings; - -export const image: RendererFactory = () => ({ - name: 'image', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render: async (domNode, config, handlers) => { - const { elasticLogo } = await getElasticLogo(); - const dataurl = isValidUrl(config.dataurl) ? config.dataurl : elasticLogo; - - const style = { - height: '100%', - backgroundImage: `url(${dataurl})`, - backgroundRepeat: 'no-repeat', - backgroundPosition: 'center center', - backgroundSize: config.mode, - }; - - ReactDOM.render(
, domNode, () => handlers.done()); - - handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - }, -}); diff --git a/x-pack/plugins/canvas/i18n/functions/dict/image.ts b/x-pack/plugins/canvas/i18n/functions/dict/image.ts deleted file mode 100644 index b619d550f9efd..0000000000000 --- a/x-pack/plugins/canvas/i18n/functions/dict/image.ts +++ /dev/null @@ -1,64 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { image } from '../../../canvas_plugin_src/functions/common/image'; -import { FunctionHelp } from '../function_help'; -import { FunctionFactory } from '../../../types'; -import { - URL, - BASE64, - IMAGE_MODE_CONTAIN, - IMAGE_MODE_COVER, - IMAGE_MODE_STRETCH, -} from '../../constants'; - -export const help: FunctionHelp> = { - help: i18n.translate('xpack.canvas.functions.imageHelpText', { - defaultMessage: - 'Displays an image. Provide an image asset as a {BASE64} data {URL}, or pass in a sub-expression.', - values: { - BASE64, - URL, - }, - }), - args: { - dataurl: i18n.translate('xpack.canvas.functions.image.args.dataurlHelpText', { - defaultMessage: 'The {https} {URL} or {BASE64} data {URL} of an image.', - values: { - BASE64, - https: 'HTTP(S)', - URL, - }, - }), - mode: i18n.translate('xpack.canvas.functions.image.args.modeHelpText', { - defaultMessage: - '{contain} shows the entire image, scaled to fit. ' + - '{cover} fills the container with the image, cropping from the sides or bottom as needed. ' + - '{stretch} resizes the height and width of the image to 100% of the container.', - values: { - contain: `\`"${IMAGE_MODE_CONTAIN}"\``, - cover: `\`"${IMAGE_MODE_COVER}"\``, - stretch: `\`"${IMAGE_MODE_STRETCH}"\``, - }, - }), - }, -}; - -export const errors = { - invalidImageMode: () => - new Error( - i18n.translate('xpack.canvas.functions.image.invalidImageModeErrorMessage', { - defaultMessage: '"mode" must be "{contain}", "{cover}", or "{stretch}"', - values: { - contain: IMAGE_MODE_CONTAIN, - cover: IMAGE_MODE_COVER, - stretch: IMAGE_MODE_STRETCH, - }, - }) - ), -}; diff --git a/x-pack/plugins/canvas/i18n/functions/function_errors.ts b/x-pack/plugins/canvas/i18n/functions/function_errors.ts index a01cb09a38347..1e515ece63569 100644 --- a/x-pack/plugins/canvas/i18n/functions/function_errors.ts +++ b/x-pack/plugins/canvas/i18n/functions/function_errors.ts @@ -14,7 +14,6 @@ import { errors as csv } from './dict/csv'; import { errors as date } from './dict/date'; import { errors as demodata } from './dict/demodata'; import { errors as getCell } from './dict/get_cell'; -import { errors as image } from './dict/image'; import { errors as joinRows } from './dict/join_rows'; import { errors as ply } from './dict/ply'; import { errors as pointseries } from './dict/pointseries'; @@ -32,7 +31,6 @@ export const getFunctionErrors = () => ({ date, demodata, getCell, - image, joinRows, ply, pointseries, diff --git a/x-pack/plugins/canvas/i18n/functions/function_help.ts b/x-pack/plugins/canvas/i18n/functions/function_help.ts index 0ca2c01718b49..bb5681264efc9 100644 --- a/x-pack/plugins/canvas/i18n/functions/function_help.ts +++ b/x-pack/plugins/canvas/i18n/functions/function_help.ts @@ -40,7 +40,6 @@ import { help as gt } from './dict/gt'; import { help as gte } from './dict/gte'; import { help as head } from './dict/head'; import { help as ifFn } from './dict/if'; -import { help as image } from './dict/image'; import { help as joinRows } from './dict/join_rows'; import { help as location } from './dict/location'; import { help as lt } from './dict/lt'; @@ -199,7 +198,6 @@ export const getFunctionHelp = (): FunctionHelpDict => ({ head, if: ifFn, joinRows, - image, location, lt, lte, diff --git a/x-pack/plugins/canvas/i18n/renderers.ts b/x-pack/plugins/canvas/i18n/renderers.ts index 80f1a5aecc89e..faa43fe03817e 100644 --- a/x-pack/plugins/canvas/i18n/renderers.ts +++ b/x-pack/plugins/canvas/i18n/renderers.ts @@ -55,16 +55,6 @@ export const RendererStrings = { defaultMessage: 'Renders an embeddable Saved Object from other parts of Kibana', }), }, - image: { - getDisplayName: () => - i18n.translate('xpack.canvas.renderer.image.displayName', { - defaultMessage: 'Image', - }), - getHelpDescription: () => - i18n.translate('xpack.canvas.renderer.image.helpDescription', { - defaultMessage: 'Render an image', - }), - }, markdown: { getDisplayName: () => i18n.translate('xpack.canvas.renderer.markdown.displayName', { diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index 1692d90884a62..acede1c9c2aa8 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -11,6 +11,7 @@ "data", "embeddable", "expressionError", + "expressionImage", "expressionRepeatImage", "expressionRevealImage", "expressionShape", diff --git a/x-pack/plugins/canvas/public/components/datasource/datasource.js b/x-pack/plugins/canvas/public/components/datasource/datasource.js index c2aa9b7f5c5ce..acda812792c45 100644 --- a/x-pack/plugins/canvas/public/components/datasource/datasource.js +++ b/x-pack/plugins/canvas/public/components/datasource/datasource.js @@ -5,20 +5,17 @@ * 2.0. */ -import { compose, branch, renderComponent } from 'recompose'; +import React from 'react'; import PropTypes from 'prop-types'; import { NoDatasource } from './no_datasource'; import { DatasourceComponent } from './datasource_component'; -const branches = [ - // rendered when there is no datasource in the expression - branch( - ({ datasource, stateDatasource }) => !datasource || !stateDatasource, - renderComponent(NoDatasource) - ), -]; +export const Datasource = (props) => { + const { datasource, stateDatasource } = props; + if (!datasource || !stateDatasource) return ; -export const Datasource = compose(...branches)(DatasourceComponent); + return ; +}; Datasource.propTypes = { args: PropTypes.object, diff --git a/x-pack/plugins/canvas/public/components/datasource/index.js b/x-pack/plugins/canvas/public/components/datasource/index.js index 91f34f4a127ec..5a0cbf6d05bd9 100644 --- a/x-pack/plugins/canvas/public/components/datasource/index.js +++ b/x-pack/plugins/canvas/public/components/datasource/index.js @@ -5,9 +5,9 @@ * 2.0. */ +import React, { useState, useCallback } from 'react'; import { PropTypes } from 'prop-types'; import { connect } from 'react-redux'; -import { withState, withHandlers, compose } from 'recompose'; import { get } from 'lodash'; import { datasourceRegistry } from '../../expression_types'; import { getServerFunctions } from '../../state/selectors/app'; @@ -15,6 +15,36 @@ import { getSelectedElement, getSelectedPage } from '../../state/selectors/workp import { setArgumentAtIndex, setAstAtIndex, flushContext } from '../../state/actions/elements'; import { Datasource as Component } from './datasource'; +const DatasourceComponent = (props) => { + const { args, datasource } = props; + const [stateArgs, updateArgs] = useState(args); + const [selecting, setSelecting] = useState(false); + const [previewing, setPreviewing] = useState(false); + const [isInvalid, setInvalid] = useState(false); + const [stateDatasource, selectDatasource] = useState(datasource); + + const resetArgs = useCallback(() => { + updateArgs(args); + }, [updateArgs, args]); + + return ( + + ); +}; + const mapStateToProps = (state) => ({ element: getSelectedElement(state), pageId: getSelectedPage(state), @@ -82,17 +112,11 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { }; }; -export const Datasource = compose( - connect(mapStateToProps, mapDispatchToProps, mergeProps), - withState('stateArgs', 'updateArgs', ({ args }) => args), - withState('selecting', 'setSelecting', false), - withState('previewing', 'setPreviewing', false), - withState('isInvalid', 'setInvalid', false), - withState('stateDatasource', 'selectDatasource', ({ datasource }) => datasource), - withHandlers({ - resetArgs: ({ updateArgs, args }) => () => updateArgs(args), - }) -)(Component); +export const Datasource = connect( + mapStateToProps, + mapDispatchToProps, + mergeProps +)(DatasourceComponent); Datasource.propTypes = { done: PropTypes.func, diff --git a/x-pack/plugins/canvas/public/components/element_content/element_content.js b/x-pack/plugins/canvas/public/components/element_content/element_content.js index ce5dd1a990261..ee0ce5193102e 100644 --- a/x-pack/plugins/canvas/public/components/element_content/element_content.js +++ b/x-pack/plugins/canvas/public/components/element_content/element_content.js @@ -7,7 +7,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { pure, compose, branch, renderComponent } from 'recompose'; import Style from 'style-it'; import { getType } from '@kbn/interpreter/common'; import { Loading } from '../loading'; @@ -16,40 +15,34 @@ import { ElementShareContainer } from '../element_share_container'; import { InvalidExpression } from './invalid_expression'; import { InvalidElementType } from './invalid_element_type'; -/* - Branches - Short circut rendering of the element if the element isn't ready or isn't valid. -*/ -const branches = [ - // no renderable or renderable config value, render loading - branch( - ({ renderable, state }) => { - return !state || !renderable; - }, - renderComponent(({ backgroundColor }) => ) - ), +const isLoading = (renderable, state) => !state || !renderable; - // renderable is available, but no matching element is found, render invalid - branch(({ renderable, renderFunction }) => { - return renderable && getType(renderable) !== 'render' && !renderFunction; - }, renderComponent(InvalidElementType)), +const isNotValidForRendering = (renderable, renderFunction) => + renderable && getType(renderable) !== 'render' && !renderFunction; - // error state, render invalid expression notice - branch(({ renderable, renderFunction, state }) => { - return ( - state === 'error' || // The renderable has an error - getType(renderable) !== 'render' || // The renderable isn't, well, renderable - !renderFunction // We can't find an element in the registry for this - ); - }, renderComponent(InvalidExpression)), -]; +const isNotValidExpression = (renderable, renderFunction, state) => + state === 'error' || // The renderable has an error + getType(renderable) !== 'render' || // The renderable isn't, well, renderable + !renderFunction; // We can't find an element in the registry for this -export const ElementContent = compose( - pure, - ...branches -)(({ renderable, renderFunction, width, height, handlers }) => { +export const ElementContent = (props) => { + const { renderable, renderFunction, width, height, handlers, backgroundColor, state } = props; const { onComplete } = handlers; + if (isLoading(renderable, state)) { + return ; + } + + // renderable is available, but no matching element is found, render invalid + if (isNotValidForRendering(renderable, renderFunction)) { + return ; + } + + // error state, render invalid expression notice + if (isNotValidExpression(renderable, renderFunction, state)) { + return ; + } + return Style.it( renderable.css,
); -}); +}; ElementContent.propTypes = { renderable: PropTypes.shape({ diff --git a/x-pack/plugins/canvas/shareable_runtime/components/__stories__/rendered_element.stories.tsx b/x-pack/plugins/canvas/shareable_runtime/components/__stories__/rendered_element.stories.tsx index db74dd7514ee9..ee609f42f1cf9 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/__stories__/rendered_element.stories.tsx +++ b/x-pack/plugins/canvas/shareable_runtime/components/__stories__/rendered_element.stories.tsx @@ -9,7 +9,7 @@ import { storiesOf } from '@storybook/react'; import React from 'react'; import { ExampleContext } from '../../test/context_example'; -import { image } from '../../../canvas_plugin_src/renderers/image'; +import { imageFunction } from '../../../../../../src/plugins/expression_image/__fixtures__'; import { sharedWorkpads } from '../../test'; import { RenderedElement, RenderedElementComponent } from '../rendered_element'; @@ -30,7 +30,7 @@ storiesOf('shareables/RenderedElement', module) ; } /** @@ -64,7 +65,7 @@ export class RenderedElementComponent extends PureComponent { try { fn.render(this.ref.current, value.value, createHandlers()); - } catch (e) { + } catch (e: any) { // eslint-disable-next-line no-console console.log(as, e.message); } diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index d5f0a2196814e..9b86ebddbd9b9 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -5,7 +5,6 @@ * 2.0. */ -import { image } from '../canvas_plugin_src/renderers/image'; import { markdown } from '../canvas_plugin_src/renderers/markdown'; import { metric } from '../canvas_plugin_src/renderers/metric'; import { pie } from '../canvas_plugin_src/renderers/pie'; @@ -13,6 +12,7 @@ import { plot } from '../canvas_plugin_src/renderers/plot'; import { progress } from '../canvas_plugin_src/renderers/progress'; import { table } from '../canvas_plugin_src/renderers/table'; import { text } from '../canvas_plugin_src/renderers/text'; +import { imageRenderer as image } from '../../../../src/plugins/expression_image/public'; import { errorRenderer as error, debugRenderer as debug, diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json index 6181df5abe464..6d57aee565c89 100644 --- a/x-pack/plugins/canvas/tsconfig.json +++ b/x-pack/plugins/canvas/tsconfig.json @@ -32,6 +32,7 @@ { "path": "../../../src/plugins/embeddable/tsconfig.json" }, { "path": "../../../src/plugins/expressions/tsconfig.json" }, { "path": "../../../src/plugins/expression_error/tsconfig.json" }, + { "path": "../../../src/plugins/expression_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_repeat_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_reveal_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_shape/tsconfig.json" }, diff --git a/x-pack/plugins/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx index 5ac594842d392..11be87f146851 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx @@ -10,7 +10,6 @@ import React, { memo, useEffect, useState } from 'react'; import type { AppMountParameters } from 'kibana/public'; import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel, EuiPortal } from '@elastic/eui'; import type { History } from 'history'; -import { createHashHistory } from 'history'; import { Router, Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -20,7 +19,10 @@ import useObservable from 'react-use/lib/useObservable'; import type { TopNavMenuData } from 'src/plugins/navigation/public'; import type { FleetConfigType, FleetStartServices } from '../../plugin'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + RedirectAppLinks, +} from '../../../../../../src/plugins/kibana_react/public'; import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common'; import { PackageInstallProvider, useUrlModal } from '../integrations/hooks'; @@ -28,7 +30,6 @@ import { PackageInstallProvider, useUrlModal } from '../integrations/hooks'; import { ConfigContext, FleetStatusProvider, - IntraAppStateProvider, KibanaVersionContext, sendGetPermissionsCheck, sendSetup, @@ -215,43 +216,31 @@ export const FleetAppContext: React.FC<{ }> = memo( ({ children, startServices, config, history, kibanaVersion, extensions, routerHistory }) => { const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); - const [routerHistoryInstance] = useState(routerHistory || createHashHistory()); - // Sync our hash history with Kibana scoped history - useEffect(() => { - const unlistenParentHistory = history.listen(() => { - const newHash = createHashHistory(); - if (newHash.location.pathname !== routerHistoryInstance.location.pathname) { - routerHistoryInstance.replace(newHash.location.pathname + newHash.location.search || ''); - } - }); - - return unlistenParentHistory; - }, [history, routerHistoryInstance]); return ( - - - - - - - - - - + + + + + + + + + + {children} - - - - - - - - - + + + + + + + + + ); } ); @@ -277,7 +266,7 @@ const FleetTopNav = memo( defaultMessage: 'Fleet settings', }), iconType: 'gear', - run: () => (window.location.href = getModalHref('settings')), + run: () => services.application.navigateToUrl(getModalHref('settings')), }, ]; return ( @@ -327,7 +316,26 @@ export const AppRoutes = memo( - + { + // BWC < 7.15 Fleet was using a hash router: redirect old routes using hash + const shouldRedirectHash = location.pathname === '' && location.hash.length > 0; + if (!shouldRedirectHash) { + return ; + } + const pathname = location.hash.replace(/^#(\/fleet)?/, ''); + + return ( + + ); + }} + /> ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx index c0c425447e556..3b0ab9c62ca11 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx @@ -150,18 +150,30 @@ const breadcrumbGetters: { }; export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) { - const { chrome, http } = useStartServices(); + const { chrome, http, application } = useStartServices(); const breadcrumbs = - breadcrumbGetters[page]?.(values).map((breadcrumb) => ({ - ...breadcrumb, - href: breadcrumb.href + breadcrumbGetters[page]?.(values).map((breadcrumb) => { + const href = breadcrumb.href ? http.basePath.prepend( - `${breadcrumb.useIntegrationsBasePath ? INTEGRATIONS_BASE_PATH : FLEET_BASE_PATH}#${ + `${breadcrumb.useIntegrationsBasePath ? INTEGRATIONS_BASE_PATH : FLEET_BASE_PATH}${ breadcrumb.href }` ) - : undefined, - })) || []; + : undefined; + return { + ...breadcrumb, + href, + onClick: href + ? (ev: React.MouseEvent) => { + if (ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey) { + return; + } + ev.preventDefault(); + application.navigateToUrl(href); + } + : undefined, + }; + }) || []; const docTitle: string[] = [...breadcrumbs] .reverse() .map((breadcrumb) => breadcrumb.text as string); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx index a624d8ced9180..c115089cccb1e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx @@ -31,7 +31,7 @@ describe('when on the package policy create page', () => { beforeEach(() => { testRenderer = createFleetTestRendererMock(); mockApiCalls(testRenderer.startServices.http); - testRenderer.history.push(createPageUrlPath); + testRenderer.mountHistory.push(createPageUrlPath); }); describe('and Route state is provided via Fleet HashRouter', () => { @@ -43,7 +43,7 @@ describe('when on the package policy create page', () => { onCancelNavigateTo: [PLUGIN_ID, { path: '/cancel/url/here' }], }; - testRenderer.history.replace({ + testRenderer.mountHistory.replace({ pathname: createPageUrlPath, state: expectedRouteState, }); @@ -72,18 +72,18 @@ describe('when on the package policy create page', () => { expect(cancelButton.href).toBe(expectedRouteState.onCancelUrl); }); - it('should redirect via Fleet HashRouter when cancel link is clicked', () => { + it('should redirect via history when cancel link is clicked', () => { act(() => { cancelLink.click(); }); - expect(testRenderer.history.location.pathname).toBe('/cancel/url/here'); + expect(testRenderer.mountHistory.location.pathname).toBe('/cancel/url/here'); }); - it('should redirect via Fleet HashRouter when cancel Button (button bar) is clicked', () => { + it('should redirect via history when cancel Button (button bar) is clicked', () => { act(() => { cancelButton.click(); }); - expect(testRenderer.history.location.pathname).toBe('/cancel/url/here'); + expect(testRenderer.mountHistory.location.pathname).toBe('/cancel/url/here'); }); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx index 39340a21d349b..e19cb7b1ca5e8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx @@ -39,7 +39,7 @@ export const NoPackagePolicies = memo<{ policyId: string }>(({ policyId }) => { fill onClick={() => application.navigateToApp(INTEGRATIONS_PLUGIN_ID, { - path: `#${pagePathGetters.integrations_all()[1]}`, + path: pagePathGetters.integrations_all()[1], state: { forAgentPolicyId: policyId }, }) } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx index 49af14b7234fa..0d2d8e1882183 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx @@ -199,7 +199,7 @@ export const PackagePoliciesTable: React.FunctionComponent = ({ iconType="refresh" onClick={() => { application.navigateToApp(INTEGRATIONS_PLUGIN_ID, { - path: `#${pagePathGetters.integrations_all()[1]}`, + path: pagePathGetters.integrations_all()[1], state: { forAgentPolicyId: agentPolicy.id }, }); }} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx index d8db44e28e4af..19f0216a39e03 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { HashRouter as Router, Switch, Route } from 'react-router-dom'; +import { Router, Switch, Route, useHistory } from 'react-router-dom'; import { FLEET_ROUTING_PATHS } from '../../constants'; import { useBreadcrumbs } from '../../hooks'; @@ -20,9 +20,10 @@ import { EditPackagePolicyPage } from './edit_package_policy_page'; export const AgentPolicyApp: React.FunctionComponent = () => { useBreadcrumbs('policies'); + const history = useHistory(); return ( - + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx index 52a4c9d17648b..494541a00fc0a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { HashRouter as Router, Route, Switch } from 'react-router-dom'; +import { Router, Route, Switch, useHistory } from 'react-router-dom'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPortal } from '@elastic/eui'; import { FLEET_ROUTING_PATHS } from '../../constants'; @@ -30,7 +30,7 @@ import { FleetServerUpgradeModal } from './components/fleet_server_upgrade_modal export const AgentsApp: React.FunctionComponent = () => { useBreadcrumbs('agent_list'); - + const history = useHistory(); const { agents } = useConfig(); const capabilities = useCapabilities(); @@ -118,7 +118,7 @@ export const AgentsApp: React.FunctionComponent = () => { ) : undefined; return ( - + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx index c660d3ed29767..51c8346a665cf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { HashRouter as Router, Route, Switch } from 'react-router-dom'; +import { Router, Route, Switch, useHistory } from 'react-router-dom'; import { FLEET_ROUTING_PATHS } from '../../constants'; import { DefaultLayout } from '../../layouts'; @@ -14,8 +14,10 @@ import { DefaultLayout } from '../../layouts'; import { DataStreamListPage } from './list_page'; export const DataStreamApp: React.FunctionComponent = () => { + const history = useHistory(); + return ( - + diff --git a/x-pack/plugins/fleet/public/applications/integrations/app.tsx b/x-pack/plugins/fleet/public/applications/integrations/app.tsx index ae59d33e44b82..f0c94b51677ee 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/app.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/app.tsx @@ -9,7 +9,6 @@ import React, { memo, useEffect, useState } from 'react'; import type { AppMountParameters } from 'kibana/public'; import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel, EuiPortal } from '@elastic/eui'; import type { History } from 'history'; -import { createHashHistory } from 'history'; import { Router, Redirect, Route, Switch } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -26,7 +25,10 @@ import { import type { FleetConfigType, FleetStartServices } from '../../plugin'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + RedirectAppLinks, +} from '../../../../../../src/plugins/kibana_react/public'; import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common'; import { AgentPolicyContextProvider, useUrlModal } from './hooks'; @@ -39,7 +41,7 @@ import type { UIExtensionsStorage } from './types'; import { EPMApp } from './sections/epm'; import { DefaultLayout, WithoutHeaderLayout } from './layouts'; import { PackageInstallProvider } from './hooks'; -import { useBreadcrumbs, IntraAppStateProvider, UIExtensionsContext } from './hooks'; +import { useBreadcrumbs, UIExtensionsContext } from './hooks'; const ErrorLayout = ({ children }: { children: JSX.Element }) => ( @@ -185,25 +187,12 @@ export const IntegrationsAppContext: React.FC<{ kibanaVersion: string; extensions: UIExtensionsStorage; /** For testing purposes only */ - routerHistory?: History; -}> = memo( - ({ children, startServices, config, history, kibanaVersion, extensions, routerHistory }) => { - const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); - const [routerHistoryInstance] = useState(routerHistory || createHashHistory()); - - // Sync our hash history with Kibana scoped history - useEffect(() => { - const unlistenParentHistory = history.listen(() => { - const newHash = createHashHistory(); - if (newHash.location.pathname !== routerHistoryInstance.location.pathname) { - routerHistoryInstance.replace(newHash.location.pathname + newHash.location.search || ''); - } - }); - - return unlistenParentHistory; - }, [history, routerHistoryInstance]); + routerHistory?: History; // TODO remove +}> = memo(({ children, startServices, config, history, kibanaVersion, extensions }) => { + const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); - return ( + return ( + @@ -212,15 +201,13 @@ export const IntegrationsAppContext: React.FC<{ - - - - - {children} - - - - + + + + {children} + + + @@ -229,9 +216,9 @@ export const IntegrationsAppContext: React.FC<{ - ); - } -); + + ); +}); export const AppRoutes = memo(() => { const { modal, setModal } = useUrlModal(); @@ -250,7 +237,26 @@ export const AppRoutes = memo(() => { - + { + // BWC < 7.15 Fleet was using a hash router: redirect old routes using hash + const shouldRedirectHash = location.pathname === '' && location.hash.length > 0; + if (!shouldRedirectHash) { + return ; + } + const pathname = location.hash.replace(/^#/, ''); + + return ( + + ); + }} + /> ); diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx index 19f72fdc69bba..63c8f1cbd318c 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx @@ -51,14 +51,23 @@ const breadcrumbGetters: { }; export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) { - const { chrome, http } = useStartServices(); + const { chrome, http, application } = useStartServices(); const breadcrumbs: ChromeBreadcrumb[] = - breadcrumbGetters[page]?.(values).map((breadcrumb) => ({ - ...breadcrumb, - href: breadcrumb.href - ? http.basePath.prepend(`${INTEGRATIONS_BASE_PATH}#${breadcrumb.href}`) - : undefined, - })) || []; + breadcrumbGetters[page]?.(values).map((breadcrumb) => { + const href = breadcrumb.href + ? http.basePath.prepend(`${INTEGRATIONS_BASE_PATH}${breadcrumb.href}`) + : undefined; + return { + ...breadcrumb, + href, + onClick: href + ? (ev: React.MouseEvent) => { + ev.preventDefault(); + application.navigateToUrl(href); + } + : undefined, + }; + }) || []; const docTitle: string[] = [...breadcrumbs] .reverse() .map((breadcrumb) => breadcrumb.text as string); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx index f436c248abd3c..31a3e2164a247 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx @@ -45,22 +45,22 @@ describe('when on integration detail', () => { )); - beforeEach(() => { + beforeEach(async () => { testRenderer = createIntegrationsTestRendererMock(); mockedApi = mockApiCalls(testRenderer.startServices.http); - testRenderer.history.push(detailPageUrlPath); + act(() => testRenderer.mountHistory.push(detailPageUrlPath)); }); afterEach(() => { cleanup(); - window.location.hash = '#/'; }); describe('and the package is installed', () => { beforeEach(() => render()); it('should display agent policy usage count', async () => { - await mockedApi.waitForApi(); + await act(() => mockedApi.waitForApi()); + expect(renderResult.queryByTestId('agentPolicyCount')).not.toBeNull(); }); @@ -105,11 +105,11 @@ describe('when on integration detail', () => { it('should redirect if custom url is accessed', () => { act(() => { - testRenderer.history.push( + testRenderer.mountHistory.push( pagePathGetters.integration_details_custom({ pkgkey: 'nginx-0.3.7' })[1] ); }); - expect(testRenderer.history.location.pathname).toEqual('/detail/nginx-0.3.7/overview'); + expect(testRenderer.mountHistory.location.pathname).toEqual('/detail/nginx-0.3.7/overview'); }); }); @@ -153,7 +153,7 @@ describe('when on integration detail', () => { it('should display custom content when tab is clicked', async () => { act(() => { - testRenderer.history.push( + testRenderer.mountHistory.push( pagePathGetters.integration_details_custom({ pkgkey: 'nginx-0.3.7' })[1] ); }); @@ -200,7 +200,7 @@ describe('when on integration detail', () => { it('should display custom assets when tab is clicked', async () => { act(() => { - testRenderer.history.push( + testRenderer.mountHistory.push( pagePathGetters.integration_details_assets({ pkgkey: 'nginx-0.3.7' })[1] ); }); @@ -215,7 +215,7 @@ describe('when on integration detail', () => { it('should link to the create page', () => { const addButton = renderResult.getByTestId('addIntegrationPolicyButton') as HTMLAnchorElement; expect(addButton.href).toEqual( - 'http://localhost/mock/app/fleet#/integrations/nginx-0.3.7/add-integration' + 'http://localhost/mock/app/fleet/integrations/nginx-0.3.7/add-integration' ); }); }); @@ -223,7 +223,7 @@ describe('when on integration detail', () => { describe('and on the Policies Tab', () => { const policiesTabURLPath = pagePathGetters.integration_details_policies({ pkgkey })[1]; beforeEach(() => { - testRenderer.history.push(policiesTabURLPath); + testRenderer.mountHistory.push(policiesTabURLPath); render(); }); @@ -238,7 +238,7 @@ describe('when on integration detail', () => { 'integrationNameLink' )[0] as HTMLAnchorElement; expect(firstPolicy.href).toEqual( - 'http://localhost/mock/app/integrations#/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc' + 'http://localhost/mock/app/integrations/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc' ); }); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 21a139ad11baa..26869f8fea574 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -235,22 +235,18 @@ export function Detail() { redirectToPath = [ PLUGIN_ID, { - path: `#${ - pagePathGetters.policy_details({ - policyId: agentPolicyIdFromContext, - })[1] - }`, + path: pagePathGetters.policy_details({ + policyId: agentPolicyIdFromContext, + })[1], }, ]; } else { redirectToPath = [ INTEGRATIONS_PLUGIN_ID, { - path: `#${ - pagePathGetters.integration_details_policies({ - pkgkey, - })[1] - }`, + path: pagePathGetters.integration_details_policies({ + pkgkey, + })[1], }, ]; } @@ -260,16 +256,16 @@ export function Detail() { onCancelNavigateTo: [ INTEGRATIONS_PLUGIN_ID, { - path: currentPath, + path: pagePathGetters.integration_details_overview({ + pkgkey, + })[1], }, ], onCancelUrl: currentPath, }; services.application.navigateToApp(PLUGIN_ID, { - // Necessary because of Fleet's HashRouter. Can be changed when - // https://github.com/elastic/kibana/issues/96134 is resolved - path: `#${path}`, + path, state: redirectBackRouteState, }); }, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx index 14f378bc379a6..3b161a375e7ce 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx @@ -53,7 +53,7 @@ const LatestVersionLink = ({ name, version }: { name: string; version: string }) pkgkey: `${name}-${version}`, }); return ( - + { - forRoute: string; - routeState?: S; -} - -const IntraAppStateContext = React.createContext({ forRoute: '' }); -const wasHandled = new WeakSet(); - -/** - * Provides a bridget between Kibana's ScopedHistory instance (normally used with BrowserRouter) - * and the Hash router used within the app in order to enable state to be used between kibana - * apps - */ -export const IntraAppStateProvider = memo<{ - kibanaScopedHistory: AppMountParameters['history']; - children: React.ReactNode; -}>(({ kibanaScopedHistory, children }) => { - const internalAppToAppState = useMemo(() => { - return { - forRoute: new URL(`${kibanaScopedHistory.location.hash.substr(1)}`, 'http://localhost') - .pathname, - routeState: kibanaScopedHistory.location.state as AnyIntraAppRouteState, - }; - }, [kibanaScopedHistory.location.state, kibanaScopedHistory.location.hash]); - return ( - - {children} - - ); -}); - /** * Retrieve UI Route state from the React Router History for the current URL location. * This state can be used by other Kibana Apps to influence certain behaviours in Ingest, for example, * redirecting back to an given Application after a craete action. */ -export function useIntraAppState(): - | IntraAppState['routeState'] - | undefined { +export function useIntraAppState(): S | undefined { const location = useLocation(); - const intraAppState = useContext(IntraAppStateContext); - if (!intraAppState) { - throw new Error('Hook called outside of IntraAppStateContext'); - } - return useMemo(() => { - // Due to the use of HashRouter in Ingest, we only want state to be returned - // once so that it does not impact navigation to the page from within the - // ingest app. side affect is that the browser back button would not work - // consistently either. - - if (location.pathname === intraAppState.forRoute && !wasHandled.has(intraAppState)) { - wasHandled.add(intraAppState); - return intraAppState.routeState as S; - } - // Default is to return the state in the Fleet HashRouter, in order to enable use of route state - // that is used via Kibana's ScopedHistory from within the Fleet HashRouter (ex. things like - // `core.application.navigateTo()` - // Once this https://github.com/elastic/kibana/issues/70358 is implemented (move to BrowserHistory - // using kibana's ScopedHistory), then this work-around can be removed. - return location.state as S; - }, [intraAppState, location.pathname, location.state]); + return location.state as S; } diff --git a/x-pack/plugins/fleet/public/hooks/use_link.ts b/x-pack/plugins/fleet/public/hooks/use_link.ts index 6917e0f5c3b8e..846ca9d0fdafa 100644 --- a/x-pack/plugins/fleet/public/hooks/use_link.ts +++ b/x-pack/plugins/fleet/public/hooks/use_link.ts @@ -27,7 +27,7 @@ export const useLink = () => { core.http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets/${path}`), getHref: (page: StaticPage | DynamicPage, values?: DynamicPagePathValues) => { const [basePath, path] = getSeparatePaths(page, values); - return core.http.basePath.prepend(`${basePath}#${path}`); + return core.http.basePath.prepend(`${basePath}${path}`); }, }; }; diff --git a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx index 71c9650709ee2..d0724545ee902 100644 --- a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx +++ b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx @@ -6,7 +6,7 @@ */ import type { History } from 'history'; -import { createMemoryHistory, createHashHistory } from 'history'; +import { createMemoryHistory } from 'history'; import React, { memo } from 'react'; import type { RenderOptions, RenderResult } from '@testing-library/react'; import { render as reactRender, act } from '@testing-library/react'; @@ -47,9 +47,10 @@ export const createFleetTestRendererMock = (): TestRenderer => { const basePath = '/mock'; const extensions: UIExtensionsStorage = {}; const startServices = createStartServices(basePath); + const history = createMemoryHistory({ initialEntries: [basePath] }); const testRendererMocks: TestRenderer = { - history: createHashHistory(), - mountHistory: new ScopedHistory(createMemoryHistory({ initialEntries: [basePath] }), basePath), + history, + mountHistory: new ScopedHistory(history, basePath), startServices, config: createConfigurationMock(), startInterface: createStartMock(extensions), @@ -89,7 +90,7 @@ export const createIntegrationsTestRendererMock = (): TestRenderer => { const extensions: UIExtensionsStorage = {}; const startServices = createStartServices(basePath); const testRendererMocks: TestRenderer = { - history: createHashHistory(), + history: createMemoryHistory(), mountHistory: new ScopedHistory(createMemoryHistory({ initialEntries: [basePath] }), basePath), startServices, config: createConfigurationMock(), diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index 0606334737a2a..2c723a3269737 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -104,6 +104,7 @@ export class FleetPlugin implements Plugin { const [coreStartServices, startDepsServices] = (await core.getStartServices()) as [ CoreStart, diff --git a/x-pack/plugins/fleet/public/search_provider.test.ts b/x-pack/plugins/fleet/public/search_provider.test.ts index 521337c9dda6b..8eee18710d477 100644 --- a/x-pack/plugins/fleet/public/search_provider.test.ts +++ b/x-pack/plugins/fleet/public/search_provider.test.ts @@ -92,7 +92,7 @@ describe('Package search provider', () => { title: 'test', type: 'integration', url: { - path: 'undefined#/detail/test-test/overview', + path: 'undefined/detail/test-test/overview', prependBasePath: false, }, }, @@ -102,7 +102,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, @@ -175,7 +175,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, @@ -231,7 +231,7 @@ describe('Package search provider', () => { title: 'test', type: 'integration', url: { - path: 'undefined#/detail/test-test/overview', + path: 'undefined/detail/test-test/overview', prependBasePath: false, }, }, @@ -241,7 +241,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, @@ -274,7 +274,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, diff --git a/x-pack/plugins/fleet/public/search_provider.ts b/x-pack/plugins/fleet/public/search_provider.ts index 9705f4da50f94..5f53c0a8e44ba 100644 --- a/x-pack/plugins/fleet/public/search_provider.ts +++ b/x-pack/plugins/fleet/public/search_provider.ts @@ -45,10 +45,8 @@ const toSearchResult = ( title: pkg.title, score: 80, url: { - // TODO: See https://github.com/elastic/kibana/issues/96134 for details about why we use '#' here. Below should be updated - // as part of migrating to non-hash based router. // prettier-ignore - path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}#${pagePathGetters.integration_details_overview({ pkgkey })[1]}`, + path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}${pagePathGetters.integration_details_overview({ pkgkey })[1]}`, prependBasePath: false, }, }; diff --git a/x-pack/plugins/graph/public/angular/templates/index.html b/x-pack/plugins/graph/public/angular/templates/index.html index 10bbb2e8ec6c7..14c37cab9d9fd 100644 --- a/x-pack/plugins/graph/public/angular/templates/index.html +++ b/x-pack/plugins/graph/public/angular/templates/index.html @@ -3,51 +3,14 @@ -
-
-
- -
- http://host:port/{{ selectedIndex.name }}/_graph/explore - - -
-
-
-
-
+ + + +
{ @@ -119,7 +120,7 @@ const mainTemplate = (basePath: string) => `
const moduleName = 'app/graph'; -const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'ui.bootstrap', 'ui.ace']; +const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'ui.bootstrap']; function mountGraphApp(appBasePath: string, element: HTMLElement) { const mountpoint = document.createElement('div'); diff --git a/x-pack/plugins/graph/public/components/inspect_panel/inspect_panel.tsx b/x-pack/plugins/graph/public/components/inspect_panel/inspect_panel.tsx new file mode 100644 index 0000000000000..2f29849bebcec --- /dev/null +++ b/x-pack/plugins/graph/public/components/inspect_panel/inspect_panel.tsx @@ -0,0 +1,109 @@ +/* + * 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, { useMemo, useState } from 'react'; +import { EuiTab, EuiTabs, EuiText } from '@elastic/eui'; +import { monaco, XJsonLang } from '@kbn/monaco'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { IUiSettingsClient } from 'kibana/public'; +import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { + CodeEditor, + KibanaContextProvider, +} from '../../../../../../src/plugins/kibana_react/public'; + +interface InspectPanelProps { + showInspect?: boolean; + indexPattern?: IndexPattern; + uiSettings: IUiSettingsClient; + lastRequest?: string; + lastResponse?: string; +} + +const CODE_EDITOR_OPTIONS: monaco.editor.IStandaloneEditorConstructionOptions = { + automaticLayout: true, + fontSize: 12, + lineNumbers: 'on', + minimap: { + enabled: false, + }, + overviewRulerBorder: false, + readOnly: true, + scrollbar: { + alwaysConsumeMouseWheel: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', +}; + +const dummyCallback = () => {}; + +export const InspectPanel = ({ + showInspect, + lastRequest, + lastResponse, + indexPattern, + uiSettings, +}: InspectPanelProps) => { + const [selectedTabId, setSelectedTabId] = useState('request'); + + const onRequestClick = () => setSelectedTabId('request'); + const onResponseClick = () => setSelectedTabId('response'); + + const services = useMemo(() => ({ uiSettings }), [uiSettings]); + + const editorContent = useMemo(() => (selectedTabId === 'request' ? lastRequest : lastResponse), [ + selectedTabId, + lastRequest, + lastResponse, + ]); + + if (showInspect) { + return ( + +
+
+
+ +
+ +
+ + http://host:port/{indexPattern?.id}/_graph/explore + + + + + + + + + + +
+
+
+
+ ); + } + + return null; +}; diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index ec19e639b91c9..70671260ce5b9 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -110,6 +110,7 @@ export class GraphPlugin indexPatterns: pluginsStart.data!.indexPatterns, overlays: coreStart.overlays, savedObjects: pluginsStart.savedObjects, + uiSettings: core.uiSettings, }); }, }); diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index a1bdbdfd14c12..fc48bd8009cf2 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -67,8 +67,8 @@ type LogThresholdAlertInstance = AlertInstance< type LogThresholdAlertInstanceFactory = ( id: string, reason: string, - threshold: number, - value: number + value: number, + threshold: number ) => LogThresholdAlertInstance; const COMPOSITE_GROUP_SIZE = 2000; @@ -96,7 +96,7 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => >(async ({ services, params }) => { const { alertWithLifecycle, savedObjectsClient, scopedClusterClient } = services; const { sources } = libs; - const alertInstanceFactory: LogThresholdAlertInstanceFactory = (id, reason, threshold, value) => + const alertInstanceFactory: LogThresholdAlertInstanceFactory = (id, reason, value, threshold) => alertWithLifecycle({ id, fields: { @@ -251,8 +251,8 @@ export const processUngroupedResults = ( const alertInstance = alertInstanceFactory( UNGROUPED_FACTORY_KEY, getReasonMessageForUngroupedCountAlert(documentCount, count.value, count.comparator), - count.value, - documentCount + documentCount, + count.value ); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { @@ -285,8 +285,8 @@ export const processUngroupedRatioResults = ( const alertInstance = alertInstanceFactory( UNGROUPED_FACTORY_KEY, getReasonMessageForUngroupedRatioAlert(ratio, count.value, count.comparator), - count.value, - ratio + ratio, + count.value ); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { @@ -364,8 +364,8 @@ export const processGroupByResults = ( count.comparator, group.name ), - count.value, - documentCount + documentCount, + count.value ); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { @@ -415,8 +415,8 @@ export const processGroupByRatioResults = ( count.comparator, numeratorGroup.name ), - count.value, - ratio + ratio, + count.value ); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/counter_rate.test.ts b/x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts similarity index 99% rename from x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/counter_rate.test.ts rename to x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts index 4b909489f3236..3e1a5ad8e3964 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/counter_rate.test.ts +++ b/x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { counterRate, CounterRateArgs } from '../counter_rate'; +import { counterRate, CounterRateArgs } from './index'; import { Datatable } from 'src/plugins/expressions/public'; import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/index.ts b/x-pack/plugins/lens/common/expressions/counter_rate/index.ts similarity index 97% rename from x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/index.ts rename to x-pack/plugins/lens/common/expressions/counter_rate/index.ts index de59597ab6ba3..41f5547dff969 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/index.ts +++ b/x-pack/plugins/lens/common/expressions/counter_rate/index.ts @@ -6,11 +6,14 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, Datatable } from 'src/plugins/expressions/public'; import { getBucketIdentifier, buildResultColumns, } from '../../../../../../src/plugins/expressions/common'; +import type { + ExpressionFunctionDefinition, + Datatable, +} from '../../../../../../src/plugins/expressions/common'; export interface CounterRateArgs { by?: string[]; diff --git a/x-pack/plugins/lens/common/expressions/datatable/datatable.ts b/x-pack/plugins/lens/common/expressions/datatable/datatable.ts new file mode 100644 index 0000000000000..d2db63a01793e --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/datatable/datatable.ts @@ -0,0 +1,158 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { cloneDeep } from 'lodash'; +import type { + DatatableColumnMeta, + ExpressionFunctionDefinition, +} from '../../../../../../src/plugins/expressions/common'; +import type { FormatFactory, LensMultiTable } from '../../types'; +import type { ColumnConfigArg } from './datatable_column'; +import { getSortingCriteria } from './sorting'; +import { computeSummaryRowForColumn } from './summary'; +import { transposeTable } from './transpose_helpers'; + +export interface SortingState { + columnId: string | undefined; + direction: 'asc' | 'desc' | 'none'; +} + +export interface DatatableProps { + data: LensMultiTable; + untransposedData?: LensMultiTable; + args: DatatableArgs; +} + +export interface DatatableRender { + type: 'render'; + as: 'lens_datatable_renderer'; + value: DatatableProps; +} + +export interface DatatableArgs { + title: string; + description?: string; + columns: ColumnConfigArg[]; + sortingColumnId: SortingState['columnId']; + sortingDirection: SortingState['direction']; +} + +function isRange(meta: { params?: { id?: string } } | undefined) { + return meta?.params?.id === 'range'; +} + +export const getDatatable = ({ + formatFactory, +}: { + formatFactory: FormatFactory; +}): ExpressionFunctionDefinition< + 'lens_datatable', + LensMultiTable, + DatatableArgs, + DatatableRender +> => ({ + name: 'lens_datatable', + type: 'render', + inputTypes: ['lens_multitable'], + help: i18n.translate('xpack.lens.datatable.expressionHelpLabel', { + defaultMessage: 'Datatable renderer', + }), + args: { + title: { + types: ['string'], + help: i18n.translate('xpack.lens.datatable.titleLabel', { + defaultMessage: 'Title', + }), + }, + description: { + types: ['string'], + help: '', + }, + columns: { + types: ['lens_datatable_column'], + help: '', + multi: true, + }, + sortingColumnId: { + types: ['string'], + help: '', + }, + sortingDirection: { + types: ['string'], + help: '', + }, + }, + fn(data, args, context) { + let untransposedData: LensMultiTable | undefined; + // do the sorting at this level to propagate it also at CSV download + const [firstTable] = Object.values(data.tables); + const [layerId] = Object.keys(context.inspectorAdapters.tables || {}); + const formatters: Record> = {}; + + firstTable.columns.forEach((column) => { + formatters[column.id] = formatFactory(column.meta?.params); + }); + + const hasTransposedColumns = args.columns.some((c) => c.isTransposed); + if (hasTransposedColumns) { + // store original shape of data separately + untransposedData = cloneDeep(data); + // transposes table and args inplace + transposeTable(args, firstTable, formatters); + } + + const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args; + + const columnsReverseLookup = firstTable.columns.reduce< + Record + >((memo, { id, name, meta }, i) => { + memo[id] = { name, index: i, meta }; + return memo; + }, {}); + + const columnsWithSummary = args.columns.filter((c) => c.summaryRow); + for (const column of columnsWithSummary) { + column.summaryRowValue = computeSummaryRowForColumn( + column, + firstTable, + formatters, + formatFactory({ id: 'number' }) + ); + } + + if (sortBy && columnsReverseLookup[sortBy] && sortDirection !== 'none') { + // Sort on raw values for these types, while use the formatted value for the rest + const sortingCriteria = getSortingCriteria( + isRange(columnsReverseLookup[sortBy]?.meta) + ? 'range' + : columnsReverseLookup[sortBy]?.meta?.type, + sortBy, + formatters[sortBy], + sortDirection + ); + // replace the table here + context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || []) + .slice() + .sort(sortingCriteria); + // replace also the local copy + firstTable.rows = context.inspectorAdapters.tables[layerId].rows; + } else { + args.sortingColumnId = undefined; + args.sortingDirection = 'none'; + } + return { + type: 'render', + as: 'lens_datatable_renderer', + value: { + data, + untransposedData, + args, + }, + }; + }, +}); diff --git a/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts b/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts new file mode 100644 index 0000000000000..892631c3b0f45 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts @@ -0,0 +1,85 @@ +/* + * 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 type { Direction } from '@elastic/eui'; +import type { + CustomPaletteState, + PaletteOutput, +} from '../../../../../../src/plugins/charts/common'; +import type { + ExpressionFunctionDefinition, + DatatableColumn, +} from '../../../../../../src/plugins/expressions/common'; +import type { CustomPaletteParams } from '../../types'; + +export type LensGridDirection = 'none' | Direction; + +export interface ColumnConfig { + columns: ColumnConfigArg[]; + sortingColumnId: string | undefined; + sortingDirection: LensGridDirection; +} + +export type ColumnConfigArg = Omit & { + type: 'lens_datatable_column'; + palette?: PaletteOutput; + summaryRowValue?: unknown; +}; + +export interface ColumnState { + columnId: string; + width?: number; + hidden?: boolean; + isTransposed?: boolean; + // These flags are necessary to transpose columns and map them back later + // They are set automatically and are not user-editable + transposable?: boolean; + originalColumnId?: string; + originalName?: string; + bucketValues?: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>; + alignment?: 'left' | 'right' | 'center'; + palette?: PaletteOutput; + colorMode?: 'none' | 'cell' | 'text'; + summaryRow?: 'none' | 'sum' | 'avg' | 'count' | 'min' | 'max'; + summaryLabel?: string; +} + +export type DatatableColumnResult = ColumnState & { type: 'lens_datatable_column' }; + +export const datatableColumn: ExpressionFunctionDefinition< + 'lens_datatable_column', + null, + ColumnState, + DatatableColumnResult +> = { + name: 'lens_datatable_column', + aliases: [], + type: 'lens_datatable_column', + help: '', + inputTypes: ['null'], + args: { + columnId: { types: ['string'], help: '' }, + alignment: { types: ['string'], help: '' }, + hidden: { types: ['boolean'], help: '' }, + width: { types: ['number'], help: '' }, + isTransposed: { types: ['boolean'], help: '' }, + transposable: { types: ['boolean'], help: '' }, + colorMode: { types: ['string'], help: '' }, + palette: { + types: ['palette'], + help: '', + }, + summaryRow: { types: ['string'], help: '' }, + summaryLabel: { types: ['string'], help: '' }, + }, + fn: function fn(input: unknown, args: ColumnState) { + return { + type: 'lens_datatable_column', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/datatable/index.ts b/x-pack/plugins/lens/common/expressions/datatable/index.ts new file mode 100644 index 0000000000000..2602aae252ca9 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/datatable/index.ts @@ -0,0 +1,12 @@ +/* + * 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 * from './datatable_column'; +export * from './datatable'; +export * from './summary'; +export * from './transpose_helpers'; +export * from './utils'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/sorting.test.tsx b/x-pack/plugins/lens/common/expressions/datatable/sorting.test.tsx similarity index 100% rename from x-pack/plugins/lens/public/datatable_visualization/sorting.test.tsx rename to x-pack/plugins/lens/common/expressions/datatable/sorting.test.tsx diff --git a/x-pack/plugins/lens/public/datatable_visualization/sorting.tsx b/x-pack/plugins/lens/common/expressions/datatable/sorting.tsx similarity index 98% rename from x-pack/plugins/lens/public/datatable_visualization/sorting.tsx rename to x-pack/plugins/lens/common/expressions/datatable/sorting.tsx index 0859ab5428c9e..13ca811b0b082 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/sorting.tsx +++ b/x-pack/plugins/lens/common/expressions/datatable/sorting.tsx @@ -7,7 +7,7 @@ import ipaddr from 'ipaddr.js'; import type { IPv4, IPv6 } from 'ipaddr.js'; -import { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from '../../../../../../src/plugins/data/common'; function isIPv6Address(ip: IPv4 | IPv6): ip is IPv6 { return ip.kind() === 'ipv6'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/summary.test.ts b/x-pack/plugins/lens/common/expressions/datatable/summary.test.ts similarity index 98% rename from x-pack/plugins/lens/public/datatable_visualization/summary.test.ts rename to x-pack/plugins/lens/common/expressions/datatable/summary.test.ts index f92c83fbbfdc8..9f8f56cc92768 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/summary.test.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/summary.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { IFieldFormat } from 'src/plugins/data/public'; +import { IFieldFormat } from 'src/plugins/data/common'; import { Datatable } from 'src/plugins/expressions'; import { computeSummaryRowForColumn, getFinalSummaryConfiguration } from './summary'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/summary.ts b/x-pack/plugins/lens/common/expressions/datatable/summary.ts similarity index 91% rename from x-pack/plugins/lens/public/datatable_visualization/summary.ts rename to x-pack/plugins/lens/common/expressions/datatable/summary.ts index 6c267445aab76..aceade2a3a513 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/summary.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/summary.ts @@ -6,11 +6,11 @@ */ import { i18n } from '@kbn/i18n'; -import { FieldFormat } from 'src/plugins/data/public'; -import { Datatable } from 'src/plugins/expressions/public'; -import { ColumnConfigArg } from './datatable_visualization'; +import type { FieldFormat } from '../../../../../../src/plugins/data/common'; +import type { Datatable } from '../../../../../../src/plugins/expressions/common'; +import { ColumnConfigArg } from './datatable_column'; import { getOriginalId } from './transpose_helpers'; -import { isNumericField } from './utils'; +import { isNumericFieldForDatatable } from './utils'; type SummaryRowType = Extract; @@ -19,7 +19,7 @@ export function getFinalSummaryConfiguration( columnArgs: Pick | undefined, table: Datatable | undefined ) { - const isNumeric = isNumericField(table, columnId); + const isNumeric = isNumericFieldForDatatable(table, columnId); const summaryRow = isNumeric ? columnArgs?.summaryRow || 'none' : 'none'; const summaryLabel = columnArgs?.summaryLabel ?? getDefaultSummaryLabel(summaryRow); diff --git a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts similarity index 99% rename from x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts rename to x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts index 91559a1778f4f..7ac6b3d987c84 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts @@ -7,8 +7,8 @@ import type { FieldFormat } from 'src/plugins/data/public'; import type { Datatable } from 'src/plugins/expressions'; +import { DatatableArgs } from './datatable'; -import { Args } from './expression'; import { transposeTable } from './transpose_helpers'; describe('transpose_helpes', () => { @@ -59,7 +59,7 @@ describe('transpose_helpes', () => { }; } - function buildArgs(): Args { + function buildArgs(): DatatableArgs { return { title: 'Table', sortingColumnId: undefined, diff --git a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts similarity index 95% rename from x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts rename to x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts index a35edf7499073..06798413c8f40 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts @@ -5,11 +5,14 @@ * 2.0. */ -import type { FieldFormat } from 'src/plugins/data/public'; -import type { Datatable, DatatableColumn, DatatableRow } from 'src/plugins/expressions'; -import { ColumnConfig } from './components/table_basic'; - -import { Args, ColumnConfigArg } from './expression'; +import type { + Datatable, + DatatableColumn, + DatatableRow, +} from '../../../../../../src/plugins/expressions'; +import type { FieldFormat } from '../../../../../../src/plugins/data/common'; +import type { DatatableArgs } from './datatable'; +import type { ColumnConfig, ColumnConfigArg } from './datatable_column'; const TRANSPOSE_SEPARATOR = '---'; @@ -42,7 +45,7 @@ export function getOriginalId(id: string) { * @param formatters Formatters for all columns to transpose columns by actual display values */ export function transposeTable( - args: Args, + args: DatatableArgs, firstTable: Datatable, formatters: Record ) { @@ -112,7 +115,7 @@ function transposeRows( * grouped by unique value */ function updateColumnArgs( - args: Args, + args: DatatableArgs, bucketsColumnArgs: ColumnConfig['columns'], transposedColumnGroups: Array ) { @@ -150,7 +153,7 @@ function getUniqueValues(table: Datatable, formatter: FieldFormat, columnId: str * @param uniqueValues */ function transposeColumns( - args: Args, + args: DatatableArgs, bucketsColumnArgs: ColumnConfig['columns'], metricColumns: ColumnConfig['columns'], firstTable: Datatable, diff --git a/x-pack/plugins/lens/public/datatable_visualization/utils.ts b/x-pack/plugins/lens/common/expressions/datatable/utils.ts similarity index 80% rename from x-pack/plugins/lens/public/datatable_visualization/utils.ts rename to x-pack/plugins/lens/common/expressions/datatable/utils.ts index 64fdee233e830..486ec7102e1eb 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/utils.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/utils.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { Datatable } from 'src/plugins/expressions/public'; +import type { Datatable } from '../../../../../../src/plugins/expressions/common'; import { getOriginalId } from './transpose_helpers'; function isValidNumber(value: unknown): boolean { return typeof value === 'number' || value == null; } -export function isNumericField(currentData: Datatable | undefined, accessor: string) { +export function isNumericFieldForDatatable(currentData: Datatable | undefined, accessor: string) { const isNumeric = currentData?.columns.find((col) => col.id === accessor || getOriginalId(col.id) === accessor) ?.meta.type === 'number'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.test.ts b/x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts similarity index 98% rename from x-pack/plugins/lens/public/indexpattern_datasource/format_column.test.ts rename to x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts index 38643f2dfbda7..4428225b349da 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.test.ts +++ b/x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts @@ -7,7 +7,7 @@ import { Datatable, DatatableColumn } from 'src/plugins/expressions/public'; import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils'; -import { FormatColumnArgs, formatColumn } from './format_column'; +import { FormatColumnArgs, formatColumn } from './index'; describe('format_column', () => { const fn: (input: Datatable, args: FormatColumnArgs) => Datatable = functionWrapper(formatColumn); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts b/x-pack/plugins/lens/common/expressions/format_column/index.ts similarity index 98% rename from x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts rename to x-pack/plugins/lens/common/expressions/format_column/index.ts index 09a4e607a1586..c874eac1ede1f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts +++ b/x-pack/plugins/lens/common/expressions/format_column/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { +import type { ExpressionFunctionDefinition, Datatable, DatatableColumn, -} from 'src/plugins/expressions/public'; +} from '../../../../../../src/plugins/expressions/common'; export interface FormatColumnArgs { format: string; diff --git a/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_chart.ts b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_chart.ts new file mode 100644 index 0000000000000..4674879dcac9f --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_chart.ts @@ -0,0 +1,122 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { PaletteOutput } from '../../../../../../src/plugins/charts/common'; +import type { LensMultiTable, CustomPaletteParams } from '../../types'; +import { HeatmapGridConfigResult, HEATMAP_GRID_FUNCTION } from './heatmap_grid'; +import { HeatmapLegendConfigResult, HEATMAP_LEGEND_FUNCTION } from './heatmap_legend'; + +export const HEATMAP_FUNCTION = 'lens_heatmap'; +export const HEATMAP_FUNCTION_RENDERER = 'lens_heatmap_renderer'; + +export type ChartShapes = 'heatmap'; + +export interface SharedHeatmapLayerState { + shape: ChartShapes; + xAccessor?: string; + yAccessor?: string; + valueAccessor?: string; + legend: HeatmapLegendConfigResult; + gridConfig: HeatmapGridConfigResult; +} + +export type HeatmapLayerState = SharedHeatmapLayerState & { + layerId: string; +}; + +export type HeatmapVisualizationState = HeatmapLayerState & { + // need to store the current accessor to reset the color stops at accessor change + palette?: PaletteOutput & { accessor: string }; +}; + +export type HeatmapExpressionArgs = SharedHeatmapLayerState & { + title?: string; + description?: string; + palette: PaletteOutput; +}; + +export interface HeatmapRender { + type: 'render'; + as: typeof HEATMAP_FUNCTION_RENDERER; + value: HeatmapExpressionProps; +} + +export interface HeatmapExpressionProps { + data: LensMultiTable; + args: HeatmapExpressionArgs; +} + +export const heatmap: ExpressionFunctionDefinition< + typeof HEATMAP_FUNCTION, + LensMultiTable, + HeatmapExpressionArgs, + HeatmapRender +> = { + name: HEATMAP_FUNCTION, + type: 'render', + help: i18n.translate('xpack.lens.heatmap.expressionHelpLabel', { + defaultMessage: 'Heatmap renderer', + }), + args: { + title: { + types: ['string'], + help: i18n.translate('xpack.lens.heatmap.titleLabel', { + defaultMessage: 'Title', + }), + }, + description: { + types: ['string'], + help: '', + }, + xAccessor: { + types: ['string'], + help: '', + }, + yAccessor: { + types: ['string'], + help: '', + }, + valueAccessor: { + types: ['string'], + help: '', + }, + shape: { + types: ['string'], + help: '', + }, + palette: { + default: `{theme "palette" default={system_palette name="default"} }`, + help: '', + types: ['palette'], + }, + legend: { + types: [HEATMAP_LEGEND_FUNCTION], + help: i18n.translate('xpack.lens.heatmapChart.legend.help', { + defaultMessage: 'Configure the chart legend.', + }), + }, + gridConfig: { + types: [HEATMAP_GRID_FUNCTION], + help: i18n.translate('xpack.lens.heatmapChart.gridConfig.help', { + defaultMessage: 'Configure the heatmap layout.', + }), + }, + }, + inputTypes: ['lens_multitable'], + fn(data: LensMultiTable, args: HeatmapExpressionArgs) { + return { + type: 'render', + as: HEATMAP_FUNCTION_RENDERER, + value: { + data, + args, + }, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_grid.ts b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_grid.ts new file mode 100644 index 0000000000000..5fe7f4b8f6c62 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_grid.ts @@ -0,0 +1,114 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; + +export const HEATMAP_GRID_FUNCTION = 'lens_heatmap_grid'; + +export interface HeatmapGridConfig { + // grid + strokeWidth?: number; + strokeColor?: string; + cellHeight?: number; + cellWidth?: number; + // cells + isCellLabelVisible: boolean; + // Y-axis + isYAxisLabelVisible: boolean; + yAxisLabelWidth?: number; + yAxisLabelColor?: string; + // X-axis + isXAxisLabelVisible: boolean; +} + +export type HeatmapGridConfigResult = HeatmapGridConfig & { type: typeof HEATMAP_GRID_FUNCTION }; + +export const heatmapGridConfig: ExpressionFunctionDefinition< + typeof HEATMAP_GRID_FUNCTION, + null, + HeatmapGridConfig, + HeatmapGridConfigResult +> = { + name: HEATMAP_GRID_FUNCTION, + aliases: [], + type: HEATMAP_GRID_FUNCTION, + help: `Configure the heatmap layout `, + inputTypes: ['null'], + args: { + // grid + strokeWidth: { + types: ['number'], + help: i18n.translate('xpack.lens.heatmapChart.config.strokeWidth.help', { + defaultMessage: 'Specifies the grid stroke width', + }), + required: false, + }, + strokeColor: { + types: ['string'], + help: i18n.translate('xpack.lens.heatmapChart.config.strokeColor.help', { + defaultMessage: 'Specifies the grid stroke color', + }), + required: false, + }, + cellHeight: { + types: ['number'], + help: i18n.translate('xpack.lens.heatmapChart.config.cellHeight.help', { + defaultMessage: 'Specifies the grid cell height', + }), + required: false, + }, + cellWidth: { + types: ['number'], + help: i18n.translate('xpack.lens.heatmapChart.config.cellWidth.help', { + defaultMessage: 'Specifies the grid cell width', + }), + required: false, + }, + // cells + isCellLabelVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.heatmapChart.config.isCellLabelVisible.help', { + defaultMessage: 'Specifies whether or not the cell label is visible.', + }), + }, + // Y-axis + isYAxisLabelVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.heatmapChart.config.isYAxisLabelVisible.help', { + defaultMessage: 'Specifies whether or not the Y-axis labels are visible.', + }), + }, + yAxisLabelWidth: { + types: ['number'], + help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelWidth.help', { + defaultMessage: 'Specifies the width of the Y-axis labels.', + }), + required: false, + }, + yAxisLabelColor: { + types: ['string'], + help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelColor.help', { + defaultMessage: 'Specifies the color of the Y-axis labels.', + }), + required: false, + }, + // X-axis + isXAxisLabelVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.heatmapChart.config.isXAxisLabelVisible.help', { + defaultMessage: 'Specifies whether or not the X-axis labels are visible.', + }), + }, + }, + fn(input, args) { + return { + type: HEATMAP_GRID_FUNCTION, + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_legend.ts b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_legend.ts new file mode 100644 index 0000000000000..0f553c6cae1f0 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_legend.ts @@ -0,0 +1,64 @@ +/* + * 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 { Position } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; + +export const HEATMAP_LEGEND_FUNCTION = 'lens_heatmap_legendConfig'; + +export interface HeatmapLegendConfig { + /** + * Flag whether the legend should be shown. If there is just a single series, it will be hidden + */ + isVisible: boolean; + /** + * Position of the legend relative to the chart + */ + position: Position; +} + +export type HeatmapLegendConfigResult = HeatmapLegendConfig & { + type: typeof HEATMAP_LEGEND_FUNCTION; +}; + +/** + * TODO check if it's possible to make a shared function + * based on the XY chart + */ +export const heatmapLegendConfig: ExpressionFunctionDefinition< + typeof HEATMAP_LEGEND_FUNCTION, + null, + HeatmapLegendConfig, + HeatmapLegendConfigResult +> = { + name: HEATMAP_LEGEND_FUNCTION, + aliases: [], + type: HEATMAP_LEGEND_FUNCTION, + help: `Configure the heatmap chart's legend`, + inputTypes: ['null'], + args: { + isVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.heatmapChart.legend.isVisible.help', { + defaultMessage: 'Specifies whether or not the legend is visible.', + }), + }, + position: { + types: ['string'], + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + help: i18n.translate('xpack.lens.heatmapChart.legend.position.help', { + defaultMessage: 'Specifies the legend position.', + }), + }, + }, + fn(input, args) { + return { + type: HEATMAP_LEGEND_FUNCTION, + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/heatmap_chart/index.ts b/x-pack/plugins/lens/common/expressions/heatmap_chart/index.ts new file mode 100644 index 0000000000000..96f8c074b1fc4 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/heatmap_chart/index.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 * from './heatmap_grid'; +export * from './heatmap_legend'; +export * from './heatmap_chart'; diff --git a/x-pack/plugins/lens/common/expressions/index.ts b/x-pack/plugins/lens/common/expressions/index.ts new file mode 100644 index 0000000000000..70a85f85938e4 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './counter_rate'; +export * from './format_column'; +export * from './rename_columns'; +export * from './merge_tables'; +export * from './time_scale'; +export * from './datatable'; +export * from './heatmap_chart'; +export * from './metric_chart'; +export * from './pie_chart'; +export * from './xy_chart'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts b/x-pack/plugins/lens/common/expressions/merge_tables/index.ts similarity index 84% rename from x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts rename to x-pack/plugins/lens/common/expressions/merge_tables/index.ts index cd93392fc712d..e190da19886df 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts +++ b/x-pack/plugins/lens/common/expressions/merge_tables/index.ts @@ -6,16 +6,16 @@ */ import { i18n } from '@kbn/i18n'; -import { - ExecutionContext, - Datatable, +import type { ExpressionFunctionDefinition, -} from 'src/plugins/expressions/public'; -import { ExpressionValueSearchContext, search } from '../../../../../src/plugins/data/public'; -const { toAbsoluteDates } = search.aggs; + Datatable, + ExecutionContext, +} from '../../../../../../src/plugins/expressions/common'; +import { toAbsoluteDates } from '../../../../../../src/plugins/data/common'; +import type { ExpressionValueSearchContext } from '../../../../../../src/plugins/data/common'; -import { LensMultiTable } from '../types'; -import { Adapters } from '../../../../../src/plugins/inspector/common'; +import type { LensMultiTable } from '../../types'; +import { Adapters } from '../../../../../../src/plugins/inspector/common'; interface MergeTables { layerIds: string[]; diff --git a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts b/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts similarity index 98% rename from x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts rename to x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts index eb381b33655e2..c883f6b7cb479 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts +++ b/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import { mergeTables } from './merge_tables'; +import { mergeTables } from './index'; import { ExpressionValueSearchContext } from 'src/plugins/data/public'; import { Datatable, diff --git a/x-pack/plugins/lens/common/expressions/metric_chart/index.ts b/x-pack/plugins/lens/common/expressions/metric_chart/index.ts new file mode 100644 index 0000000000000..40bd4f3886455 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/metric_chart/index.ts @@ -0,0 +1,9 @@ +/* + * 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 * from './types'; +export * from './metric_chart'; diff --git a/x-pack/plugins/lens/common/expressions/metric_chart/metric_chart.ts b/x-pack/plugins/lens/common/expressions/metric_chart/metric_chart.ts new file mode 100644 index 0000000000000..53ed7c8da32eb --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/metric_chart/metric_chart.ts @@ -0,0 +1,68 @@ +/* + * 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 type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { LensMultiTable } from '../../types'; +import type { MetricConfig } from './types'; + +export interface MetricChartProps { + data: LensMultiTable; + args: MetricConfig; +} + +export interface MetricRender { + type: 'render'; + as: 'lens_metric_chart_renderer'; + value: MetricChartProps; +} + +export const metricChart: ExpressionFunctionDefinition< + 'lens_metric_chart', + LensMultiTable, + Omit, + MetricRender +> = { + name: 'lens_metric_chart', + type: 'render', + help: 'A metric chart', + args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, + metricTitle: { + types: ['string'], + help: 'The title of the metric shown.', + }, + accessor: { + types: ['string'], + help: 'The column whose value is being displayed', + }, + mode: { + types: ['string'], + options: ['reduced', 'full'], + default: 'full', + help: + 'The display mode of the chart - reduced will only show the metric itself without min size', + }, + }, + inputTypes: ['lens_multitable'], + fn(data, args) { + return { + type: 'render', + as: 'lens_metric_chart_renderer', + value: { + data, + args, + }, + } as MetricRender; + }, +}; diff --git a/x-pack/plugins/lens/public/metric_visualization/types.ts b/x-pack/plugins/lens/common/expressions/metric_chart/types.ts similarity index 100% rename from x-pack/plugins/lens/public/metric_visualization/types.ts rename to x-pack/plugins/lens/common/expressions/metric_chart/types.ts diff --git a/x-pack/plugins/lens/common/expressions/pie_chart/index.ts b/x-pack/plugins/lens/common/expressions/pie_chart/index.ts new file mode 100644 index 0000000000000..e82294f8aff25 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/pie_chart/index.ts @@ -0,0 +1,9 @@ +/* + * 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 * from './types'; +export * from './pie_chart'; diff --git a/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts b/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts new file mode 100644 index 0000000000000..b298f1d8b3a80 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts @@ -0,0 +1,103 @@ +/* + * 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 { Position } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { LensMultiTable } from '../../types'; +import type { PieExpressionProps, PieExpressionArgs } from './types'; + +export interface PieRender { + type: 'render'; + as: 'lens_pie_renderer'; + value: PieExpressionProps; +} + +export const pie: ExpressionFunctionDefinition< + 'lens_pie', + LensMultiTable, + PieExpressionArgs, + PieRender +> = { + name: 'lens_pie', + type: 'render', + help: i18n.translate('xpack.lens.pie.expressionHelpLabel', { + defaultMessage: 'Pie renderer', + }), + args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, + groups: { + types: ['string'], + multi: true, + help: '', + }, + metric: { + types: ['string'], + help: '', + }, + shape: { + types: ['string'], + options: ['pie', 'donut', 'treemap'], + help: '', + }, + hideLabels: { + types: ['boolean'], + help: '', + }, + numberDisplay: { + types: ['string'], + options: ['hidden', 'percent', 'value'], + help: '', + }, + categoryDisplay: { + types: ['string'], + options: ['default', 'inside', 'hide'], + help: '', + }, + legendDisplay: { + types: ['string'], + options: ['default', 'show', 'hide'], + help: '', + }, + nestedLegend: { + types: ['boolean'], + help: '', + }, + legendPosition: { + types: ['string'], + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + help: '', + }, + percentDecimals: { + types: ['number'], + help: '', + }, + palette: { + default: `{theme "palette" default={system_palette name="default"} }`, + help: '', + types: ['palette'], + }, + }, + inputTypes: ['lens_multitable'], + fn(data: LensMultiTable, args: PieExpressionArgs) { + return { + type: 'render', + as: 'lens_pie_renderer', + value: { + data, + args, + }, + }; + }, +}; diff --git a/x-pack/plugins/lens/public/pie_visualization/types.ts b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts similarity index 89% rename from x-pack/plugins/lens/public/pie_visualization/types.ts rename to x-pack/plugins/lens/common/expressions/pie_chart/types.ts index c03ab15ecc290..e377272322950 100644 --- a/x-pack/plugins/lens/public/pie_visualization/types.ts +++ b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { PaletteOutput } from 'src/plugins/charts/public'; -import { LensMultiTable } from '../types'; +import type { PaletteOutput } from '../../../../../../src/plugins/charts/common'; +import type { LensMultiTable } from '../../types'; export interface SharedPieLayerState { groups: string[]; diff --git a/x-pack/plugins/lens/common/expressions/rename_columns/index.ts b/x-pack/plugins/lens/common/expressions/rename_columns/index.ts new file mode 100644 index 0000000000000..4cb8ff75f486d --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/rename_columns/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 * from './rename_columns'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts b/x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.test.ts similarity index 96% rename from x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts rename to x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.test.ts index 5654a599c5e27..f3db64c1d2257 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts +++ b/x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.test.ts @@ -6,8 +6,8 @@ */ import { renameColumns } from './rename_columns'; -import { Datatable } from '../../../../../src/plugins/expressions/public'; -import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; +import { Datatable } from '../../../../../../src/plugins/expressions/common'; +import { createMockExecutionContext } from '../../../../../../src/plugins/expressions/common/mocks'; describe('rename_columns', () => { it('should rename columns of a given datatable', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts b/x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.ts similarity index 86% rename from x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts rename to x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.ts index a16756126c030..517bd683d80ae 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts +++ b/x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.ts @@ -6,14 +6,20 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, Datatable, DatatableColumn } from 'src/plugins/expressions'; -import { IndexPatternColumn } from './operations'; +import { + ExpressionFunctionDefinition, + Datatable, + DatatableColumn, +} from '../../../../../../src/plugins/expressions/common'; interface RemapArgs { idMap: string; } -export type OriginalColumn = { id: string } & IndexPatternColumn; +type OriginalColumn = { id: string; label: string } & ( + | { operationType: 'date_histogram'; sourceField: string } + | { operationType: string; sourceField: never } +); export const renameColumns: ExpressionFunctionDefinition< 'lens_rename_columns', @@ -75,7 +81,7 @@ export const renameColumns: ExpressionFunctionDefinition< }; function getColumnName(originalColumn: OriginalColumn, newColumn: DatatableColumn) { - if (originalColumn && originalColumn.operationType === 'date_histogram') { + if (originalColumn?.operationType === 'date_histogram') { const fieldName = originalColumn.sourceField; // HACK: This is a hack, and introduces some fragility into diff --git a/x-pack/plugins/lens/common/expressions/time_scale/index.ts b/x-pack/plugins/lens/common/expressions/time_scale/index.ts new file mode 100644 index 0000000000000..92fec01a9ecbc --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/time_scale/index.ts @@ -0,0 +1,9 @@ +/* + * 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 * from './time_scale'; +export * from './types'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.test.ts b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts similarity index 89% rename from x-pack/plugins/lens/public/indexpattern_datasource/time_scale.test.ts rename to x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts index 34579927cfe19..c0a5c4bf1e1ec 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.test.ts +++ b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts @@ -7,14 +7,25 @@ import moment from 'moment'; import { Datatable } from 'src/plugins/expressions/public'; -import { DataPublicPluginStart, TimeRange } from 'src/plugins/data/public'; -import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; +import { TimeRange } from 'src/plugins/data/public'; import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils'; -import { getTimeScaleFunction, TimeScaleArgs } from './time_scale'; + +// mock the specific inner variable: +// there are intra dependencies in the data plugin we might break trying to mock the whole thing +jest.mock('../../../../../../src/plugins/data/common/query/timefilter/get_time', () => { + const localMoment = jest.requireActual('moment'); + return { + calculateBounds: jest.fn(({ from, to }) => ({ + min: localMoment(from), + max: localMoment(to), + })), + }; +}); + +import { timeScale, TimeScaleArgs } from './time_scale'; describe('time_scale', () => { - let timeScale: (input: Datatable, args: TimeScaleArgs) => Promise; - let dataMock: jest.Mocked; + let timeScaleWrapped: (input: Datatable, args: TimeScaleArgs) => Promise; const emptyTable: Datatable = { type: 'datatable', @@ -61,7 +72,6 @@ describe('time_scale', () => { } beforeEach(() => { - dataMock = dataPluginMock.createStartContract(); setDateHistogramMeta({ timeZone: 'UTC', timeRange: { @@ -70,17 +80,11 @@ describe('time_scale', () => { }, interval: '1d', }); - (dataMock.query.timefilter.timefilter.calculateBounds as jest.Mock).mockImplementation( - ({ from, to }) => ({ - min: moment(from), - max: moment(to), - }) - ); - timeScale = functionWrapper(getTimeScaleFunction(dataMock)); + timeScaleWrapped = functionWrapper(timeScale); }); it('should apply time scale factor to each row', async () => { - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -115,7 +119,7 @@ describe('time_scale', () => { }); it('should skip gaps in the data', async () => { - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -163,7 +167,7 @@ describe('time_scale', () => { }, ], }; - const result = await timeScale(mismatchedTable, { + const result = await timeScaleWrapped(mismatchedTable, { ...defaultArgs, inputColumnId: 'nonexistent', }); @@ -180,7 +184,7 @@ describe('time_scale', () => { }, interval: '1h', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -220,7 +224,7 @@ describe('time_scale', () => { }, interval: '3h', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -262,7 +266,7 @@ describe('time_scale', () => { }, interval: '1d', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -307,7 +311,7 @@ describe('time_scale', () => { }, interval: '1d', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -347,7 +351,7 @@ describe('time_scale', () => { }, interval: '1y', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ diff --git a/x-pack/plugins/lens/common/expressions/time_scale/time_scale.ts b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.ts new file mode 100644 index 0000000000000..fc2023ca4d599 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.ts @@ -0,0 +1,151 @@ +/* + * 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 moment from 'moment-timezone'; +import { i18n } from '@kbn/i18n'; +import type { + ExpressionFunctionDefinition, + Datatable, +} from '../../../../../../src/plugins/expressions/common'; +import { + getDateHistogramMetaDataByDatatableColumn, + parseInterval, + calculateBounds, +} from '../../../../../../src/plugins/data/common'; +import { buildResultColumns } from '../../../../../../src/plugins/expressions/common'; +import type { TimeScaleUnit } from './types'; + +export interface TimeScaleArgs { + dateColumnId: string; + inputColumnId: string; + outputColumnId: string; + targetUnit: TimeScaleUnit; + outputColumnName?: string; +} + +const unitInMs: Record = { + s: 1000, + m: 1000 * 60, + h: 1000 * 60 * 60, + d: 1000 * 60 * 60 * 24, +}; + +export const timeScale: ExpressionFunctionDefinition< + 'lens_time_scale', + Datatable, + TimeScaleArgs, + Promise +> = { + name: 'lens_time_scale', + type: 'datatable', + help: '', + args: { + dateColumnId: { + types: ['string'], + help: '', + required: true, + }, + inputColumnId: { + types: ['string'], + help: '', + required: true, + }, + outputColumnId: { + types: ['string'], + help: '', + required: true, + }, + outputColumnName: { + types: ['string'], + help: '', + }, + targetUnit: { + types: ['string'], + options: ['s', 'm', 'h', 'd'], + help: '', + required: true, + }, + }, + inputTypes: ['datatable'], + async fn( + input, + { dateColumnId, inputColumnId, outputColumnId, outputColumnName, targetUnit }: TimeScaleArgs + ) { + const dateColumnDefinition = input.columns.find((column) => column.id === dateColumnId); + + if (!dateColumnDefinition) { + throw new Error( + i18n.translate('xpack.lens.functions.timeScale.dateColumnMissingMessage', { + defaultMessage: 'Specified dateColumnId {columnId} does not exist.', + values: { + columnId: dateColumnId, + }, + }) + ); + } + + const resultColumns = buildResultColumns( + input, + outputColumnId, + inputColumnId, + outputColumnName, + { allowColumnOverwrite: true } + ); + + if (!resultColumns) { + return input; + } + + const targetUnitInMs = unitInMs[targetUnit]; + const timeInfo = getDateHistogramMetaDataByDatatableColumn(dateColumnDefinition); + const intervalDuration = timeInfo?.interval && parseInterval(timeInfo.interval); + + if (!timeInfo || !intervalDuration) { + throw new Error( + i18n.translate('xpack.lens.functions.timeScale.timeInfoMissingMessage', { + defaultMessage: 'Could not fetch date histogram information', + }) + ); + } + // the datemath plugin always parses dates by using the current default moment time zone. + // to use the configured time zone, we are switching just for the bounds calculation. + const defaultTimezone = moment().zoneName(); + moment.tz.setDefault(timeInfo.timeZone); + + const timeBounds = timeInfo.timeRange && calculateBounds(timeInfo.timeRange); + + const result = { + ...input, + columns: resultColumns, + rows: input.rows.map((row) => { + const newRow = { ...row }; + + let startOfBucket = moment(row[dateColumnId]); + let endOfBucket = startOfBucket.clone().add(intervalDuration); + if (timeBounds && timeBounds.min) { + startOfBucket = moment.max(startOfBucket, timeBounds.min); + } + if (timeBounds && timeBounds.max) { + endOfBucket = moment.min(endOfBucket, timeBounds.max); + } + const bucketSize = endOfBucket.diff(startOfBucket); + const factor = bucketSize / targetUnitInMs; + + const currentValue = newRow[inputColumnId]; + if (currentValue != null) { + newRow[outputColumnId] = Number(currentValue) / factor; + } + + return newRow; + }), + }; + // reset default moment timezone + moment.tz.setDefault(defaultTimezone); + + return result; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/time_scale/types.ts b/x-pack/plugins/lens/common/expressions/time_scale/types.ts new file mode 100644 index 0000000000000..4ee00ce53e68b --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/time_scale/types.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 type TimeScaleUnit = 's' | 'm' | 'h' | 'd'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts new file mode 100644 index 0000000000000..9a9273e43f6f1 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts @@ -0,0 +1,171 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { + ArgumentType, + ExpressionFunctionDefinition, +} from '../../../../../../src/plugins/expressions/common'; + +export interface AxesSettingsConfig { + x: boolean; + yLeft: boolean; + yRight: boolean; +} + +export interface AxisExtentConfig { + mode: 'full' | 'dataBounds' | 'custom'; + lowerBound?: number; + upperBound?: number; +} + +interface AxisConfig { + title: string; + hide?: boolean; +} + +export type YAxisMode = 'auto' | 'left' | 'right'; + +export interface YConfig { + forAccessor: string; + axisMode?: YAxisMode; + color?: string; +} + +export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { + type: 'lens_xy_axisTitlesVisibilityConfig'; +}; + +export const axisTitlesVisibilityConfig: ExpressionFunctionDefinition< + 'lens_xy_axisTitlesVisibilityConfig', + null, + AxesSettingsConfig, + AxisTitlesVisibilityConfigResult +> = { + name: 'lens_xy_axisTitlesVisibilityConfig', + aliases: [], + type: 'lens_xy_axisTitlesVisibilityConfig', + help: `Configure the xy chart's axis titles appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the right y-axis are visible.', + }), + }, + }, + fn: function fn(input: unknown, args: AxesSettingsConfig) { + return { + type: 'lens_xy_axisTitlesVisibilityConfig', + ...args, + }; + }, +}; + +export type AxisExtentConfigResult = AxisExtentConfig & { type: 'lens_xy_axisExtentConfig' }; + +export const axisExtentConfig: ExpressionFunctionDefinition< + 'lens_xy_axisExtentConfig', + null, + AxisExtentConfig, + AxisExtentConfigResult +> = { + name: 'lens_xy_axisExtentConfig', + aliases: [], + type: 'lens_xy_axisExtentConfig', + help: `Configure the xy chart's axis extents`, + inputTypes: ['null'], + args: { + mode: { + types: ['string'], + options: ['full', 'dataBounds', 'custom'], + help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + defaultMessage: 'The extent mode', + }), + }, + lowerBound: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + defaultMessage: 'The extent mode', + }), + }, + upperBound: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + defaultMessage: 'The extent mode', + }), + }, + }, + fn: function fn(input: unknown, args: AxisExtentConfig) { + return { + type: 'lens_xy_axisExtentConfig', + ...args, + }; + }, +}; + +export const axisConfig: { [key in keyof AxisConfig]: ArgumentType } = { + title: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.title.help', { + defaultMessage: 'The axis title', + }), + }, + hide: { + types: ['boolean'], + default: false, + help: 'Show / hide axis', + }, +}; + +export type YConfigResult = YConfig & { type: 'lens_xy_yConfig' }; + +export const yAxisConfig: ExpressionFunctionDefinition< + 'lens_xy_yConfig', + null, + YConfig, + YConfigResult +> = { + name: 'lens_xy_yConfig', + aliases: [], + type: 'lens_xy_yConfig', + help: `Configure the behavior of a xy chart's y axis metric`, + inputTypes: ['null'], + args: { + forAccessor: { + types: ['string'], + help: 'The accessor this configuration is for', + }, + axisMode: { + types: ['string'], + options: ['auto', 'left', 'right'], + help: 'The axis mode of the metric', + }, + color: { + types: ['string'], + help: 'The color of the series', + }, + }, + fn: function fn(input: unknown, args: YConfig) { + return { + type: 'lens_xy_yConfig', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/fitting_function.ts b/x-pack/plugins/lens/common/expressions/xy_chart/fitting_function.ts new file mode 100644 index 0000000000000..0cfea62d578d7 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/fitting_function.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 { i18n } from '@kbn/i18n'; + +export type FittingFunction = typeof fittingFunctionDefinitions[number]['id']; + +export const fittingFunctionDefinitions = [ + { + id: 'None', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', { + defaultMessage: 'Hide', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.none', { + defaultMessage: 'Do not fill gaps', + }), + }, + { + id: 'Zero', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.zero', { + defaultMessage: 'Zero', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.zero', { + defaultMessage: 'Fill gaps with zeros', + }), + }, + { + id: 'Linear', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.linear', { + defaultMessage: 'Linear', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.linear', { + defaultMessage: 'Fill gaps with a line', + }), + }, + { + id: 'Carry', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.carry', { + defaultMessage: 'Last', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.carry', { + defaultMessage: 'Fill gaps with the last value', + }), + }, + { + id: 'Lookahead', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.lookahead', { + defaultMessage: 'Next', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.lookahead', { + defaultMessage: 'Fill gaps with the next value', + }), + }, +] as const; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts new file mode 100644 index 0000000000000..6338e9f039937 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts @@ -0,0 +1,51 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { AxesSettingsConfig } from './axis_config'; + +export type GridlinesConfigResult = AxesSettingsConfig & { type: 'lens_xy_gridlinesConfig' }; + +export const gridlinesConfig: ExpressionFunctionDefinition< + 'lens_xy_gridlinesConfig', + null, + AxesSettingsConfig, + GridlinesConfigResult +> = { + name: 'lens_xy_gridlinesConfig', + aliases: [], + type: 'lens_xy_gridlinesConfig', + help: `Configure the xy chart's gridlines appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisGridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisgridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisgridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the right y-axis are visible.', + }), + }, + }, + fn: function fn(input: unknown, args: AxesSettingsConfig) { + return { + type: 'lens_xy_gridlinesConfig', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/index.ts b/x-pack/plugins/lens/common/expressions/xy_chart/index.ts new file mode 100644 index 0000000000000..a6f6c715c0ed1 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './axis_config'; +export * from './fitting_function'; +export * from './grid_lines_config'; +export * from './layer_config'; +export * from './legend_config'; +export * from './series_type'; +export * from './tick_labels_config'; +export * from './xy_args'; +export * from './xy_chart'; +export * from './labels_orientation_config'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/labels_orientation_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/labels_orientation_config.ts new file mode 100644 index 0000000000000..773ce61a102f9 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/labels_orientation_config.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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; + +export interface LabelsOrientationConfig { + x: number; + yLeft: number; + yRight: number; +} + +export type LabelsOrientationConfigResult = LabelsOrientationConfig & { + type: 'lens_xy_labelsOrientationConfig'; +}; + +export const labelsOrientationConfig: ExpressionFunctionDefinition< + 'lens_xy_labelsOrientationConfig', + null, + LabelsOrientationConfig, + LabelsOrientationConfigResult +> = { + name: 'lens_xy_labelsOrientationConfig', + aliases: [], + type: 'lens_xy_labelsOrientationConfig', + help: `Configure the xy chart's tick labels orientation`, + inputTypes: ['null'], + args: { + x: { + types: ['number'], + options: [0, -90, -45], + help: i18n.translate('xpack.lens.xyChart.xAxisLabelsOrientation.help', { + defaultMessage: 'Specifies the labels orientation of the x-axis.', + }), + }, + yLeft: { + types: ['number'], + options: [0, -90, -45], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisLabelsOrientation.help', { + defaultMessage: 'Specifies the labels orientation of the left y-axis.', + }), + }, + yRight: { + types: ['number'], + options: [0, -90, -45], + help: i18n.translate('xpack.lens.xyChart.yRightAxisLabelsOrientation.help', { + defaultMessage: 'Specifies the labels orientation of the right y-axis.', + }), + }, + }, + fn: function fn(input: unknown, args: LabelsOrientationConfig) { + return { + type: 'lens_xy_labelsOrientationConfig', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts new file mode 100644 index 0000000000000..f3baf242425f5 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts @@ -0,0 +1,120 @@ +/* + * 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 type { PaletteOutput } from '../../../../../../src/plugins/charts/common'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { axisConfig, YConfig } from './axis_config'; +import type { SeriesType } from './series_type'; + +export interface XYLayerConfig { + hide?: boolean; + layerId: string; + xAccessor?: string; + accessors: string[]; + yConfig?: YConfig[]; + seriesType: SeriesType; + splitAccessor?: string; + palette?: PaletteOutput; +} + +export interface ValidLayer extends XYLayerConfig { + xAccessor: NonNullable; +} + +export type LayerArgs = XYLayerConfig & { + columnToLabel?: string; // Actually a JSON key-value pair + yScaleType: 'time' | 'linear' | 'log' | 'sqrt'; + xScaleType: 'time' | 'linear' | 'ordinal'; + isHistogram: boolean; + // palette will always be set on the expression + palette: PaletteOutput; +}; + +export type LayerConfigResult = LayerArgs & { type: 'lens_xy_layer' }; + +export const layerConfig: ExpressionFunctionDefinition< + 'lens_xy_layer', + null, + LayerArgs, + LayerConfigResult +> = { + name: 'lens_xy_layer', + aliases: [], + type: 'lens_xy_layer', + help: `Configure a layer in the xy chart`, + inputTypes: ['null'], + args: { + ...axisConfig, + layerId: { + types: ['string'], + help: '', + }, + xAccessor: { + types: ['string'], + help: '', + }, + seriesType: { + types: ['string'], + options: [ + 'bar', + 'line', + 'area', + 'bar_stacked', + 'area_stacked', + 'bar_percentage_stacked', + 'area_percentage_stacked', + ], + help: 'The type of chart to display.', + }, + xScaleType: { + options: ['ordinal', 'linear', 'time'], + help: 'The scale type of the x axis', + default: 'ordinal', + }, + isHistogram: { + types: ['boolean'], + default: false, + help: 'Whether to layout the chart as a histogram', + }, + yScaleType: { + options: ['log', 'sqrt', 'linear', 'time'], + help: 'The scale type of the y axes', + default: 'linear', + }, + splitAccessor: { + types: ['string'], + help: 'The column to split by', + multi: false, + }, + accessors: { + types: ['string'], + help: 'The columns to display on the y axis.', + multi: true, + }, + yConfig: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + types: ['lens_xy_yConfig' as any], + help: 'Additional configuration for y axes', + multi: true, + }, + columnToLabel: { + types: ['string'], + help: 'JSON key-value pairs of column ID to label', + }, + palette: { + default: `{theme "palette" default={system_palette name="default"} }`, + help: '', + types: ['palette'], + }, + }, + fn: function fn(input: unknown, args: LayerArgs) { + return { + type: 'lens_xy_layer', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts new file mode 100644 index 0000000000000..e228039b53ef6 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts @@ -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 { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; + +export interface LegendConfig { + /** + * Flag whether the legend should be shown. If there is just a single series, it will be hidden + */ + isVisible: boolean; + /** + * Position of the legend relative to the chart + */ + position: Position; + /** + * Flag whether the legend should be shown even with just a single series + */ + showSingleSeries?: boolean; + /** + * Flag whether the legend is inside the chart + */ + isInside?: boolean; + /** + * Horizontal Alignment of the legend when it is set inside chart + */ + horizontalAlignment?: HorizontalAlignment; + /** + * Vertical Alignment of the legend when it is set inside chart + */ + verticalAlignment?: VerticalAlignment; + /** + * Number of columns when legend is set inside chart + */ + floatingColumns?: number; +} + +export type LegendConfigResult = LegendConfig & { type: 'lens_xy_legendConfig' }; + +export const legendConfig: ExpressionFunctionDefinition< + 'lens_xy_legendConfig', + null, + LegendConfig, + LegendConfigResult +> = { + name: 'lens_xy_legendConfig', + aliases: [], + type: 'lens_xy_legendConfig', + help: `Configure the xy chart's legend`, + inputTypes: ['null'], + args: { + isVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.isVisible.help', { + defaultMessage: 'Specifies whether or not the legend is visible.', + }), + }, + position: { + types: ['string'], + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + help: i18n.translate('xpack.lens.xyChart.position.help', { + defaultMessage: 'Specifies the legend position.', + }), + }, + showSingleSeries: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.showSingleSeries.help', { + defaultMessage: 'Specifies whether a legend with just a single entry should be shown', + }), + }, + isInside: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.isInside.help', { + defaultMessage: 'Specifies whether a legend is inside the chart', + }), + }, + horizontalAlignment: { + types: ['string'], + options: [HorizontalAlignment.Right, HorizontalAlignment.Left], + help: i18n.translate('xpack.lens.xyChart.horizontalAlignment.help', { + defaultMessage: + 'Specifies the horizontal alignment of the legend when it is displayed inside chart.', + }), + }, + verticalAlignment: { + types: ['string'], + options: [VerticalAlignment.Top, VerticalAlignment.Bottom], + help: i18n.translate('xpack.lens.xyChart.verticalAlignment.help', { + defaultMessage: + 'Specifies the vertical alignment of the legend when it is displayed inside chart.', + }), + }, + floatingColumns: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.floatingColumns.help', { + defaultMessage: 'Specifies the number of columns when legend is displayed inside chart.', + }), + }, + }, + fn: function fn(input: unknown, args: LegendConfig) { + return { + type: 'lens_xy_legendConfig', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts b/x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts new file mode 100644 index 0000000000000..f9a375b8b47a1 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts @@ -0,0 +1,18 @@ +/* + * 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 SeriesType = + | 'bar' + | 'bar_horizontal' + | 'line' + | 'area' + | 'bar_stacked' + | 'bar_percentage_stacked' + | 'bar_horizontal_stacked' + | 'bar_horizontal_percentage_stacked' + | 'area_stacked' + | 'area_percentage_stacked'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts new file mode 100644 index 0000000000000..4af78d8355786 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts @@ -0,0 +1,51 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { AxesSettingsConfig } from './axis_config'; + +export type TickLabelsConfigResult = AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' }; + +export const tickLabelsConfig: ExpressionFunctionDefinition< + 'lens_xy_tickLabelsConfig', + null, + AxesSettingsConfig, + TickLabelsConfigResult +> = { + name: 'lens_xy_tickLabelsConfig', + aliases: [], + type: 'lens_xy_tickLabelsConfig', + help: `Configure the xy chart's tick labels appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the right y-axis are visible.', + }), + }, + }, + fn: function fn(input: unknown, args: AxesSettingsConfig) { + return { + type: 'lens_xy_tickLabelsConfig', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts new file mode 100644 index 0000000000000..fb794eda22dbe --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts @@ -0,0 +1,41 @@ +/* + * 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 type { AxisExtentConfigResult, AxisTitlesVisibilityConfigResult } from './axis_config'; +import type { FittingFunction } from './fitting_function'; +import type { GridlinesConfigResult } from './grid_lines_config'; +import type { LayerArgs } from './layer_config'; +import type { LegendConfigResult } from './legend_config'; +import type { TickLabelsConfigResult } from './tick_labels_config'; +import type { LabelsOrientationConfigResult } from './labels_orientation_config'; + +export type ValueLabelConfig = 'hide' | 'inside' | 'outside'; + +export type XYCurveType = 'LINEAR' | 'CURVE_MONOTONE_X'; + +// Arguments to XY chart expression, with computed properties +export interface XYArgs { + title?: string; + description?: string; + xTitle: string; + yTitle: string; + yRightTitle: string; + yLeftExtent: AxisExtentConfigResult; + yRightExtent: AxisExtentConfigResult; + legend: LegendConfigResult; + valueLabels: ValueLabelConfig; + layers: LayerArgs[]; + fittingFunction?: FittingFunction; + axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; + tickLabelsVisibilitySettings?: TickLabelsConfigResult; + gridlinesVisibilitySettings?: GridlinesConfigResult; + labelsOrientation?: LabelsOrientationConfigResult; + curveType?: XYCurveType; + fillOpacity?: number; + hideEndzones?: boolean; + valuesInLegend?: boolean; +} diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts new file mode 100644 index 0000000000000..0e58105447689 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts @@ -0,0 +1,162 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { ExpressionValueSearchContext } from '../../../../../../src/plugins/data/common'; +import type { LensMultiTable } from '../../types'; +import type { XYArgs } from './xy_args'; +import { fittingFunctionDefinitions } from './fitting_function'; + +export interface XYChartProps { + data: LensMultiTable; + args: XYArgs; +} + +export interface XYRender { + type: 'render'; + as: 'lens_xy_chart_renderer'; + value: XYChartProps; +} + +export const xyChart: ExpressionFunctionDefinition< + 'lens_xy_chart', + LensMultiTable | ExpressionValueSearchContext | null, + XYArgs, + XYRender +> = { + name: 'lens_xy_chart', + type: 'render', + inputTypes: ['lens_multitable', 'kibana_context', 'null'], + help: i18n.translate('xpack.lens.xyChart.help', { + defaultMessage: 'An X/Y chart', + }), + args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, + xTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.xTitle.help', { + defaultMessage: 'X axis title', + }), + }, + yTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { + defaultMessage: 'Y left axis title', + }), + }, + yRightTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { + defaultMessage: 'Y right axis title', + }), + }, + yLeftExtent: { + types: ['lens_xy_axisExtentConfig'], + help: i18n.translate('xpack.lens.xyChart.yLeftExtent.help', { + defaultMessage: 'Y left axis extents', + }), + }, + yRightExtent: { + types: ['lens_xy_axisExtentConfig'], + help: i18n.translate('xpack.lens.xyChart.yRightExtent.help', { + defaultMessage: 'Y right axis extents', + }), + }, + legend: { + types: ['lens_xy_legendConfig'], + help: i18n.translate('xpack.lens.xyChart.legend.help', { + defaultMessage: 'Configure the chart legend.', + }), + }, + fittingFunction: { + types: ['string'], + options: [...fittingFunctionDefinitions.map(({ id }) => id)], + help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { + defaultMessage: 'Define how missing values are treated', + }), + }, + valueLabels: { + types: ['string'], + options: ['hide', 'inside'], + help: '', + }, + tickLabelsVisibilitySettings: { + types: ['lens_xy_tickLabelsConfig'], + help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { + defaultMessage: 'Show x and y axes tick labels', + }), + }, + labelsOrientation: { + types: ['lens_xy_labelsOrientationConfig'], + help: i18n.translate('xpack.lens.xyChart.labelsOrientation.help', { + defaultMessage: 'Defines the rotation of the axis labels', + }), + }, + gridlinesVisibilitySettings: { + types: ['lens_xy_gridlinesConfig'], + help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { + defaultMessage: 'Show x and y axes gridlines', + }), + }, + axisTitlesVisibilitySettings: { + types: ['lens_xy_axisTitlesVisibilityConfig'], + help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { + defaultMessage: 'Show x and y axes titles', + }), + }, + layers: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + types: ['lens_xy_layer'] as any, + help: 'Layers of visual series', + multi: true, + }, + curveType: { + types: ['string'], + options: ['LINEAR', 'CURVE_MONOTONE_X'], + help: i18n.translate('xpack.lens.xyChart.curveType.help', { + defaultMessage: 'Define how curve type is rendered for a line chart', + }), + }, + fillOpacity: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.fillOpacity.help', { + defaultMessage: 'Define the area chart fill opacity', + }), + }, + hideEndzones: { + types: ['boolean'], + default: false, + help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', { + defaultMessage: 'Hide endzone markers for partial data', + }), + }, + valuesInLegend: { + types: ['boolean'], + default: false, + help: i18n.translate('xpack.lens.xyChart.valuesInLegend.help', { + defaultMessage: 'Show values in legend', + }), + }, + }, + fn(data: LensMultiTable, args: XYArgs) { + return { + type: 'render', + as: 'lens_xy_chart_renderer', + value: { + data, + args, + }, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/index.ts b/x-pack/plugins/lens/common/index.ts index 25a96d764bb28..42e673058f1db 100644 --- a/x-pack/plugins/lens/common/index.ts +++ b/x-pack/plugins/lens/common/index.ts @@ -8,3 +8,6 @@ export * from './api'; export * from './constants'; export * from './types'; + +// Note: do not import the expression folder here or the page bundle will be bloated with all +// the package diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts b/x-pack/plugins/lens/common/suffix_formatter/index.ts similarity index 93% rename from x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts rename to x-pack/plugins/lens/common/suffix_formatter/index.ts index f21b854128958..12a4e02a81ef2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts +++ b/x-pack/plugins/lens/common/suffix_formatter/index.ts @@ -10,9 +10,9 @@ import { FieldFormat, FieldFormatInstanceType, KBN_FIELD_TYPES, -} from '../../../../../src/plugins/data/public'; -import { FormatFactory } from '../types'; -import { TimeScaleUnit } from './time_scale'; +} from '../../../../../src/plugins/data/common'; +import type { FormatFactory } from '../types'; +import type { TimeScaleUnit } from '../expressions/time_scale'; const unitSuffixes: Record = { s: i18n.translate('xpack.lens.fieldFormats.suffix.s', { defaultMessage: '/s' }), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts b/x-pack/plugins/lens/common/suffix_formatter/suffix_formatter.test.ts similarity index 96% rename from x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts rename to x-pack/plugins/lens/common/suffix_formatter/suffix_formatter.test.ts index 4349b95c4deaf..c4379bdd1fb34 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts +++ b/x-pack/plugins/lens/common/suffix_formatter/suffix_formatter.test.ts @@ -6,7 +6,7 @@ */ import { FormatFactory } from '../types'; -import { getSuffixFormatter } from './suffix_formatter'; +import { getSuffixFormatter } from './index'; describe('suffix formatter', () => { it('should call nested formatter and apply suffix', () => { diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index 2ca31c08a4ec7..a60061a3aa054 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { FilterMeta, Filter } from 'src/plugins/data/common'; +import type { FilterMeta, Filter, IFieldFormat } from '../../../../src/plugins/data/common'; +import type { Datatable, SerializedFieldFormat } from '../../../../src/plugins/expressions/common'; + +export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; export interface ExistingFields { indexPatternTitle: string; @@ -24,3 +27,32 @@ export interface PersistableFilterMeta extends FilterMeta { export interface PersistableFilter extends Filter { meta: PersistableFilterMeta; } + +export interface LensMultiTable { + type: 'lens_multitable'; + tables: Record; + dateRange?: { + fromDate: Date; + toDate: Date; + }; +} + +export interface ColorStop { + color: string; + stop: number; +} + +export interface CustomPaletteParams { + name?: string; + reverse?: boolean; + rangeType?: 'number' | 'percent'; + continuity?: 'above' | 'below' | 'all' | 'none'; + progression?: 'fixed'; + rangeMin?: number; + rangeMax?: number; + stops?: ColorStop[]; + colorStops?: ColorStop[]; + steps?: number; +} + +export type RequiredPaletteParamTypes = Required; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx index aa8c6ffb26d17..fb9cb992fcf47 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx @@ -14,7 +14,7 @@ import { Datatable } from 'src/plugins/expressions/public'; import { IUiSettingsClient } from 'kibana/public'; import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; -import { Args, ColumnConfigArg } from '../expression'; +import { DatatableArgs, ColumnConfigArg } from '../../../common/expressions'; import { DataContextType } from './types'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; @@ -91,7 +91,7 @@ describe('datatable cell renderer', () => { const paletteRegistry = chartPluginMock.createPaletteRegistry(); const customPalette = paletteRegistry.get('custom'); - function getCellRenderer(columnConfig: Args) { + function getCellRenderer(columnConfig: DatatableArgs) { return createGridCell( { a: { convert: (x) => `formatted ${x}` } as FieldFormat, @@ -101,7 +101,7 @@ describe('datatable cell renderer', () => { ({ get: jest.fn() } as unknown) as IUiSettingsClient ); } - function getColumnConfiguration(): Args { + function getColumnConfiguration(): DatatableArgs { return { title: 'myData', columns: [ @@ -136,7 +136,10 @@ describe('datatable cell renderer', () => { }); } - async function renderCellComponent(columnConfig: Args, context: Partial = {}) { + async function renderCellComponent( + columnConfig: DatatableArgs, + context: Partial = {} + ) { const CellRendererWithPalette = getCellRenderer(columnConfig); const setCellProps = jest.fn(); diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx index 5a3aa3b45b848..6d6b2e4b1013f 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx @@ -8,11 +8,11 @@ import React, { useContext, useEffect } from 'react'; import { EuiDataGridCellValueElementProps } from '@elastic/eui'; import { IUiSettingsClient } from 'kibana/public'; -import type { FormatFactory } from '../../types'; +import type { FormatFactory } from '../../../common'; +import { getOriginalId } from '../../../common/expressions'; +import type { ColumnConfig } from '../../../common/expressions'; import type { DataContextType } from './types'; -import { ColumnConfig } from './table_basic'; import { getContrastColor, getNumericValue } from '../../shared_components/coloring/utils'; -import { getOriginalId } from '../transpose_helpers'; export const createGridCell = ( formatters: Record>, diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx index 4372e2cd9e964..0bc249c783239 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx @@ -13,8 +13,8 @@ import { EuiListGroupItemProps, } from '@elastic/eui'; import type { Datatable, DatatableColumn, DatatableColumnMeta } from 'src/plugins/expressions'; -import type { FormatFactory } from '../../types'; -import { ColumnConfig } from './table_basic'; +import type { FormatFactory } from '../../../common'; +import type { ColumnConfig } from '../../../common/expressions'; export const createGridColumns = ( bucketColumns: string[], diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx index 705484edcf0e6..6840f4f13450c 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx @@ -21,8 +21,7 @@ import { } from '@elastic/eui'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { VisualizationDimensionEditorProps } from '../../types'; -import { ColumnState, DatatableVisualizationState } from '../visualization'; -import { getOriginalId } from '../transpose_helpers'; +import { DatatableVisualizationState } from '../visualization'; import { CustomizablePalette, applyPaletteParams, @@ -33,13 +32,16 @@ import { PalettePanelContainer, findMinMaxByColumnId, } from '../../shared_components/'; -import './dimension_editor.scss'; +import type { ColumnState } from '../../../common/expressions'; import { + isNumericFieldForDatatable, getDefaultSummaryLabel, getFinalSummaryConfiguration, getSummaryRowOptions, -} from '../summary'; -import { isNumericField } from '../utils'; + getOriginalId, +} from '../../../common/expressions'; + +import './dimension_editor.scss'; const idPrefix = htmlIdGenerator()(); @@ -93,7 +95,7 @@ export function TableDimensionEditor( const currentData = frame.activeData?.[state.layerId]; // either read config state or use same logic as chart itself - const isNumeric = isNumericField(currentData, accessor); + const isNumeric = isNumericFieldForDatatable(currentData, accessor); const currentAlignment = column?.alignment || (isNumeric ? 'right' : 'left'); const currentColorMode = column?.colorMode || 'none'; const hasDynamicColoring = currentColorMode !== 'none'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts index 8490d33f83444..bcce2fa2f6f69 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts @@ -17,9 +17,8 @@ import { createGridHideHandler, createTransposeColumnFilterHandler, } from './table_actions'; -import { LensGridDirection } from './types'; -import { ColumnConfig } from './table_basic'; -import { LensMultiTable } from '../../types'; +import { LensMultiTable } from '../../../common'; +import { LensGridDirection, ColumnConfig } from '../../../common/expressions'; function getDefaultConfig(): ColumnConfig { return { diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts index 8615ed6536316..62c2ec3a7f7fd 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts @@ -7,15 +7,11 @@ import type { EuiDataGridSorting } from '@elastic/eui'; import type { Datatable, DatatableColumn } from 'src/plugins/expressions'; -import type { LensFilterEvent, LensMultiTable } from '../../types'; -import type { - LensGridDirection, - LensResizeAction, - LensSortAction, - LensToggleAction, -} from './types'; -import { ColumnConfig } from './table_basic'; -import { getOriginalId } from '../transpose_helpers'; +import type { LensFilterEvent } from '../../types'; +import type { LensMultiTable } from '../../../common'; +import type { LensResizeAction, LensSortAction, LensToggleAction } from './types'; +import type { ColumnConfig, LensGridDirection } from '../../../common/expressions'; +import { getOriginalId } from '../../../common/expressions'; export const createGridResizeHandler = ( columnConfig: ColumnConfig, diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx index ae51f7d42312f..bb678a361e174 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx @@ -15,8 +15,8 @@ import { VisualizationContainer } from '../../visualization_container'; import { EmptyPlaceholder } from '../../shared_components'; import { LensIconChartDatatable } from '../../assets/chart_datatable'; import { DataContext, DatatableComponent } from './table_basic'; -import { LensMultiTable } from '../../types'; -import { DatatableProps } from '../expression'; +import { LensMultiTable } from '../../../common'; +import { DatatableProps } from '../../../common/expressions'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { IUiSettingsClient } from 'kibana/public'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx index 8ef64e4acdccc..ac1324385dbd1 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx @@ -18,18 +18,17 @@ import { EuiDataGridSorting, EuiDataGridStyle, } from '@elastic/eui'; -import { CustomPaletteState, PaletteOutput } from 'src/plugins/charts/common'; -import { FormatFactory, LensFilterEvent, LensTableRowContextMenuEvent } from '../../types'; +import type { LensFilterEvent, LensTableRowContextMenuEvent } from '../../types'; +import type { FormatFactory } from '../../../common'; +import { LensGridDirection } from '../../../common/expressions'; import { VisualizationContainer } from '../../visualization_container'; import { EmptyPlaceholder, findMinMaxByColumnId } from '../../shared_components'; import { LensIconChartDatatable } from '../../assets/chart_datatable'; -import { ColumnState } from '../visualization'; -import { +import type { DataContextType, DatatableRenderProps, LensSortAction, LensResizeAction, - LensGridDirection, LensToggleAction, } from './types'; import { createGridColumns } from './columns'; @@ -42,8 +41,7 @@ import { createTransposeColumnFilterHandler, } from './table_actions'; import { CUSTOM_PALETTE } from '../../shared_components/coloring/constants'; -import { getFinalSummaryConfiguration } from '../summary'; -import { getOriginalId } from '../transpose_helpers'; +import { getOriginalId, getFinalSummaryConfiguration } from '../../../common/expressions'; export const DataContext = React.createContext({}); @@ -52,17 +50,6 @@ const gridStyle: EuiDataGridStyle = { header: 'underline', }; -export interface ColumnConfig { - columns: Array< - Omit & { - type: 'lens_datatable_column'; - palette?: PaletteOutput; - } - >; - sortingColumnId: string | undefined; - sortingDirection: LensGridDirection; -} - export const DatatableComponent = (props: DatatableRenderProps) => { const [firstTable] = Object.values(props.data.tables); diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/types.ts b/x-pack/plugins/lens/public/datatable_visualization/components/types.ts index 2095715756a53..f3d81c2d13340 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/types.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/types.ts @@ -5,16 +5,14 @@ * 2.0. */ -import type { Direction } from '@elastic/eui'; import { IUiSettingsClient } from 'kibana/public'; import { CustomPaletteState, PaletteRegistry } from 'src/plugins/charts/public'; import type { IAggType } from 'src/plugins/data/public'; import type { Datatable, RenderMode } from 'src/plugins/expressions'; -import type { FormatFactory, ILensInterpreterRenderHandlers, LensEditEvent } from '../../types'; -import type { DatatableProps } from '../expression'; +import type { ILensInterpreterRenderHandlers, LensEditEvent } from '../../types'; import { LENS_EDIT_SORT_ACTION, LENS_EDIT_RESIZE_ACTION, LENS_TOGGLE_ACTION } from './constants'; - -export type LensGridDirection = 'none' | Direction; +import type { FormatFactory } from '../../../common'; +import type { DatatableProps, LensGridDirection } from '../../../common/expressions'; export interface LensSortActionData { columnId: string | undefined; @@ -49,12 +47,6 @@ export type DatatableRenderProps = DatatableProps & { rowHasRowClickTriggerActions?: boolean[]; }; -export interface DatatableRender { - type: 'render'; - as: 'lens_datatable_renderer'; - value: DatatableProps; -} - export interface DataContextType { table?: Datatable; rowHasRowClickTriggerActions?: boolean[]; diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index 3ba448b49afc9..4b4d2275d0dec 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import { DatatableProps, getDatatable } from './expression'; -import { LensMultiTable } from '../types'; +import { DatatableProps } from '../../common/expressions'; +import type { LensMultiTable } from '../../common'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { IFieldFormat } from '../../../../../src/plugins/data/public'; +import { getDatatable } from './expression'; function sampleArgs() { const indexPatternId = 'indexPatternId'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 79a541b0288ab..4e541bce9a8c2 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -7,194 +7,20 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import type { IAggType } from 'src/plugins/data/public'; -import { - DatatableColumnMeta, - ExpressionFunctionDefinition, - ExpressionRenderDefinition, -} from 'src/plugins/expressions'; -import { CustomPaletteState, PaletteOutput } from 'src/plugins/charts/common'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { IUiSettingsClient } from 'kibana/public'; -import { getSortingCriteria } from './sorting'; - +import { ExpressionRenderDefinition } from 'src/plugins/expressions'; import { DatatableComponent } from './components/table_basic'; -import { ColumnState } from './visualization'; - -import type { FormatFactory, ILensInterpreterRenderHandlers, LensMultiTable } from '../types'; -import type { DatatableRender } from './components/types'; -import { transposeTable } from './transpose_helpers'; -import { computeSummaryRowForColumn } from './summary'; - -export type ColumnConfigArg = Omit & { - type: 'lens_datatable_column'; - palette?: PaletteOutput; - summaryRowValue?: unknown; -}; - -export interface Args { - title: string; - description?: string; - columns: ColumnConfigArg[]; - sortingColumnId: string | undefined; - sortingDirection: 'asc' | 'desc' | 'none'; -} - -export interface DatatableProps { - data: LensMultiTable; - untransposedData?: LensMultiTable; - args: Args; -} - -function isRange(meta: { params?: { id?: string } } | undefined) { - return meta?.params?.id === 'range'; -} - -export const getDatatable = ({ - formatFactory, -}: { - formatFactory: FormatFactory; -}): ExpressionFunctionDefinition<'lens_datatable', LensMultiTable, Args, DatatableRender> => ({ - name: 'lens_datatable', - type: 'render', - inputTypes: ['lens_multitable'], - help: i18n.translate('xpack.lens.datatable.expressionHelpLabel', { - defaultMessage: 'Datatable renderer', - }), - args: { - title: { - types: ['string'], - help: i18n.translate('xpack.lens.datatable.titleLabel', { - defaultMessage: 'Title', - }), - }, - description: { - types: ['string'], - help: '', - }, - columns: { - types: ['lens_datatable_column'], - help: '', - multi: true, - }, - sortingColumnId: { - types: ['string'], - help: '', - }, - sortingDirection: { - types: ['string'], - help: '', - }, - }, - fn(data, args, context) { - let untransposedData: LensMultiTable | undefined; - // do the sorting at this level to propagate it also at CSV download - const [firstTable] = Object.values(data.tables); - const [layerId] = Object.keys(context.inspectorAdapters.tables || {}); - const formatters: Record> = {}; - - firstTable.columns.forEach((column) => { - formatters[column.id] = formatFactory(column.meta?.params); - }); - - const hasTransposedColumns = args.columns.some((c) => c.isTransposed); - if (hasTransposedColumns) { - // store original shape of data separately - untransposedData = cloneDeep(data); - // transposes table and args inplace - transposeTable(args, firstTable, formatters); - } - - const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args; - - const columnsReverseLookup = firstTable.columns.reduce< - Record - >((memo, { id, name, meta }, i) => { - memo[id] = { name, index: i, meta }; - return memo; - }, {}); - - const columnsWithSummary = args.columns.filter((c) => c.summaryRow); - for (const column of columnsWithSummary) { - column.summaryRowValue = computeSummaryRowForColumn( - column, - firstTable, - formatters, - formatFactory({ id: 'number' }) - ); - } - - if (sortBy && columnsReverseLookup[sortBy] && sortDirection !== 'none') { - // Sort on raw values for these types, while use the formatted value for the rest - const sortingCriteria = getSortingCriteria( - isRange(columnsReverseLookup[sortBy]?.meta) - ? 'range' - : columnsReverseLookup[sortBy]?.meta?.type, - sortBy, - formatters[sortBy], - sortDirection - ); - // replace the table here - context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || []) - .slice() - .sort(sortingCriteria); - // replace also the local copy - firstTable.rows = context.inspectorAdapters.tables[layerId].rows; - } else { - args.sortingColumnId = undefined; - args.sortingDirection = 'none'; - } - return { - type: 'render', - as: 'lens_datatable_renderer', - value: { - data, - untransposedData, - args, - }, - }; - }, -}); -type DatatableColumnResult = ColumnState & { type: 'lens_datatable_column' }; +import type { ILensInterpreterRenderHandlers } from '../types'; +import type { FormatFactory } from '../../common'; +import { DatatableProps } from '../../common/expressions'; -export const datatableColumn: ExpressionFunctionDefinition< - 'lens_datatable_column', - null, - ColumnState, - DatatableColumnResult -> = { - name: 'lens_datatable_column', - aliases: [], - type: 'lens_datatable_column', - help: '', - inputTypes: ['null'], - args: { - columnId: { types: ['string'], help: '' }, - alignment: { types: ['string'], help: '' }, - hidden: { types: ['boolean'], help: '' }, - width: { types: ['number'], help: '' }, - isTransposed: { types: ['boolean'], help: '' }, - transposable: { types: ['boolean'], help: '' }, - colorMode: { types: ['string'], help: '' }, - palette: { - types: ['palette'], - help: '', - }, - summaryRow: { types: ['string'], help: '' }, - summaryLabel: { types: ['string'], help: '' }, - }, - fn: function fn(input: unknown, args: ColumnState) { - return { - type: 'lens_datatable_column', - ...args, - }; - }, -}; +export { datatableColumn, getDatatable } from '../../common/expressions'; export const getDatatableRenderer = (dependencies: { formatFactory: FormatFactory; diff --git a/x-pack/plugins/lens/public/datatable_visualization/index.ts b/x-pack/plugins/lens/public/datatable_visualization/index.ts index 7f48d00d00f7f..b4f37faf0bc00 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/index.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/index.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { CoreSetup } from 'kibana/public'; -import { ChartsPluginSetup } from 'src/plugins/charts/public'; -import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; -import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; +import type { CoreSetup } from 'kibana/public'; +import type { ChartsPluginSetup } from 'src/plugins/charts/public'; +import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; +import type { FormatFactory } from '../../common'; interface DatatableVisualizationPluginStartPlugins { data: DataPublicPluginStart; diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index e7ab4aab88f2e..691fce0ed70d2 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -10,9 +10,8 @@ import { render } from 'react-dom'; import { Ast } from '@kbn/interpreter/common'; import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { DatatableColumn } from 'src/plugins/expressions/public'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; -import { +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { SuggestionRequest, Visualization, VisualizationSuggestion, @@ -21,32 +20,9 @@ import { import { LensIconChartDatatable } from '../assets/chart_datatable'; import { TableDimensionEditor } from './components/dimension_editor'; import { CUSTOM_PALETTE } from '../shared_components/coloring/constants'; -import { CustomPaletteParams } from '../shared_components/coloring/types'; import { getStopsForFixedMode } from '../shared_components'; -import { getDefaultSummaryLabel } from './summary'; - -export interface ColumnState { - columnId: string; - width?: number; - hidden?: boolean; - isTransposed?: boolean; - // These flags are necessary to transpose columns and map them back later - // They are set automatically and are not user-editable - transposable?: boolean; - originalColumnId?: string; - originalName?: string; - bucketValues?: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>; - alignment?: 'left' | 'right' | 'center'; - palette?: PaletteOutput; - colorMode?: 'none' | 'cell' | 'text'; - summaryRow?: 'none' | 'sum' | 'avg' | 'count' | 'min' | 'max'; - summaryLabel?: string; -} - -export interface SortingState { - columnId: string | undefined; - direction: 'asc' | 'desc' | 'none'; -} +import { getDefaultSummaryLabel } from '../../common/expressions'; +import type { ColumnState, SortingState } from '../../common/expressions'; export interface DatatableVisualizationState { columns: ColumnState[]; diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index 63340795ec6c8..b3574f19b5caf 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -22,7 +22,7 @@ import { EditorFrameStart, } from '../types'; import { Document } from '../persistence/saved_object_store'; -import { mergeTables } from './merge_tables'; +import { mergeTables } from '../../common/expressions'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; import { DashboardStart } from '../../../../../src/plugins/dashboard/public'; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index e27abc4ae32ec..e8095f6c741a4 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -23,9 +23,8 @@ import type { LensByReferenceInput, LensByValueInput } from './embeddable'; import type { Document } from '../persistence'; import type { IndexPatternPersistedState } from '../indexpattern_datasource/types'; import type { XYState } from '../xy_visualization/types'; -import type { PieVisualizationState } from '../pie_visualization/types'; +import type { PieVisualizationState, MetricState } from '../../common/expressions'; import type { DatatableVisualizationState } from '../datatable_visualization/visualization'; -import type { MetricState } from '../metric_visualization/types'; type LensAttributes = Omit< Document, diff --git a/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx b/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx index 3e8e9d184ed8a..15e9963ff5740 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx @@ -16,11 +16,11 @@ import { ScaleType, Settings, } from '@elastic/charts'; -import { CustomPaletteState } from 'src/plugins/charts/public'; +import type { CustomPaletteState } from 'src/plugins/charts/public'; import { VisualizationContainer } from '../visualization_container'; -import { HeatmapRenderProps } from './types'; +import type { HeatmapRenderProps } from './types'; import './index.scss'; -import { LensBrushEvent, LensFilterEvent } from '../types'; +import type { LensBrushEvent, LensFilterEvent } from '../types'; import { applyPaletteParams, defaultPaletteParams, diff --git a/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx b/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx index 85daa4805b9ec..ca4a65e6fb10f 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx @@ -14,8 +14,8 @@ import { EuiFlexGroup, EuiButtonEmpty, } from '@elastic/eui'; -import { PaletteRegistry } from 'src/plugins/charts/public'; -import { VisualizationDimensionEditorProps } from '../types'; +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { VisualizationDimensionEditorProps } from '../types'; import { CustomizablePalette, FIXED_PROGRESSION, @@ -23,7 +23,7 @@ import { PalettePanelContainer, } from '../shared_components/'; import './dimension_editor.scss'; -import { HeatmapVisualizationState } from './types'; +import type { HeatmapVisualizationState } from './types'; import { getSafePaletteParams } from './utils'; export function HeatmapDimensionEditor( diff --git a/x-pack/plugins/lens/public/heatmap_visualization/expression.tsx b/x-pack/plugins/lens/public/heatmap_visualization/expression.tsx index 37dad117bb783..27be4b9ce7fe9 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/expression.tsx @@ -9,221 +9,15 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import ReactDOM from 'react-dom'; import React from 'react'; -import { Position } from '@elastic/charts'; -import { - ExpressionFunctionDefinition, - IInterpreterRenderHandlers, -} from '../../../../../src/plugins/expressions'; -import { FormatFactory, LensBrushEvent, LensFilterEvent, LensMultiTable } from '../types'; -import { - FUNCTION_NAME, - HEATMAP_GRID_FUNCTION, - LEGEND_FUNCTION, - LENS_HEATMAP_RENDERER, -} from './constants'; -import type { - HeatmapExpressionArgs, - HeatmapExpressionProps, - HeatmapGridConfig, - HeatmapGridConfigResult, - HeatmapRender, - LegendConfigResult, -} from './types'; -import { HeatmapLegendConfig } from './types'; -import { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; +import type { IInterpreterRenderHandlers } from '../../../../../src/plugins/expressions'; +import type { LensBrushEvent, LensFilterEvent } from '../types'; +import type { FormatFactory } from '../../common'; +import { LENS_HEATMAP_RENDERER } from './constants'; +import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; import { HeatmapChartReportable } from './chart_component'; +import type { HeatmapExpressionProps } from './types'; -export const heatmapGridConfig: ExpressionFunctionDefinition< - typeof HEATMAP_GRID_FUNCTION, - null, - HeatmapGridConfig, - HeatmapGridConfigResult -> = { - name: HEATMAP_GRID_FUNCTION, - aliases: [], - type: HEATMAP_GRID_FUNCTION, - help: `Configure the heatmap layout `, - inputTypes: ['null'], - args: { - // grid - strokeWidth: { - types: ['number'], - help: i18n.translate('xpack.lens.heatmapChart.config.strokeWidth.help', { - defaultMessage: 'Specifies the grid stroke width', - }), - required: false, - }, - strokeColor: { - types: ['string'], - help: i18n.translate('xpack.lens.heatmapChart.config.strokeColor.help', { - defaultMessage: 'Specifies the grid stroke color', - }), - required: false, - }, - cellHeight: { - types: ['number'], - help: i18n.translate('xpack.lens.heatmapChart.config.cellHeight.help', { - defaultMessage: 'Specifies the grid cell height', - }), - required: false, - }, - cellWidth: { - types: ['number'], - help: i18n.translate('xpack.lens.heatmapChart.config.cellWidth.help', { - defaultMessage: 'Specifies the grid cell width', - }), - required: false, - }, - // cells - isCellLabelVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.heatmapChart.config.isCellLabelVisible.help', { - defaultMessage: 'Specifies whether or not the cell label is visible.', - }), - }, - // Y-axis - isYAxisLabelVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.heatmapChart.config.isYAxisLabelVisible.help', { - defaultMessage: 'Specifies whether or not the Y-axis labels are visible.', - }), - }, - yAxisLabelWidth: { - types: ['number'], - help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelWidth.help', { - defaultMessage: 'Specifies the width of the Y-axis labels.', - }), - required: false, - }, - yAxisLabelColor: { - types: ['string'], - help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelColor.help', { - defaultMessage: 'Specifies the color of the Y-axis labels.', - }), - required: false, - }, - // X-axis - isXAxisLabelVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.heatmapChart.config.isXAxisLabelVisible.help', { - defaultMessage: 'Specifies whether or not the X-axis labels are visible.', - }), - }, - }, - fn(input, args) { - return { - type: HEATMAP_GRID_FUNCTION, - ...args, - }; - }, -}; - -/** - * TODO check if it's possible to make a shared function - * based on the XY chart - */ -export const heatmapLegendConfig: ExpressionFunctionDefinition< - typeof LEGEND_FUNCTION, - null, - HeatmapLegendConfig, - LegendConfigResult -> = { - name: LEGEND_FUNCTION, - aliases: [], - type: LEGEND_FUNCTION, - help: `Configure the heatmap chart's legend`, - inputTypes: ['null'], - args: { - isVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.heatmapChart.legend.isVisible.help', { - defaultMessage: 'Specifies whether or not the legend is visible.', - }), - }, - position: { - types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: i18n.translate('xpack.lens.heatmapChart.legend.position.help', { - defaultMessage: 'Specifies the legend position.', - }), - }, - }, - fn(input, args) { - return { - type: LEGEND_FUNCTION, - ...args, - }; - }, -}; - -export const heatmap: ExpressionFunctionDefinition< - typeof FUNCTION_NAME, - LensMultiTable, - HeatmapExpressionArgs, - HeatmapRender -> = { - name: FUNCTION_NAME, - type: 'render', - help: i18n.translate('xpack.lens.heatmap.expressionHelpLabel', { - defaultMessage: 'Heatmap renderer', - }), - args: { - title: { - types: ['string'], - help: i18n.translate('xpack.lens.heatmap.titleLabel', { - defaultMessage: 'Title', - }), - }, - description: { - types: ['string'], - help: '', - }, - xAccessor: { - types: ['string'], - help: '', - }, - yAccessor: { - types: ['string'], - help: '', - }, - valueAccessor: { - types: ['string'], - help: '', - }, - shape: { - types: ['string'], - help: '', - }, - palette: { - default: `{theme "palette" default={system_palette name="default"} }`, - help: '', - types: ['palette'], - }, - legend: { - types: [LEGEND_FUNCTION], - help: i18n.translate('xpack.lens.heatmapChart.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - }, - gridConfig: { - types: [HEATMAP_GRID_FUNCTION], - help: i18n.translate('xpack.lens.heatmapChart.gridConfig.help', { - defaultMessage: 'Configure the heatmap layout.', - }), - }, - }, - inputTypes: ['lens_multitable'], - fn(data: LensMultiTable, args: HeatmapExpressionArgs) { - return { - type: 'render', - as: LENS_HEATMAP_RENDERER, - value: { - data, - args, - }, - }; - }, -}; +export { heatmapGridConfig, heatmapLegendConfig, heatmap } from '../../common/expressions'; export const getHeatmapRenderer = (dependencies: { formatFactory: Promise; diff --git a/x-pack/plugins/lens/public/heatmap_visualization/index.ts b/x-pack/plugins/lens/public/heatmap_visualization/index.ts index 4599bd8d2a208..11f9b907eb929 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/index.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/index.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { CoreSetup } from 'kibana/public'; -import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; -import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; +import type { CoreSetup } from 'kibana/public'; +import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; import { getTimeZone } from '../utils'; +import type { FormatFactory } from '../../common'; export interface HeatmapVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; diff --git a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts index c11078be6c8b9..d7443ea8fe43d 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts @@ -5,10 +5,10 @@ * 2.0. */ +import { Position } from '@elastic/charts'; import { getSuggestions } from './suggestions'; -import { HeatmapVisualizationState } from './types'; +import type { HeatmapVisualizationState } from './types'; import { HEATMAP_GRID_FUNCTION, LEGEND_FUNCTION } from './constants'; -import { Position } from '@elastic/charts'; describe('heatmap suggestions', () => { describe('rejects suggestions', () => { diff --git a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts index 5cddebe2cc230..3f27d5e81b507 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts @@ -8,8 +8,8 @@ import { partition } from 'lodash'; import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { Visualization } from '../types'; -import { HeatmapVisualizationState } from './types'; +import type { Visualization } from '../types'; +import type { HeatmapVisualizationState } from './types'; import { CHART_SHAPES, HEATMAP_GRID_FUNCTION, LEGEND_FUNCTION } from './constants'; export const getSuggestions: Visualization['getSuggestions'] = ({ diff --git a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx index 6fd863ba91936..c35143773551d 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx @@ -9,9 +9,9 @@ import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { VisualizationToolbarProps } from '../types'; +import type { VisualizationToolbarProps } from '../types'; import { LegendSettingsPopover } from '../shared_components'; -import { HeatmapVisualizationState } from './types'; +import type { HeatmapVisualizationState } from './types'; const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: string }> = [ { diff --git a/x-pack/plugins/lens/public/heatmap_visualization/types.ts b/x-pack/plugins/lens/public/heatmap_visualization/types.ts index 32e3079c951d4..0cf830bea609a 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/types.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/types.ts @@ -5,17 +5,12 @@ * 2.0. */ -import { Position } from '@elastic/charts'; -import { PaletteOutput } from '../../../../../src/plugins/charts/common'; -import { FormatFactory, LensBrushEvent, LensFilterEvent, LensMultiTable } from '../types'; -import { - CHART_SHAPES, - HEATMAP_GRID_FUNCTION, - LEGEND_FUNCTION, - LENS_HEATMAP_RENDERER, -} from './constants'; -import { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; -import { CustomPaletteParams } from '../shared_components'; +import type { PaletteOutput } from '../../../../../src/plugins/charts/common'; +import type { LensBrushEvent, LensFilterEvent } from '../types'; +import type { LensMultiTable, FormatFactory, CustomPaletteParams } from '../../common'; +import type { HeatmapGridConfigResult, HeatmapLegendConfigResult } from '../../common/expressions'; +import { CHART_SHAPES, LENS_HEATMAP_RENDERER } from './constants'; +import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; export type ChartShapes = typeof CHART_SHAPES[keyof typeof CHART_SHAPES]; @@ -24,7 +19,7 @@ export interface SharedHeatmapLayerState { xAccessor?: string; yAccessor?: string; valueAccessor?: string; - legend: LegendConfigResult; + legend: HeatmapLegendConfigResult; gridConfig: HeatmapGridConfigResult; } @@ -62,34 +57,3 @@ export type HeatmapRenderProps = HeatmapExpressionProps & { onSelectRange: (data: LensBrushEvent['data']) => void; paletteService: PaletteRegistry; }; - -export interface HeatmapLegendConfig { - /** - * Flag whether the legend should be shown. If there is just a single series, it will be hidden - */ - isVisible: boolean; - /** - * Position of the legend relative to the chart - */ - position: Position; -} - -export type LegendConfigResult = HeatmapLegendConfig & { type: typeof LEGEND_FUNCTION }; - -export interface HeatmapGridConfig { - // grid - strokeWidth?: number; - strokeColor?: string; - cellHeight?: number; - cellWidth?: number; - // cells - isCellLabelVisible: boolean; - // Y-axis - isYAxisLabelVisible: boolean; - yAxisLabelWidth?: number; - yAxisLabelColor?: string; - // X-axis - isXAxisLabelVisible: boolean; -} - -export type HeatmapGridConfigResult = HeatmapGridConfig & { type: typeof HEATMAP_GRID_FUNCTION }; diff --git a/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts b/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts index 316a3ef36d66c..6cbe27fbf323f 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts @@ -19,8 +19,8 @@ import { LEGEND_FUNCTION, } from './constants'; import { Position } from '@elastic/charts'; -import { HeatmapVisualizationState } from './types'; -import { DatasourcePublicAPI, Operation } from '../types'; +import type { HeatmapVisualizationState } from './types'; +import type { DatasourcePublicAPI, Operation } from '../types'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; function exampleState(): HeatmapVisualizationState { diff --git a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx b/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx index 12fe28f801ef2..716792805e1b5 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx @@ -12,8 +12,8 @@ import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { Ast } from '@kbn/interpreter/common'; import { Position } from '@elastic/charts'; import { PaletteRegistry } from '../../../../../src/plugins/charts/public'; -import { OperationMetadata, Visualization } from '../types'; -import { HeatmapVisualizationState } from './types'; +import type { OperationMetadata, Visualization } from '../types'; +import type { HeatmapVisualizationState } from './types'; import { getSuggestions } from './suggestions'; import { CHART_NAMES, @@ -27,9 +27,10 @@ import { } from './constants'; import { HeatmapToolbar } from './toolbar_component'; import { LensIconChartHeatmap } from '../assets/chart_heatmap'; -import { CustomPaletteParams, CUSTOM_PALETTE, getStopsForFixedMode } from '../shared_components'; +import { CUSTOM_PALETTE, getStopsForFixedMode } from '../shared_components'; import { HeatmapDimensionEditor } from './dimension_editor'; import { getSafePaletteParams } from './utils'; +import type { CustomPaletteParams } from '../../common'; const groupLabelForBar = i18n.translate('xpack.lens.heatmapVisualization.heatmapGroupLabel', { defaultMessage: 'Heatmap', diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 76afe7260a35a..84302f25d0a02 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -11,8 +11,13 @@ export type { EmbeddableComponentProps, TypedLensByValueInput, } from './embeddable/embeddable_component'; +export type { XYState } from './xy_visualization/types'; +export type { DataType, OperationMetadata } from './types'; export type { - XYState, + PieVisualizationState, + PieLayerState, + SharedPieLayerState, + MetricState, AxesSettingsConfig, XYLayerConfig, LegendConfig, @@ -21,15 +26,8 @@ export type { YAxisMode, XYCurveType, YConfig, -} from './xy_visualization/types'; -export type { DataType, OperationMetadata } from './types'; -export type { - PieVisualizationState, - PieLayerState, - SharedPieLayerState, -} from './pie_visualization/types'; +} from '../common/expressions'; export type { DatatableVisualizationState } from './datatable_visualization/visualization'; -export type { MetricState } from './metric_visualization/types'; export type { IndexPatternPersistedState, PersistedIndexPatternLayer, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx index 3965f992805b5..d6091557ce235 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx @@ -16,7 +16,7 @@ import { IndexPatternColumn } from '../indexpattern'; import { isColumnInvalid } from '../utils'; import { IndexPatternPrivateState } from '../types'; import { DimensionEditor } from './dimension_editor'; -import { DateRange } from '../../../common'; +import type { DateRange } from '../../../common'; import { getOperationSupportMatrix } from './operation_support'; export type IndexPatternDimensionTriggerProps = DatasourceDimensionTriggerProps & { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx index db0a42047a1b8..54eb3d48efccf 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx @@ -7,8 +7,9 @@ import './field_select.scss'; import { partition } from 'lodash'; -import React, { useMemo } from 'react'; +import React, { useMemo, useRef } from 'react'; import { i18n } from '@kbn/i18n'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; import { EuiComboBox, EuiFlexGroup, @@ -17,7 +18,6 @@ import { EuiComboBoxProps, } from '@elastic/eui'; import classNames from 'classnames'; -import { EuiHighlight } from '@elastic/eui'; import { OperationType } from '../indexpattern'; import { LensFieldIcon } from '../lens_field_icon'; import { DataType } from '../../types'; @@ -25,7 +25,7 @@ import { OperationSupportMatrix } from './operation_support'; import { IndexPattern, IndexPatternPrivateState } from '../types'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { fieldExists } from '../pure_helpers'; - +import { TruncatedLabel } from './truncated_label'; export interface FieldChoice { type: 'field'; field: string; @@ -45,6 +45,10 @@ export interface FieldSelectProps extends EuiComboBoxProps(null); + const [labelProps, setLabelProps] = React.useState<{ + width: number; + font: string; + }>({ + width: DEFAULT_COMBOBOX_WIDTH - COMBOBOX_PADDINGS, + font: DEFAULT_FONT, + }); - return ( - { + if (comboBoxRef.current) { + const current = { + ...labelProps, + width: comboBoxRef.current?.clientWidth - COMBOBOX_PADDINGS, + }; + if (shouldRecomputeAll) { + current.font = window.getComputedStyle(comboBoxRef.current).font; } - singleSelection={{ asPlainText: true }} - onChange={(choices) => { - if (choices.length === 0) { - onDeleteColumn?.(); - return; - } + setLabelProps(current); + } + }; - const choice = (choices[0].value as unknown) as FieldChoice; + useEffectOnce(() => { + if (comboBoxRef.current) { + computeStyles(undefined, true); + } + window.addEventListener('resize', computeStyles); + }); - if (choice.field !== selectedField) { - trackUiEvent('indexpattern_dimension_field_changed'); - onChoose(choice); + return ( +
+ { - return ( - - - - - - {option.label} - - - ); - }} - {...rest} - /> + singleSelection={{ asPlainText: true }} + onChange={(choices) => { + if (choices.length === 0) { + onDeleteColumn?.(); + return; + } + + const choice = (choices[0].value as unknown) as FieldChoice; + + if (choice.field !== selectedField) { + trackUiEvent('indexpattern_dimension_field_changed'); + onChoose(choice); + } + }} + renderOption={(option, searchValue) => { + return ( + + + + + + + + + ); + }} + {...rest} + /> +
); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx index 61e5da5931e88..7c611230683d3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx @@ -15,8 +15,8 @@ import { IndexPatternColumn, operationDefinitionMap, } from '../operations'; -import { unitSuffixesLong } from '../suffix_formatter'; -import { TimeScaleUnit } from '../time_scale'; +import type { TimeScaleUnit } from '../../../common/expressions'; +import { unitSuffixesLong } from '../../../common/suffix_formatter'; import { IndexPatternLayer } from '../types'; export function setTimeScaling( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/truncated_label.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/truncated_label.test.tsx new file mode 100644 index 0000000000000..b558afddd689e --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/truncated_label.test.tsx @@ -0,0 +1,78 @@ +/* + * 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 { mount } from 'enzyme'; +import 'jest-canvas-mock'; +import { TruncatedLabel } from './truncated_label'; + +describe('truncated_label', () => { + const defaultProps = { + font: '14px Inter', + // jest-canvas-mock mocks measureText as the number of string characters, thats why the width is so low + width: 30, + search: '', + label: 'example_field', + }; + it('displays passed label if shorter than passed labelLength', () => { + const wrapper = mount(); + expect(wrapper.text()).toEqual('example_field'); + }); + it('middle truncates label', () => { + const wrapper = mount( + + ); + expect(wrapper.text()).toEqual('example_….subcategory.subfield'); + }); + describe('with search value passed', () => { + it('constructs truncated label when searching for the string of index = 0', () => { + const wrapper = mount( + + ); + expect(wrapper.text()).toEqual('example_space.example_field.s…'); + expect(wrapper.find('mark').text()).toEqual('example_space'); + }); + it('constructs truncated label when searching for the string in the middle', () => { + const wrapper = mount( + + ); + expect(wrapper.text()).toEqual('…ample_field.subcategory.subf…'); + expect(wrapper.find('mark').text()).toEqual('ample_field'); + }); + it('constructs truncated label when searching for the string at the end of the label', () => { + const wrapper = mount( + + ); + expect(wrapper.text()).toEqual('…le_field.subcategory.subfield'); + expect(wrapper.find('mark').text()).toEqual('subf'); + }); + + it('constructs truncated label when searching for the string longer than the truncated width and highlights the whole content', () => { + const wrapper = mount( + + ); + expect(wrapper.text()).toEqual('…ample_space.example_field.su…'); + expect(wrapper.find('mark').text()).toEqual('…ample_space.example_field.su…'); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/truncated_label.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/truncated_label.tsx new file mode 100644 index 0000000000000..47b1313a74c4e --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/truncated_label.tsx @@ -0,0 +1,86 @@ +/* + * 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, { useMemo } from 'react'; +import { EuiMark } from '@elastic/eui'; +import { EuiHighlight } from '@elastic/eui'; + +const createContext = () => + document.createElement('canvas').getContext('2d') as CanvasRenderingContext2D; + +// extracted from getTextWidth for performance +const context = createContext(); + +const getTextWidth = (text: string, font: string) => { + const ctx = context ?? createContext(); + ctx.font = font; + const metrics = ctx.measureText(text); + return metrics.width; +}; + +const truncateLabel = ( + width: number, + font: string, + label: string, + approximateLength: number, + labelFn: (label: string, length: number) => string +) => { + let output = labelFn(label, approximateLength); + while (getTextWidth(output, font) > width) { + approximateLength = approximateLength - 1; + output = labelFn(label, approximateLength); + } + return output; +}; + +export const TruncatedLabel = React.memo(function TruncatedLabel({ + label, + width, + search, + font, +}: { + label: string; + search: string; + width: number; + font: string; +}) { + const textWidth = useMemo(() => getTextWidth(label, font), [label, font]); + + if (textWidth < width) { + return {label}; + } + + const searchPosition = label.indexOf(search); + const approximateLen = Math.round((width * label.length) / textWidth); + const separator = `…`; + let separatorsLength = separator.length; + let labelFn; + + if (!search || searchPosition === -1) { + labelFn = (text: string, length: number) => + `${text.substr(0, 8)}${separator}${text.substr(text.length - (length - 8))}`; + } else if (searchPosition === 0) { + // search phrase at the beginning + labelFn = (text: string, length: number) => `${text.substr(0, length)}${separator}`; + } else if (approximateLen > label.length - searchPosition) { + // search phrase close to the end or at the end + labelFn = (text: string, length: number) => `${separator}${text.substr(text.length - length)}`; + } else { + // search phrase is in the middle + labelFn = (text: string, length: number) => + `${separator}${text.substr(searchPosition, length)}${separator}`; + separatorsLength = 2 * separator.length; + } + + const outputLabel = truncateLabel(width, font, label, approximateLen, labelFn); + + return search.length < outputLabel.length - separatorsLength ? ( + {outputLabel} + ) : ( + {outputLabel} + ); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts index f8bc84643bcab..35afd28c0f1ab 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts @@ -43,14 +43,14 @@ export class IndexPatternDatasource { renameColumns, formatColumn, counterRate, - getTimeScaleFunction, + timeScale, getSuffixFormatter, } = await import('../async_services'); return core .getStartServices() .then(([coreStart, { data, indexPatternFieldEditor, uiActions }]) => { data.fieldFormats.register([getSuffixFormatter(data.fieldFormats.deserialize)]); - expressions.registerFunction(getTimeScaleFunction(data)); + expressions.registerFunction(timeScale); expressions.registerFunction(counterRate); expressions.registerFunction(renameColumns); expressions.registerFunction(formatColumn); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 34be770a7f50d..2cbe801a5b7b8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -69,11 +69,15 @@ export function columnToOperation(column: IndexPatternColumn, uniqueLabel?: stri }; } -export * from './rename_columns'; -export * from './format_column'; -export * from './time_scale'; -export * from './counter_rate'; -export * from './suffix_formatter'; +export { + CounterRateArgs, + ExpressionFunctionCounterRate, + counterRate, +} from '../../common/expressions'; +export { FormatColumnArgs, supportedFormats, formatColumn } from '../../common/expressions'; +export { getSuffixFormatter, unitSuffixesLong } from '../../common/suffix_formatter'; +export { timeScale, TimeScaleArgs } from '../../common/expressions'; +export { renameColumns } from '../../common/expressions'; export function getIndexPatternDatasource({ core, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts index 87116f71919b5..34b33d35d4139 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionAST } from '@kbn/interpreter/common'; import memoizeOne from 'memoize-one'; -import type { TimeScaleUnit } from '../../../time_scale'; +import type { TimeScaleUnit } from '../../../../../common/expressions'; import type { IndexPattern, IndexPatternLayer } from '../../../types'; import { adjustTimeScaleLabelSuffix } from '../../time_scale_utils'; import type { ReferenceBasedIndexPatternColumn } from '../column_types'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts index ae606a5851665..15bd7d4242b92 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts @@ -7,7 +7,7 @@ import { Query } from 'src/plugins/data/public'; import type { Operation } from '../../../types'; -import { TimeScaleUnit } from '../../time_scale'; +import type { TimeScaleUnit } from '../../../../common/expressions'; import type { OperationType } from '../definitions'; export interface BaseIndexPatternColumn extends Operation { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index fd21258002808..cb737d694295d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -17,7 +17,7 @@ import { RangeEditor } from './range_editor'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { updateColumnParam } from '../../layer_helpers'; -import { supportedFormats } from '../../../format_column'; +import { supportedFormats } from '../../../../../common/expressions'; import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; import { IndexPattern, IndexPatternField } from '../../../types'; import { getInvalidFieldMessage, isValidNumber } from '../helpers'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index b5b1960b7b769..1e0d0792e132a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -32,7 +32,7 @@ import { getSortScoreByPriority } from './operations'; import { generateId } from '../../id_generator'; import { ReferenceBasedIndexPatternColumn } from './definitions/column_types'; import { FormulaIndexPatternColumn, regenerateLayerFromAst } from './definitions/formula'; -import { TimeScaleUnit } from '../time_scale'; +import type { TimeScaleUnit } from '../../../common/expressions'; interface ColumnAdvancedParams { filter?: Query | undefined; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts index 152fcaa457c3b..dbdfd5c564125 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts @@ -6,7 +6,7 @@ */ import type { IndexPatternLayer } from '../types'; -import type { TimeScaleUnit } from '../time_scale'; +import type { TimeScaleUnit } from '../../../common/expressions'; import type { IndexPatternColumn } from './definitions'; import { adjustTimeScaleLabelSuffix, adjustTimeScaleOnOtherColumnChange } from './time_scale_utils'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts index a0b61060b9f3a..a6c056933f022 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { unitSuffixesLong } from '../suffix_formatter'; -import type { TimeScaleUnit } from '../time_scale'; +import { unitSuffixesLong } from '../../../common/suffix_formatter'; +import type { TimeScaleUnit } from '../../../common/expressions'; import type { IndexPatternLayer } from '../types'; import type { IndexPatternColumn } from './definitions'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts b/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts deleted file mode 100644 index 368e06110efc9..0000000000000 --- a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts +++ /dev/null @@ -1,150 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import moment from 'moment-timezone'; -import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, Datatable } from 'src/plugins/expressions/public'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { search } from '../../../../../src/plugins/data/public'; -import { buildResultColumns } from '../../../../../src/plugins/expressions/common'; - -export type TimeScaleUnit = 's' | 'm' | 'h' | 'd'; - -export interface TimeScaleArgs { - dateColumnId: string; - inputColumnId: string; - outputColumnId: string; - targetUnit: TimeScaleUnit; - outputColumnName?: string; -} - -const unitInMs: Record = { - s: 1000, - m: 1000 * 60, - h: 1000 * 60 * 60, - d: 1000 * 60 * 60 * 24, -}; - -export function getTimeScaleFunction(data: DataPublicPluginStart) { - const timeScale: ExpressionFunctionDefinition< - 'lens_time_scale', - Datatable, - TimeScaleArgs, - Promise - > = { - name: 'lens_time_scale', - type: 'datatable', - help: '', - args: { - dateColumnId: { - types: ['string'], - help: '', - required: true, - }, - inputColumnId: { - types: ['string'], - help: '', - required: true, - }, - outputColumnId: { - types: ['string'], - help: '', - required: true, - }, - outputColumnName: { - types: ['string'], - help: '', - }, - targetUnit: { - types: ['string'], - options: ['s', 'm', 'h', 'd'], - help: '', - required: true, - }, - }, - inputTypes: ['datatable'], - async fn( - input, - { dateColumnId, inputColumnId, outputColumnId, outputColumnName, targetUnit }: TimeScaleArgs - ) { - const dateColumnDefinition = input.columns.find((column) => column.id === dateColumnId); - - if (!dateColumnDefinition) { - throw new Error( - i18n.translate('xpack.lens.functions.timeScale.dateColumnMissingMessage', { - defaultMessage: 'Specified dateColumnId {columnId} does not exist.', - values: { - columnId: dateColumnId, - }, - }) - ); - } - - const resultColumns = buildResultColumns( - input, - outputColumnId, - inputColumnId, - outputColumnName, - { allowColumnOverwrite: true } - ); - - if (!resultColumns) { - return input; - } - - const targetUnitInMs = unitInMs[targetUnit]; - const timeInfo = search.aggs.getDateHistogramMetaDataByDatatableColumn(dateColumnDefinition); - const intervalDuration = timeInfo?.interval && search.aggs.parseInterval(timeInfo.interval); - - if (!timeInfo || !intervalDuration) { - throw new Error( - i18n.translate('xpack.lens.functions.timeScale.timeInfoMissingMessage', { - defaultMessage: 'Could not fetch date histogram information', - }) - ); - } - // the datemath plugin always parses dates by using the current default moment time zone. - // to use the configured time zone, we are switching just for the bounds calculation. - const defaultTimezone = moment().zoneName(); - moment.tz.setDefault(timeInfo.timeZone); - - const timeBounds = - timeInfo.timeRange && data.query.timefilter.timefilter.calculateBounds(timeInfo.timeRange); - - const result = { - ...input, - columns: resultColumns, - rows: input.rows.map((row) => { - const newRow = { ...row }; - - let startOfBucket = moment(row[dateColumnId]); - let endOfBucket = startOfBucket.clone().add(intervalDuration); - if (timeBounds && timeBounds.min) { - startOfBucket = moment.max(startOfBucket, timeBounds.min); - } - if (timeBounds && timeBounds.max) { - endOfBucket = moment.min(endOfBucket, timeBounds.max); - } - const bucketSize = endOfBucket.diff(startOfBucket); - const factor = bucketSize / targetUnitInMs; - - const currentValue = newRow[inputColumnId]; - if (currentValue != null) { - newRow[outputColumnId] = Number(currentValue) / factor; - } - - return newRow; - }), - }; - // reset default moment timezone - moment.tz.setDefault(defaultTimezone); - - return result; - }, - }; - return timeScale; -} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index b6f5c364e2d04..69b60711e5186 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -22,9 +22,10 @@ import { import { IndexPatternColumn } from './indexpattern'; import { operationDefinitionMap } from './operations'; import { IndexPattern, IndexPatternPrivateState, IndexPatternLayer } from './types'; -import { OriginalColumn } from './rename_columns'; import { dateHistogramOperation } from './operations/definitions'; +type OriginalColumn = { id: string } & IndexPatternColumn; + function getExpressionForLayer( layer: IndexPatternLayer, indexPattern: IndexPattern, diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx index 27a7659b1c817..21c68a9fe1d82 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import { metricChart, MetricChart } from './expression'; -import { LensMultiTable } from '../types'; +import { MetricChart, metricChart } from './expression'; +import { MetricConfig } from '../../common/expressions'; import React from 'react'; import { shallow } from 'enzyme'; -import { MetricConfig } from './types'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { IFieldFormat } from '../../../../../src/plugins/data/public'; +import type { LensMultiTable } from '../../common'; function sampleArgs() { const data: LensMultiTable = { diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.tsx index cf6921b2ca579..5a1e0d7fb5bdf 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.tsx @@ -9,75 +9,19 @@ import './expression.scss'; import { I18nProvider } from '@kbn/i18n/react'; import React from 'react'; import ReactDOM from 'react-dom'; -import { - ExpressionFunctionDefinition, +import type { ExpressionRenderDefinition, IInterpreterRenderHandlers, } from '../../../../../src/plugins/expressions/public'; -import { MetricConfig } from './types'; -import { FormatFactory, LensMultiTable } from '../types'; import { AutoScale } from './auto_scale'; import { VisualizationContainer } from '../visualization_container'; import { EmptyPlaceholder } from '../shared_components'; import { LensIconChartMetric } from '../assets/chart_metric'; +import type { FormatFactory } from '../../common'; +import type { MetricChartProps } from '../../common/expressions'; -export interface MetricChartProps { - data: LensMultiTable; - args: MetricConfig; -} - -export interface MetricRender { - type: 'render'; - as: 'lens_metric_chart_renderer'; - value: MetricChartProps; -} - -export const metricChart: ExpressionFunctionDefinition< - 'lens_metric_chart', - LensMultiTable, - Omit, - MetricRender -> = { - name: 'lens_metric_chart', - type: 'render', - help: 'A metric chart', - args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, - metricTitle: { - types: ['string'], - help: 'The title of the metric shown.', - }, - accessor: { - types: ['string'], - help: 'The column whose value is being displayed', - }, - mode: { - types: ['string'], - options: ['reduced', 'full'], - default: 'full', - help: - 'The display mode of the chart - reduced will only show the metric itself without min size', - }, - }, - inputTypes: ['lens_multitable'], - fn(data, args) { - return { - type: 'render', - as: 'lens_metric_chart_renderer', - value: { - data, - args, - }, - } as MetricRender; - }, -}; +export { metricChart } from '../../common/expressions'; +export type { MetricState, MetricConfig } from '../../common/expressions'; export const getMetricChartRenderer = ( formatFactory: Promise diff --git a/x-pack/plugins/lens/public/metric_visualization/index.ts b/x-pack/plugins/lens/public/metric_visualization/index.ts index c94063ed0bd74..484dc6140ecf2 100644 --- a/x-pack/plugins/lens/public/metric_visualization/index.ts +++ b/x-pack/plugins/lens/public/metric_visualization/index.ts @@ -5,9 +5,10 @@ * 2.0. */ -import { CoreSetup } from 'kibana/public'; -import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; +import type { CoreSetup } from 'kibana/public'; +import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { FormatFactory } from '../../common'; export interface MetricVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts b/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts index e7f510f106fff..d07dccb770196 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts +++ b/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts @@ -6,7 +6,7 @@ */ import { SuggestionRequest, VisualizationSuggestion, TableSuggestion } from '../types'; -import { MetricState } from './types'; +import type { MetricState } from '../../common/expressions'; import { LensIconChartMetric } from '../assets/chart_metric'; /** diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts index 2882d9c4c0246..2c359d139bb3b 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts @@ -6,7 +6,7 @@ */ import { metricVisualization } from './visualization'; -import { MetricState } from './types'; +import type { MetricState } from '../../common/expressions'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { generateId } from '../id_generator'; import { DatasourcePublicAPI, FramePublicAPI } from '../types'; diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx index 49565f53bda36..d312030b5a490 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx @@ -10,7 +10,7 @@ import { Ast } from '@kbn/interpreter/target/common'; import { getSuggestions } from './metric_suggestions'; import { LensIconChartMetric } from '../assets/chart_metric'; import { Visualization, OperationMetadata, DatasourcePublicAPI } from '../types'; -import { MetricState } from './types'; +import type { MetricState } from '../../common/expressions'; const toExpression = ( state: MetricState, diff --git a/x-pack/plugins/lens/public/pie_visualization/expression.tsx b/x-pack/plugins/lens/public/pie_visualization/expression.tsx index 208ce746f4b9d..ce36f88b2805e 100644 --- a/x-pack/plugins/lens/public/pie_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/expression.tsx @@ -8,108 +8,18 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; -import { Position } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; -import { +import type { IInterpreterRenderHandlers, ExpressionRenderDefinition, - ExpressionFunctionDefinition, } from 'src/plugins/expressions/public'; -import { LensMultiTable, FormatFactory, LensFilterEvent } from '../types'; -import { PieExpressionProps, PieExpressionArgs } from './types'; +import type { LensFilterEvent } from '../types'; import { PieComponent } from './render_function'; -import { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; +import type { FormatFactory } from '../../common'; +import type { PieExpressionProps } from '../../common/expressions'; +import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; -export interface PieRender { - type: 'render'; - as: 'lens_pie_renderer'; - value: PieExpressionProps; -} - -export const pie: ExpressionFunctionDefinition< - 'lens_pie', - LensMultiTable, - PieExpressionArgs, - PieRender -> = { - name: 'lens_pie', - type: 'render', - help: i18n.translate('xpack.lens.pie.expressionHelpLabel', { - defaultMessage: 'Pie renderer', - }), - args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, - groups: { - types: ['string'], - multi: true, - help: '', - }, - metric: { - types: ['string'], - help: '', - }, - shape: { - types: ['string'], - options: ['pie', 'donut', 'treemap'], - help: '', - }, - hideLabels: { - types: ['boolean'], - help: '', - }, - numberDisplay: { - types: ['string'], - options: ['hidden', 'percent', 'value'], - help: '', - }, - categoryDisplay: { - types: ['string'], - options: ['default', 'inside', 'hide'], - help: '', - }, - legendDisplay: { - types: ['string'], - options: ['default', 'show', 'hide'], - help: '', - }, - nestedLegend: { - types: ['boolean'], - help: '', - }, - legendPosition: { - types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: '', - }, - percentDecimals: { - types: ['number'], - help: '', - }, - palette: { - default: `{theme "palette" default={system_palette name="default"} }`, - help: '', - types: ['palette'], - }, - }, - inputTypes: ['lens_multitable'], - fn(data: LensMultiTable, args: PieExpressionArgs) { - return { - type: 'render', - as: 'lens_pie_renderer', - value: { - data, - args, - }, - }; - }, -}; +export { pie } from '../../common/expressions'; export const getPieRenderer = (dependencies: { formatFactory: Promise; diff --git a/x-pack/plugins/lens/public/pie_visualization/index.ts b/x-pack/plugins/lens/public/pie_visualization/index.ts index 9f4a176ef8aa8..aa74eb5088ea3 100644 --- a/x-pack/plugins/lens/public/pie_visualization/index.ts +++ b/x-pack/plugins/lens/public/pie_visualization/index.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { CoreSetup } from 'src/core/public'; -import { ExpressionsSetup } from 'src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; -import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; +import type { CoreSetup } from 'src/core/public'; +import type { ExpressionsSetup } from 'src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; +import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; +import type { FormatFactory } from '../../common'; export interface PieVisualizationPluginSetupPlugins { editorFrame: EditorFrameSetup; diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx index a3a10b803fcd3..adef7188d12d0 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx @@ -16,9 +16,9 @@ import { Chart, } from '@elastic/charts'; import { shallow } from 'enzyme'; -import { LensMultiTable } from '../types'; +import type { LensMultiTable } from '../../common'; +import type { PieExpressionArgs } from '../../common/expressions'; import { PieComponent } from './render_function'; -import { PieExpressionArgs } from './types'; import { VisualizationContainer } from '../visualization_container'; import { EmptyPlaceholder } from '../shared_components'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index b161a81a835f1..ac0aa6cd4b1f1 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -24,10 +24,11 @@ import { ElementClickListener, } from '@elastic/charts'; import { RenderMode } from 'src/plugins/expressions'; -import { FormatFactory, LensFilterEvent } from '../types'; +import type { LensFilterEvent } from '../types'; import { VisualizationContainer } from '../visualization_container'; import { CHART_NAMES, DEFAULT_PERCENT_DECIMALS } from './constants'; -import { PieExpressionProps } from './types'; +import type { FormatFactory } from '../../common'; +import type { PieExpressionProps } from '../../common/expressions'; import { getSliceValue, getFilterContext } from './render_helpers'; import { EmptyPlaceholder } from '../shared_components'; import './visualization.scss'; diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts b/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts index a527a3c864543..36470fa3d74cf 100644 --- a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts +++ b/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts @@ -8,7 +8,7 @@ import { PaletteOutput } from 'src/plugins/charts/public'; import { DataType, SuggestionRequest } from '../types'; import { suggestions } from './suggestions'; -import { PieVisualizationState } from './types'; +import type { PieVisualizationState } from '../../common/expressions'; describe('suggestions', () => { describe('pie', () => { diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts b/x-pack/plugins/lens/public/pie_visualization/suggestions.ts index 644f0a0cd8aaf..22be8e3357bbb 100644 --- a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts +++ b/x-pack/plugins/lens/public/pie_visualization/suggestions.ts @@ -7,8 +7,8 @@ import { partition } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { SuggestionRequest, VisualizationSuggestion } from '../types'; -import { PieVisualizationState } from './types'; +import type { SuggestionRequest, VisualizationSuggestion } from '../types'; +import type { PieVisualizationState } from '../../common/expressions'; import { CHART_NAMES, MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS } from './constants'; function shouldReject({ table, keptLayerIds }: SuggestionRequest) { diff --git a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts index 14f1fe81c7bd6..7ee26383cebbf 100644 --- a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts @@ -9,7 +9,7 @@ import { Ast } from '@kbn/interpreter/common'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { Operation, DatasourcePublicAPI } from '../types'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; -import { PieVisualizationState } from './types'; +import type { PieVisualizationState } from '../../common/expressions'; export function toExpression( state: PieVisualizationState, diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx index a2596f7ce1e0f..5da69e47f861c 100644 --- a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx @@ -15,10 +15,10 @@ import { EuiRange, EuiHorizontalRule, } from '@elastic/eui'; -import { Position } from '@elastic/charts'; -import { PaletteRegistry } from 'src/plugins/charts/public'; +import type { Position } from '@elastic/charts'; +import type { PaletteRegistry } from 'src/plugins/charts/public'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; -import { PieVisualizationState, SharedPieLayerState } from './types'; +import type { PieVisualizationState, SharedPieLayerState } from '../../common/expressions'; import { VisualizationDimensionEditorProps, VisualizationToolbarProps } from '../types'; import { ToolbarPopover, LegendSettingsPopover, useDebouncedValue } from '../shared_components'; import { PalettePicker } from '../shared_components'; diff --git a/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts b/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts index 2a961cef315bf..07a4161e7d239 100644 --- a/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts @@ -6,7 +6,7 @@ */ import { getPieVisualization } from './visualization'; -import { PieVisualizationState } from './types'; +import type { PieVisualizationState } from '../../common/expressions'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; jest.mock('../id_generator'); diff --git a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx index c82fdb2766f7e..5d75d82220d1f 100644 --- a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx @@ -9,10 +9,10 @@ import React from 'react'; import { render } from 'react-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; -import { PaletteRegistry } from 'src/plugins/charts/public'; -import { Visualization, OperationMetadata, AccessorConfig } from '../types'; +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { Visualization, OperationMetadata, AccessorConfig } from '../types'; import { toExpression, toPreviewExpression } from './to_expression'; -import { PieLayerState, PieVisualizationState } from './types'; +import type { PieLayerState, PieVisualizationState } from '../../common/expressions'; import { suggestions } from './suggestions'; import { CHART_NAMES, MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS } from './constants'; import { DimensionEditor, PieToolbar } from './toolbar'; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_stops.tsx b/x-pack/plugins/lens/public/shared_components/coloring/color_stops.tsx index 37197b232ddf5..1431e6ad135be 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_stops.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_stops.tsx @@ -22,7 +22,7 @@ import useUnmount from 'react-use/lib/useUnmount'; import { DEFAULT_COLOR } from './constants'; import { getDataMinMax, getStepValue, isValidColor } from './utils'; import { TooltipWrapper, useDebouncedValue } from '../index'; -import { ColorStop, CustomPaletteParams } from './types'; +import type { ColorStop, CustomPaletteParams } from '../../../common'; const idGeneratorFn = htmlIdGenerator(); diff --git a/x-pack/plugins/lens/public/shared_components/coloring/constants.ts b/x-pack/plugins/lens/public/shared_components/coloring/constants.ts index 5e6fc207656ac..29b50d3aee22d 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/constants.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/constants.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RequiredPaletteParamTypes } from './types'; +import type { RequiredPaletteParamTypes } from '../../../common'; export const DEFAULT_PALETTE_NAME = 'positive'; export const FIXED_PROGRESSION = 'fixed' as const; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/index.ts b/x-pack/plugins/lens/public/shared_components/coloring/index.ts index 0ad831603ab95..7cbf79ac43b1e 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/index.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/index.ts @@ -8,6 +8,5 @@ export { CustomizablePalette } from './palette_configuration'; export { PalettePanelContainer } from './palette_panel_container'; export { CustomStops } from './color_stops'; -export * from './types'; export * from './utils'; export * from './constants'; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.test.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.test.tsx index e36e817b3d714..ad1755bdbe85c 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.test.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.test.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { EuiColorPalettePickerPaletteProps } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test/jest'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; +import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; import { ReactWrapper } from 'enzyme'; -import { CustomPaletteParams } from './types'; +import type { CustomPaletteParams } from '../../../common'; import { applyPaletteParams } from './utils'; import { CustomizablePalette } from './palette_configuration'; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx index 993bf4f78dd20..bc6a590db0cb7 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx @@ -6,7 +6,7 @@ */ import React, { FC } from 'react'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; +import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; import { EuiFormRow, htmlIdGenerator, @@ -26,7 +26,7 @@ import './palette_configuration.scss'; import { CustomStops } from './color_stops'; import { defaultPaletteParams, CUSTOM_PALETTE, DEFAULT_COLOR_STEPS } from './constants'; -import { CustomPaletteParams, RequiredPaletteParamTypes } from './types'; +import type { CustomPaletteParams, RequiredPaletteParamTypes } from '../../../common'; import { getColorStops, getPaletteStops, diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx index 1371fbe73ef84..583d6e25ed4e2 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx @@ -7,6 +7,7 @@ import './palette_panel_container.scss'; +import { i18n } from '@kbn/i18n'; import React, { useState, useEffect, MutableRefObject } from 'react'; import { EuiFlyoutHeader, @@ -21,8 +22,6 @@ import { EuiPortal, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - export function PalettePanelContainer({ isOpen, handleClose, diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_picker.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_picker.tsx index 164ed9bf067a6..2a415cd178925 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_picker.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_picker.tsx @@ -13,9 +13,9 @@ import { DEFAULT_COLOR_STEPS, FIXED_PROGRESSION, defaultPaletteParams, -} from '../../shared_components/coloring/constants'; -import { CustomPaletteParams } from '../../shared_components/coloring/types'; -import { getStopsForFixedMode } from '../../shared_components/coloring/utils'; +} from './constants'; +import type { CustomPaletteParams } from '../../../common'; +import { getStopsForFixedMode } from './utils'; function getCustomPaletteConfig( palettes: PaletteRegistry, diff --git a/x-pack/plugins/lens/public/shared_components/coloring/types.ts b/x-pack/plugins/lens/public/shared_components/coloring/types.ts deleted file mode 100644 index d9a8edf0ccb62..0000000000000 --- a/x-pack/plugins/lens/public/shared_components/coloring/types.ts +++ /dev/null @@ -1,25 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -export interface ColorStop { - color: string; - stop: number; -} - -export interface CustomPaletteParams { - name?: string; - reverse?: boolean; - rangeType?: 'number' | 'percent'; - continuity?: 'above' | 'below' | 'all' | 'none'; - progression?: 'fixed'; - rangeMin?: number; - rangeMax?: number; - stops?: ColorStop[]; - colorStops?: ColorStop[]; - steps?: number; -} - -export type RequiredPaletteParamTypes = Required; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/utils.ts b/x-pack/plugins/lens/public/shared_components/coloring/utils.ts index 9c42ce5013b9b..8cd0a6cf49001 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/utils.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/utils.ts @@ -9,7 +9,7 @@ import chroma from 'chroma-js'; import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; import { euiLightVars, euiDarkVars } from '@kbn/ui-shared-deps/theme'; import { isColorDark } from '@elastic/eui'; -import { Datatable } from 'src/plugins/expressions/public'; +import type { Datatable } from 'src/plugins/expressions/public'; import { CUSTOM_PALETTE, defaultPaletteParams, @@ -17,7 +17,7 @@ import { DEFAULT_MAX_STOP, DEFAULT_MIN_STOP, } from './constants'; -import { CustomPaletteParams, ColorStop } from './types'; +import type { CustomPaletteParams, ColorStop } from '../../../common'; /** * Some name conventions here: diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index cb47dcf6ec388..3d87d234ae986 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -16,11 +16,10 @@ import { ExpressionRendererEvent, IInterpreterRenderHandlers, Datatable, - SerializedFieldFormat, } from '../../../../src/plugins/expressions/public'; import { DraggingIdentifier, DragDropIdentifier, DragContextState } from './drag_drop'; import { DateRange } from '../common'; -import { Query, Filter, IFieldFormat } from '../../../../src/plugins/data/public'; +import { Query, Filter } from '../../../../src/plugins/data/public'; import { VisualizeFieldContext } from '../../../../src/plugins/ui_actions/public'; import { RangeSelectContext, ValueClickContext } from '../../../../src/plugins/embeddable/public'; import { @@ -37,8 +36,6 @@ import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; export type ErrorCallback = (e: { message: string }) => void; -export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; - export interface PublicAPIProps { state: T; layerId: string; @@ -387,15 +384,6 @@ export interface OperationMetadata { // introduce a raw document datasource, this should be considered here. } -export interface LensMultiTable { - type: 'lens_multitable'; - tables: Record; - dateRange?: { - fromDate: Date; - toDate: Date; - }; -} - export interface VisualizationConfigProps { layerId: string; frame: Pick; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts index b82afeb1b7d1d..873827700d6e8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { LayerArgs } from './types'; +import { LayerArgs } from '../../common/expressions'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import { getAxesConfiguration } from './axes_configuration'; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index 58a80ad0fed37..83d86eb410b19 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { AxisExtentConfig, XYLayerConfig } from './types'; +import { FormatFactory } from '../../common'; +import { AxisExtentConfig, XYLayerConfig } from '../../common/expressions'; import { Datatable, SerializedFieldFormat } from '../../../../../src/plugins/expressions/public'; import { IFieldFormat } from '../../../../../src/plugins/data/public'; @@ -33,7 +34,7 @@ export function getAxesConfiguration( layers: XYLayerConfig[], shouldRotate: boolean, tables?: Record, - formatFactory?: (mapping: SerializedFieldFormat) => IFieldFormat + formatFactory?: FormatFactory ): GroupsConfiguration { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; right: FormattedMetric[] } = { auto: [], diff --git a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx index 3c1cecc3c5a44..2285cd1a7a43a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx @@ -20,7 +20,7 @@ import { EuiFieldNumber, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { XYLayerConfig, AxesSettingsConfig, AxisExtentConfig } from './types'; +import { XYLayerConfig, AxesSettingsConfig, AxisExtentConfig } from '../../common/expressions'; import { ToolbarPopover, useDebouncedValue } from '../shared_components'; import { isHorizontalChart } from './state_helpers'; import { EuiIconAxisBottom } from '../assets/axis_bottom'; diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts index 04a64d7ff5c93..390eded97d705 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { FormatFactory, LensMultiTable } from '../types'; import { getColorAssignments } from './color_assignment'; -import { LayerArgs } from './types'; +import type { FormatFactory, LensMultiTable } from '../../common'; +import type { LayerArgs } from '../../common/expressions'; describe('color_assignment', () => { const layers: LayerArgs[] = [ diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index ef0c350f20961..1e00d821d9b30 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -6,11 +6,12 @@ */ import { uniq, mapValues } from 'lodash'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; -import { Datatable } from 'src/plugins/expressions'; -import { AccessorConfig, FormatFactory, FramePublicAPI } from '../types'; +import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; +import type { Datatable } from 'src/plugins/expressions'; +import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; -import { XYLayerConfig } from './types'; +import type { FormatFactory } from '../../common'; +import type { XYLayerConfig } from '../../common/expressions'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx index e2066584f66b6..291b536210de5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx @@ -22,29 +22,24 @@ import { LayoutDirection, } from '@elastic/charts'; import { PaletteOutput } from 'src/plugins/charts/public'; +import { calculateMinInterval, XYChart, XYChartRenderProps, xyChart } from './expression'; +import type { LensMultiTable } from '../../common'; import { - calculateMinInterval, - xyChart, - XYChart, - XYChartProps, - XYChartRenderProps, -} from './expression'; -import { LensMultiTable } from '../types'; -import { Datatable, DatatableRow } from '../../../../../src/plugins/expressions/public'; -import React from 'react'; -import { shallow } from 'enzyme'; -import { + layerConfig, + legendConfig, + tickLabelsConfig, + gridlinesConfig, XYArgs, LegendConfig, - legendConfig, - layerConfig, LayerArgs, AxesSettingsConfig, - tickLabelsConfig, - gridlinesConfig, + XYChartProps, labelsOrientationConfig, LabelsOrientationConfig, -} from './types'; +} from '../../common/expressions'; +import { Datatable, DatatableRow } from '../../../../../src/plugins/expressions/public'; +import React from 'react'; +import { shallow } from 'enzyme'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { mountWithIntl } from '@kbn/test/jest'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; @@ -283,18 +278,18 @@ const createArgsWithLayers = (layers: LayerArgs[] = [sampleLayer]): XYArgs => ({ yLeft: false, yRight: false, }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, labelsOrientation: { type: 'lens_xy_labelsOrientationConfig', x: 0, yLeft: -90, yRight: -45, }, + gridlinesVisibilitySettings: { + type: 'lens_xy_gridlinesConfig', + x: true, + yLeft: false, + yRight: false, + }, yLeftExtent: { mode: 'full', type: 'lens_xy_axisExtentConfig', @@ -379,32 +374,32 @@ describe('xy_expression', () => { }); }); - test('labelsOrientationConfig produces the correct arguments', () => { - const args: LabelsOrientationConfig = { - x: 0, - yLeft: -90, - yRight: -45, + test('gridlinesConfig produces the correct arguments', () => { + const args: AxesSettingsConfig = { + x: true, + yLeft: false, + yRight: false, }; - const result = labelsOrientationConfig.fn(null, args, createMockExecutionContext()); + const result = gridlinesConfig.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ - type: 'lens_xy_labelsOrientationConfig', + type: 'lens_xy_gridlinesConfig', ...args, }); }); - test('gridlinesConfig produces the correct arguments', () => { - const args: AxesSettingsConfig = { - x: true, - yLeft: false, - yRight: false, + test('labelsOrientationConfig produces the correct arguments', () => { + const args: LabelsOrientationConfig = { + x: 0, + yLeft: -90, + yRight: -45, }; - const result = gridlinesConfig.fn(null, args, createMockExecutionContext()); + const result = labelsOrientationConfig.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ - type: 'lens_xy_gridlinesConfig', + type: 'lens_xy_labelsOrientationConfig', ...args, }); }); @@ -1968,74 +1963,74 @@ describe('xy_expression', () => { }); }); - test('it should set the tickLabel orientation on the x axis', () => { + test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { const { data, args } = sampleArgs(); - args.labelsOrientation = { - x: -45, - yLeft: 0, - yRight: -90, - type: 'lens_xy_labelsOrientationConfig', + args.tickLabelsVisibilitySettings = { + x: true, + yLeft: false, + yRight: false, + type: 'lens_xy_tickLabelsConfig', }; const instance = shallow(); - const axisStyle = instance.find(Axis).first().prop('style'); + const axisStyle = instance.find(Axis).at(1).prop('style'); expect(axisStyle).toMatchObject({ tickLabel: { - rotation: -45, + visible: false, }, }); }); - test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { + test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { const { data, args } = sampleArgs(); args.tickLabelsVisibilitySettings = { x: true, - yLeft: false, - yRight: false, + yLeft: true, + yRight: true, type: 'lens_xy_tickLabelsConfig', }; const instance = shallow(); - const axisStyle = instance.find(Axis).at(1).prop('style'); + const axisStyle = instance.find(Axis).first().prop('style'); expect(axisStyle).toMatchObject({ tickLabel: { - visible: false, + visible: true, }, }); }); - test('it should set the tickLabel orientation on the y axis', () => { + test('it should set the tickLabel orientation on the x axis', () => { const { data, args } = sampleArgs(); args.labelsOrientation = { x: -45, - yLeft: -90, + yLeft: 0, yRight: -90, type: 'lens_xy_labelsOrientationConfig', }; const instance = shallow(); - const axisStyle = instance.find(Axis).at(1).prop('style'); + const axisStyle = instance.find(Axis).first().prop('style'); expect(axisStyle).toMatchObject({ tickLabel: { - rotation: -90, + rotation: -45, }, }); }); - test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { + test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { const { data, args } = sampleArgs(); args.tickLabelsVisibilitySettings = { - x: true, + x: false, yLeft: true, yRight: true, type: 'lens_xy_tickLabelsConfig', @@ -2043,7 +2038,7 @@ describe('xy_expression', () => { const instance = shallow(); - const axisStyle = instance.find(Axis).first().prop('style'); + const axisStyle = instance.find(Axis).at(1).prop('style'); expect(axisStyle).toMatchObject({ tickLabel: { @@ -2052,14 +2047,14 @@ describe('xy_expression', () => { }); }); - test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { + test('it should set the tickLabel orientation on the y axis', () => { const { data, args } = sampleArgs(); - args.tickLabelsVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', + args.labelsOrientation = { + x: -45, + yLeft: -90, + yRight: -90, + type: 'lens_xy_labelsOrientationConfig', }; const instance = shallow(); @@ -2068,7 +2063,7 @@ describe('xy_expression', () => { expect(axisStyle).toMatchObject({ tickLabel: { - visible: true, + rotation: -90, }, }); }); @@ -2284,6 +2279,12 @@ describe('xy_expression', () => { yLeft: false, yRight: false, }, + labelsOrientation: { + type: 'lens_xy_labelsOrientationConfig', + x: 0, + yLeft: 0, + yRight: 0, + }, yLeftExtent: { mode: 'full', type: 'lens_xy_axisExtentConfig', diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index b6487fa448314..56867c625bb6f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -30,8 +30,7 @@ import { LabelOverflowConstraint, } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; -import { - ExpressionFunctionDefinition, +import type { ExpressionRenderDefinition, Datatable, DatatableRow, @@ -39,24 +38,20 @@ import { import { IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { RenderMode } from 'src/plugins/expressions'; -import { - LensMultiTable, - FormatFactory, - ILensInterpreterRenderHandlers, - LensFilterEvent, - LensBrushEvent, -} from '../types'; -import { XYArgs, SeriesType, visualizationTypes, LayerArgs } from './types'; +import type { ILensInterpreterRenderHandlers, LensFilterEvent, LensBrushEvent } from '../types'; +import type { LensMultiTable, FormatFactory } from '../../common'; +import { LayerArgs, SeriesType, XYChartProps } from '../../common/expressions'; +import { visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart, getSeriesColor } from './state_helpers'; -import { ExpressionValueSearchContext, search } from '../../../../../src/plugins/data/public'; +import { search } from '../../../../../src/plugins/data/public'; import { ChartsPluginSetup, PaletteRegistry, SeriesLayer, } from '../../../../../src/plugins/charts/public'; import { EmptyPlaceholder } from '../shared_components'; -import { fittingFunctionDefinitions, getFitOptions } from './fitting_functions'; +import { getFitOptions } from './fitting_functions'; import { getAxesConfiguration, GroupsConfiguration, validateExtent } from './axes_configuration'; import { getColorAssignments } from './color_assignment'; import { getXDomain, XyEndzones } from './x_domain'; @@ -76,16 +71,17 @@ type SeriesSpec = InferPropType & InferPropType & InferPropType; -export interface XYChartProps { - data: LensMultiTable; - args: XYArgs; -} - -export interface XYRender { - type: 'render'; - as: 'lens_xy_chart_renderer'; - value: XYChartProps; -} +export { + legendConfig, + yAxisConfig, + tickLabelsConfig, + gridlinesConfig, + axisTitlesVisibilityConfig, + axisExtentConfig, + layerConfig, + xyChart, + labelsOrientationConfig, +} from '../../common/expressions'; export type XYChartRenderProps = XYChartProps & { chartsThemeService: ChartsPluginSetup['theme']; @@ -99,145 +95,6 @@ export type XYChartRenderProps = XYChartProps & { syncColors: boolean; }; -export const xyChart: ExpressionFunctionDefinition< - 'lens_xy_chart', - LensMultiTable | ExpressionValueSearchContext | null, - XYArgs, - XYRender -> = { - name: 'lens_xy_chart', - type: 'render', - inputTypes: ['lens_multitable', 'kibana_context', 'null'], - help: i18n.translate('xpack.lens.xyChart.help', { - defaultMessage: 'An X/Y chart', - }), - args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, - xTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.xTitle.help', { - defaultMessage: 'X axis title', - }), - }, - yTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { - defaultMessage: 'Y left axis title', - }), - }, - yRightTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { - defaultMessage: 'Y right axis title', - }), - }, - yLeftExtent: { - types: ['lens_xy_axisExtentConfig'], - help: i18n.translate('xpack.lens.xyChart.yLeftExtent.help', { - defaultMessage: 'Y left axis extents', - }), - }, - yRightExtent: { - types: ['lens_xy_axisExtentConfig'], - help: i18n.translate('xpack.lens.xyChart.yRightExtent.help', { - defaultMessage: 'Y right axis extents', - }), - }, - legend: { - types: ['lens_xy_legendConfig'], - help: i18n.translate('xpack.lens.xyChart.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - }, - fittingFunction: { - types: ['string'], - options: [...fittingFunctionDefinitions.map(({ id }) => id)], - help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { - defaultMessage: 'Define how missing values are treated', - }), - }, - valueLabels: { - types: ['string'], - options: ['hide', 'inside'], - help: '', - }, - tickLabelsVisibilitySettings: { - types: ['lens_xy_tickLabelsConfig'], - help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { - defaultMessage: 'Show x and y axes tick labels', - }), - }, - gridlinesVisibilitySettings: { - types: ['lens_xy_gridlinesConfig'], - help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { - defaultMessage: 'Show x and y axes gridlines', - }), - }, - labelsOrientation: { - types: ['lens_xy_labelsOrientationConfig'], - help: i18n.translate('xpack.lens.xyChart.labelsOrientation.help', { - defaultMessage: 'Defines the orientation of x and y axes', - }), - }, - axisTitlesVisibilitySettings: { - types: ['lens_xy_axisTitlesVisibilityConfig'], - help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { - defaultMessage: 'Show x and y axes titles', - }), - }, - layers: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - types: ['lens_xy_layer'] as any, - help: 'Layers of visual series', - multi: true, - }, - curveType: { - types: ['string'], - options: ['LINEAR', 'CURVE_MONOTONE_X'], - help: i18n.translate('xpack.lens.xyChart.curveType.help', { - defaultMessage: 'Define how curve type is rendered for a line chart', - }), - }, - fillOpacity: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.fillOpacity.help', { - defaultMessage: 'Define the area chart fill opacity', - }), - }, - hideEndzones: { - types: ['boolean'], - default: false, - help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', { - defaultMessage: 'Hide endzone markers for partial data', - }), - }, - valuesInLegend: { - types: ['boolean'], - default: false, - help: i18n.translate('xpack.lens.xyChart.valuesInLegend.help', { - defaultMessage: 'Show values in legend', - }), - }, - }, - fn(data: LensMultiTable, args: XYArgs) { - return { - type: 'render', - as: 'lens_xy_chart_renderer', - value: { - data, - args, - }, - }; - }, -}; - export function calculateMinInterval({ args: { layers }, data }: XYChartProps) { const filteredLayers = getFilteredLayers(layers, data); if (filteredLayers.length === 0) return; diff --git a/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts b/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts index a1f8ad1fa259a..0b0878dfe9684 100644 --- a/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts @@ -6,57 +6,7 @@ */ import { Fit } from '@elastic/charts'; -import { i18n } from '@kbn/i18n'; - -export type FittingFunction = typeof fittingFunctionDefinitions[number]['id']; - -export const fittingFunctionDefinitions = [ - { - id: 'None', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', { - defaultMessage: 'Hide', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.none', { - defaultMessage: 'Do not fill gaps', - }), - }, - { - id: 'Zero', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.zero', { - defaultMessage: 'Zero', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.zero', { - defaultMessage: 'Fill gaps with zeros', - }), - }, - { - id: 'Linear', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.linear', { - defaultMessage: 'Linear', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.linear', { - defaultMessage: 'Fill gaps with a line', - }), - }, - { - id: 'Carry', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.carry', { - defaultMessage: 'Last', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.carry', { - defaultMessage: 'Fill gaps with the last value', - }), - }, - { - id: 'Lookahead', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.lookahead', { - defaultMessage: 'Next', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.lookahead', { - defaultMessage: 'Fill gaps with the next value', - }), - }, -] as const; +import { FittingFunction } from '../../common/expressions'; export function getFitEnum(fittingFunction?: FittingFunction) { if (fittingFunction) { diff --git a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx index e4edfe918a242..e3489ae7808af 100644 --- a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx @@ -10,8 +10,8 @@ import { LegendActionProps, SeriesIdentifier } from '@elastic/charts'; import { EuiPopover } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test/jest'; import { ComponentType, ReactWrapper } from 'enzyme'; -import type { LayerArgs } from './types'; -import type { LensMultiTable } from '../types'; +import type { LensMultiTable } from '../../common'; +import type { LayerArgs } from '../../common/expressions'; import { getLegendAction } from './get_legend_action'; import { LegendActionPopover } from '../shared_components'; diff --git a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx index c99bf948d6e37..0603328ee5bb3 100644 --- a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx @@ -7,8 +7,9 @@ import React from 'react'; import type { LegendAction, XYChartSeriesIdentifier } from '@elastic/charts'; -import type { LayerArgs } from './types'; -import type { LensMultiTable, LensFilterEvent, FormatFactory } from '../types'; +import type { LensFilterEvent } from '../types'; +import type { LensMultiTable, FormatFactory } from '../../common'; +import type { LayerArgs } from '../../common/expressions'; import { LegendActionPopover } from '../shared_components'; export const getLegendAction = ( diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index 6263d941688a7..d0f6666f7cd93 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -5,12 +5,13 @@ * 2.0. */ -import { CoreSetup } from 'kibana/public'; -import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; -import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; -import { LensPluginStartDependencies } from '../plugin'; +import type { CoreSetup } from 'kibana/public'; +import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; +import type { LensPluginStartDependencies } from '../plugin'; import { getTimeZone } from '../utils'; +import type { FormatFactory } from '../../common'; export interface XyVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index aa8dede62f566..e3b16f5981f88 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -6,8 +6,9 @@ */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { FramePublicAPI, DatasourcePublicAPI } from '../types'; -import { SeriesType, visualizationTypes, XYLayerConfig, YConfig, ValidLayer } from './types'; +import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; +import type { SeriesType, XYLayerConfig, YConfig, ValidLayer } from '../../common/expressions'; +import { visualizationTypes } from './types'; export function isHorizontalSeries(seriesType: SeriesType) { return ( diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index b2ecdc5179ad4..dfad8334ab76a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -8,9 +8,10 @@ import { Ast } from '@kbn/interpreter/common'; import { ScaleType } from '@elastic/charts'; import { PaletteRegistry } from 'src/plugins/charts/public'; -import { State, ValidLayer, XYLayerConfig } from './types'; +import { State } from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; +import { ValidLayer, XYLayerConfig } from '../../common/expressions'; export const getSortedAccessors = (datasource: DatasourcePublicAPI, layer: XYLayerConfig) => { const originalOrder = datasource diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 06192b236dee5..9d32c2f71c530 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -5,10 +5,7 @@ * 2.0. */ -import { Position, VerticalAlignment, HorizontalAlignment } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { PaletteOutput } from 'src/plugins/charts/public'; -import { ArgumentType, ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { LensIconChartArea } from '../assets/chart_area'; import { LensIconChartAreaStacked } from '../assets/chart_area_stacked'; import { LensIconChartAreaPercentage } from '../assets/chart_area_percentage'; @@ -21,552 +18,17 @@ import { LensIconChartBarHorizontalPercentage } from '../assets/chart_bar_horizo import { LensIconChartLine } from '../assets/chart_line'; import { VisualizationType } from '../types'; -import { FittingFunction } from './fitting_functions'; - -export interface LegendConfig { - /** - * Flag whether the legend should be shown. If there is just a single series, it will be hidden - */ - isVisible: boolean; - /** - * Position of the legend relative to the chart - */ - position: Position; - /** - * Flag whether the legend should be shown even with just a single series - */ - showSingleSeries?: boolean; - /** - * Flag whether the legend is inside the chart - */ - isInside?: boolean; - /** - * Horizontal Alignment of the legend when it is set inside chart - */ - horizontalAlignment?: HorizontalAlignment; - /** - * Vertical Alignment of the legend when it is set inside chart - */ - verticalAlignment?: VerticalAlignment; - /** - * Number of columns when legend is set inside chart - */ - floatingColumns?: number; -} - -type LegendConfigResult = LegendConfig & { type: 'lens_xy_legendConfig' }; - -export const legendConfig: ExpressionFunctionDefinition< - 'lens_xy_legendConfig', - null, +import { + SeriesType, + ValueLabelConfig, LegendConfig, - LegendConfigResult -> = { - name: 'lens_xy_legendConfig', - aliases: [], - type: 'lens_xy_legendConfig', - help: `Configure the xy chart's legend`, - inputTypes: ['null'], - args: { - isVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.isVisible.help', { - defaultMessage: 'Specifies whether or not the legend is visible.', - }), - }, - position: { - types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: i18n.translate('xpack.lens.xyChart.position.help', { - defaultMessage: 'Specifies the legend position.', - }), - }, - showSingleSeries: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.showSingleSeries.help', { - defaultMessage: 'Specifies whether a legend with just a single entry should be shown', - }), - }, - isInside: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.isInside.help', { - defaultMessage: 'Specifies whether a legend is inside the chart', - }), - }, - horizontalAlignment: { - types: ['string'], - options: [HorizontalAlignment.Right, HorizontalAlignment.Left], - help: i18n.translate('xpack.lens.xyChart.horizontalAlignment.help', { - defaultMessage: - 'Specifies the horizontal alignment of the legend when it is displayed inside chart.', - }), - }, - verticalAlignment: { - types: ['string'], - options: [VerticalAlignment.Top, VerticalAlignment.Bottom], - help: i18n.translate('xpack.lens.xyChart.verticalAlignment.help', { - defaultMessage: - 'Specifies the vertical alignment of the legend when it is displayed inside chart.', - }), - }, - floatingColumns: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.floatingColumns.help', { - defaultMessage: 'Specifies the number of columns when legend is displayed inside chart.', - }), - }, - }, - fn: function fn(input: unknown, args: LegendConfig) { - return { - type: 'lens_xy_legendConfig', - ...args, - }; - }, -}; - -export interface AxesSettingsConfig { - x: boolean; - yLeft: boolean; - yRight: boolean; -} - -type TickLabelsConfigResult = AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' }; - -export const tickLabelsConfig: ExpressionFunctionDefinition< - 'lens_xy_tickLabelsConfig', - null, + AxisExtentConfig, + XYLayerConfig, + XYCurveType, AxesSettingsConfig, - TickLabelsConfigResult -> = { - name: 'lens_xy_tickLabelsConfig', - aliases: [], - type: 'lens_xy_tickLabelsConfig', - help: `Configure the xy chart's tick labels appearance`, - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the x-axis are visible.', - }), - }, - yLeft: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the right y-axis are visible.', - }), - }, - }, - fn: function fn(input: unknown, args: AxesSettingsConfig) { - return { - type: 'lens_xy_tickLabelsConfig', - ...args, - }; - }, -}; - -export interface LabelsOrientationConfig { - x: number; - yLeft: number; - yRight: number; -} - -type LabelsOrientationConfigResult = LabelsOrientationConfig & { - type: 'lens_xy_labelsOrientationConfig'; -}; - -export const labelsOrientationConfig: ExpressionFunctionDefinition< - 'lens_xy_labelsOrientationConfig', - null, + FittingFunction, LabelsOrientationConfig, - LabelsOrientationConfigResult -> = { - name: 'lens_xy_labelsOrientationConfig', - aliases: [], - type: 'lens_xy_labelsOrientationConfig', - help: `Configure the xy chart's tick labels orientation`, - inputTypes: ['null'], - args: { - x: { - types: ['number'], - options: [0, -90, -45], - help: i18n.translate('xpack.lens.xyChart.xAxisLabelsOrientation.help', { - defaultMessage: 'Specifies the labels orientation of the x-axis.', - }), - }, - yLeft: { - types: ['number'], - options: [0, -90, -45], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisLabelsOrientation.help', { - defaultMessage: 'Specifies the labels orientation of the left y-axis.', - }), - }, - yRight: { - types: ['number'], - options: [0, -90, -45], - help: i18n.translate('xpack.lens.xyChart.yRightAxisLabelsOrientation.help', { - defaultMessage: 'Specifies the labels orientation of the right y-axis.', - }), - }, - }, - fn: function fn(input: unknown, args: LabelsOrientationConfig) { - return { - type: 'lens_xy_labelsOrientationConfig', - ...args, - }; - }, -}; - -type GridlinesConfigResult = AxesSettingsConfig & { type: 'lens_xy_gridlinesConfig' }; - -export const gridlinesConfig: ExpressionFunctionDefinition< - 'lens_xy_gridlinesConfig', - null, - AxesSettingsConfig, - GridlinesConfigResult -> = { - name: 'lens_xy_gridlinesConfig', - aliases: [], - type: 'lens_xy_gridlinesConfig', - help: `Configure the xy chart's gridlines appearance`, - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisGridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the x-axis are visible.', - }), - }, - yLeft: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisgridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisgridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the right y-axis are visible.', - }), - }, - }, - fn: function fn(input: unknown, args: AxesSettingsConfig) { - return { - type: 'lens_xy_gridlinesConfig', - ...args, - }; - }, -}; - -type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { - type: 'lens_xy_axisTitlesVisibilityConfig'; -}; - -export const axisTitlesVisibilityConfig: ExpressionFunctionDefinition< - 'lens_xy_axisTitlesVisibilityConfig', - null, - AxesSettingsConfig, - AxisTitlesVisibilityConfigResult -> = { - name: 'lens_xy_axisTitlesVisibilityConfig', - aliases: [], - type: 'lens_xy_axisTitlesVisibilityConfig', - help: `Configure the xy chart's axis titles appearance`, - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisTitle.help', { - defaultMessage: 'Specifies whether or not the title of the x-axis are visible.', - }), - }, - yLeft: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisTitle.help', { - defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisTitle.help', { - defaultMessage: 'Specifies whether or not the title of the right y-axis are visible.', - }), - }, - }, - fn: function fn(input: unknown, args: AxesSettingsConfig) { - return { - type: 'lens_xy_axisTitlesVisibilityConfig', - ...args, - }; - }, -}; - -export interface AxisExtentConfig { - mode: 'full' | 'dataBounds' | 'custom'; - lowerBound?: number; - upperBound?: number; -} - -export const axisExtentConfig: ExpressionFunctionDefinition< - 'lens_xy_axisExtentConfig', - null, - AxisExtentConfig, - AxisExtentConfigResult -> = { - name: 'lens_xy_axisExtentConfig', - aliases: [], - type: 'lens_xy_axisExtentConfig', - help: `Configure the xy chart's axis extents`, - inputTypes: ['null'], - args: { - mode: { - types: ['string'], - options: ['full', 'dataBounds', 'custom'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', - }), - }, - lowerBound: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', - }), - }, - upperBound: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', - }), - }, - }, - fn: function fn(input: unknown, args: AxisExtentConfig) { - return { - type: 'lens_xy_axisExtentConfig', - ...args, - }; - }, -}; - -export type AxisExtentConfigResult = AxisExtentConfig & { type: 'lens_xy_axisExtentConfig' }; - -interface AxisConfig { - title: string; - hide?: boolean; -} - -const axisConfig: { [key in keyof AxisConfig]: ArgumentType } = { - title: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.title.help', { - defaultMessage: 'The axis title', - }), - }, - hide: { - types: ['boolean'], - default: false, - help: 'Show / hide axis', - }, -}; - -type YConfigResult = YConfig & { type: 'lens_xy_yConfig' }; - -export const yAxisConfig: ExpressionFunctionDefinition< - 'lens_xy_yConfig', - null, - YConfig, - YConfigResult -> = { - name: 'lens_xy_yConfig', - aliases: [], - type: 'lens_xy_yConfig', - help: `Configure the behavior of a xy chart's y axis metric`, - inputTypes: ['null'], - args: { - forAccessor: { - types: ['string'], - help: 'The accessor this configuration is for', - }, - axisMode: { - types: ['string'], - options: ['auto', 'left', 'right'], - help: 'The axis mode of the metric', - }, - color: { - types: ['string'], - help: 'The color of the series', - }, - }, - fn: function fn(input: unknown, args: YConfig) { - return { - type: 'lens_xy_yConfig', - ...args, - }; - }, -}; - -type LayerConfigResult = LayerArgs & { type: 'lens_xy_layer' }; - -export const layerConfig: ExpressionFunctionDefinition< - 'lens_xy_layer', - null, - LayerArgs, - LayerConfigResult -> = { - name: 'lens_xy_layer', - aliases: [], - type: 'lens_xy_layer', - help: `Configure a layer in the xy chart`, - inputTypes: ['null'], - args: { - ...axisConfig, - layerId: { - types: ['string'], - help: '', - }, - xAccessor: { - types: ['string'], - help: '', - }, - seriesType: { - types: ['string'], - options: [ - 'bar', - 'line', - 'area', - 'bar_stacked', - 'area_stacked', - 'bar_percentage_stacked', - 'area_percentage_stacked', - ], - help: 'The type of chart to display.', - }, - xScaleType: { - options: ['ordinal', 'linear', 'time'], - help: 'The scale type of the x axis', - default: 'ordinal', - }, - isHistogram: { - types: ['boolean'], - default: false, - help: 'Whether to layout the chart as a histogram', - }, - yScaleType: { - options: ['log', 'sqrt', 'linear', 'time'], - help: 'The scale type of the y axes', - default: 'linear', - }, - splitAccessor: { - types: ['string'], - help: 'The column to split by', - multi: false, - }, - accessors: { - types: ['string'], - help: 'The columns to display on the y axis.', - multi: true, - }, - yConfig: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - types: ['lens_xy_yConfig' as any], - help: 'Additional configuration for y axes', - multi: true, - }, - columnToLabel: { - types: ['string'], - help: 'JSON key-value pairs of column ID to label', - }, - palette: { - default: `{theme "palette" default={system_palette name="default"} }`, - help: '', - types: ['palette'], - }, - }, - fn: function fn(input: unknown, args: LayerArgs) { - return { - type: 'lens_xy_layer', - ...args, - }; - }, -}; - -export type SeriesType = - | 'bar' - | 'bar_horizontal' - | 'line' - | 'area' - | 'bar_stacked' - | 'bar_percentage_stacked' - | 'bar_horizontal_stacked' - | 'bar_horizontal_percentage_stacked' - | 'area_stacked' - | 'area_percentage_stacked'; - -export type YAxisMode = 'auto' | 'left' | 'right'; - -export type ValueLabelConfig = 'hide' | 'inside' | 'outside'; - -export interface YConfig { - forAccessor: string; - axisMode?: YAxisMode; - color?: string; -} - -export interface XYLayerConfig { - hide?: boolean; - layerId: string; - xAccessor?: string; - accessors: string[]; - yConfig?: YConfig[]; - seriesType: SeriesType; - splitAccessor?: string; - palette?: PaletteOutput; -} - -export interface ValidLayer extends XYLayerConfig { - xAccessor: NonNullable; -} - -export type LayerArgs = XYLayerConfig & { - columnToLabel?: string; // Actually a JSON key-value pair - yScaleType: 'time' | 'linear' | 'log' | 'sqrt'; - xScaleType: 'time' | 'linear' | 'ordinal'; - isHistogram: boolean; - // palette will always be set on the expression - palette: PaletteOutput; -}; - -// Arguments to XY chart expression, with computed properties -export interface XYArgs { - title?: string; - description?: string; - xTitle: string; - yTitle: string; - yRightTitle: string; - yLeftExtent: AxisExtentConfigResult; - yRightExtent: AxisExtentConfigResult; - legend: LegendConfig & { type: 'lens_xy_legendConfig' }; - valueLabels: ValueLabelConfig; - layers: LayerArgs[]; - fittingFunction?: FittingFunction; - axisTitlesVisibilitySettings?: AxesSettingsConfig & { - type: 'lens_xy_axisTitlesVisibilityConfig'; - }; - tickLabelsVisibilitySettings?: AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' }; - gridlinesVisibilitySettings?: AxesSettingsConfig & { type: 'lens_xy_gridlinesConfig' }; - labelsOrientation?: LabelsOrientationConfig & { type: 'lens_xy_labelsOrientationConfig' }; - curveType?: XYCurveType; - fillOpacity?: number; - hideEndzones?: boolean; - valuesInLegend?: boolean; -} - -export type XYCurveType = 'LINEAR' | 'CURVE_MONOTONE_X'; +} from '../../common/expressions'; // Persisted parts of the state export interface XYState { diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx index 1df7744524779..6080a8c68e57d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSwitch } from '@elastic/eui'; -import { XYCurveType } from '../types'; +import type { XYCurveType } from '../../../common/expressions'; export interface LineCurveOptionProps { /** diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx index fb6ecec4d2801..3dba8757903e9 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, EuiIconTip, EuiSuperSelect, EuiText } from '@elastic/eui'; -import { FittingFunction, fittingFunctionDefinitions } from '../fitting_functions'; -import { ValueLabelConfig } from '../types'; +import { fittingFunctionDefinitions } from '../../../common/expressions'; +import type { FittingFunction, ValueLabelConfig } from '../../../common/expressions'; export interface MissingValuesOptionProps { valueLabels?: ValueLabelConfig; diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx index ec0c11a0b1d86..b4c8e8f40dde7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallowWithIntl as shallow } from '@kbn/test/jest'; import { Position } from '@elastic/charts'; -import { FramePublicAPI } from '../../types'; +import type { FramePublicAPI } from '../../types'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { State } from '../types'; import { VisualOptionsPopover } from './visual_options_popover'; diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx index 843680e3f28ac..6d0e5c2d55b70 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx @@ -13,8 +13,8 @@ import { LineCurveOption } from './line_curve_option'; import { FillOpacityOption } from './fill_opacity_option'; import { XYState } from '../types'; import { hasHistogramSeries } from '../state_helpers'; -import { ValidLayer } from '../types'; -import { FramePublicAPI } from '../../types'; +import { ValidLayer } from '../../../common/expressions'; +import type { FramePublicAPI } from '../../types'; function getValueLabelDisableReason({ isAreaPercentage, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 304e323789c14..fd80b9d96d30a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -8,7 +8,8 @@ import { getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; import { Operation } from '../types'; -import { State, SeriesType, XYLayerConfig } from './types'; +import type { State } from './types'; +import type { SeriesType, XYLayerConfig } from '../../common/expressions'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { LensIconChartBar } from '../assets/chart_bar'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 199dccdf702f7..40caed7188190 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -15,14 +15,15 @@ import { PaletteRegistry } from 'src/plugins/charts/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { getSuggestions } from './xy_suggestions'; import { LayerContextMenu, XyToolbar, DimensionEditor } from './xy_config_panel'; -import { +import type { Visualization, OperationMetadata, VisualizationType, AccessorConfig, DatasourcePublicAPI, } from '../types'; -import { State, SeriesType, visualizationTypes, XYLayerConfig, XYState } from './types'; +import { State, visualizationTypes, XYState } from './types'; +import type { SeriesType, XYLayerConfig } from '../../common/expressions'; import { isHorizontalChart } from './state_helpers'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; import { LensIconChartBarStacked } from '../assets/chart_bar_stacked'; diff --git a/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx b/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx index 369063644a754..ccb047d54e369 100644 --- a/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx @@ -8,8 +8,8 @@ import { uniq } from 'lodash'; import React from 'react'; import { Endzones } from '../../../../../src/plugins/charts/public'; -import { LensMultiTable } from '../types'; -import { LayerArgs } from './types'; +import type { LensMultiTable } from '../../common'; +import type { LayerArgs } from '../../common/expressions'; export interface XDomain { min?: number; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx index d5d66cfda6029..129f2df895ef2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx @@ -21,23 +21,21 @@ import { EuiToolTip, EuiIcon, } from '@elastic/eui'; -import { PaletteRegistry } from 'src/plugins/charts/public'; -import { +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { VisualizationLayerWidgetProps, VisualizationToolbarProps, VisualizationDimensionEditorProps, - FormatFactory, FramePublicAPI, } from '../types'; -import { - State, +import { State, visualizationTypes, XYState } from './types'; +import type { FormatFactory } from '../../common'; +import type { SeriesType, - visualizationTypes, YAxisMode, AxesSettingsConfig, AxisExtentConfig, - XYState, -} from './types'; +} from '../../common/expressions'; import { isHorizontalChart, isHorizontalSeries, getSeriesColor } from './state_helpers'; import { trackUiEvent } from '../lens_ui_telemetry'; import { LegendSettingsPopover } from '../shared_components'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index 511d9387e9c28..dfa0646404388 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -16,7 +16,8 @@ import { TableSuggestion, TableChangeType, } from '../types'; -import { State, SeriesType, XYState, visualizationTypes, XYLayerConfig } from './types'; +import { State, XYState, visualizationTypes } from './types'; +import type { SeriesType, XYLayerConfig } from '../../common/expressions'; import { getIconForSeries } from './state_helpers'; const columnSortOrder = { diff --git a/x-pack/plugins/lens/server/plugin.tsx b/x-pack/plugins/lens/server/plugin.tsx index c23c98cd12aec..b47019fa54ec0 100644 --- a/x-pack/plugins/lens/server/plugin.tsx +++ b/x-pack/plugins/lens/server/plugin.tsx @@ -9,6 +9,7 @@ import { Plugin, CoreSetup, CoreStart, PluginInitializerContext, Logger } from ' import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { Observable } from 'rxjs'; import { PluginStart as DataPluginStart } from 'src/plugins/data/server'; +import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server'; import { setupRoutes } from './routes'; import { @@ -24,6 +25,7 @@ export interface PluginSetupContract { usageCollection?: UsageCollectionSetup; taskManager?: TaskManagerSetupContract; embeddable: EmbeddableSetup; + expressions: ExpressionsServerSetup; } export interface PluginStartContract { diff --git a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts index 148deda9aec76..05e1c2c4dca81 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts @@ -26,7 +26,7 @@ type EventHandlerCallback = MouseEventHandlerSee policies */ export const useNavigateToAppEventHandler = ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts index ca14dde18455b..e8fa53e2cf920 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts @@ -14,6 +14,7 @@ import { MANAGEMENT_STORE_GLOBAL_NAMESPACE, } from '../../../../common/constants'; import { useAppUrl } from '../../../../../common/lib/kibana'; +import { pagePathGetters } from '../../../../../../../fleet/public'; export function useEndpointSelector(selector: (state: EndpointState) => TSelected) { return useSelector(function (state: State) { @@ -47,7 +48,8 @@ export const useAgentDetailsIngestUrl = ( ): { url: string; appId: string; appPath: string } => { const { getAppUrl } = useAppUrl(); return useMemo(() => { - const appPath = `#/fleet/agents/${agentId}/activity`; + const appPath = pagePathGetters.agent_details_logs({ agentId })[1]; + return { url: `${getAppUrl({ appId: 'fleet' })}${appPath}`, appId: 'fleet', diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 584e6df1ff781..03df5d2bcbac7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -120,13 +120,13 @@ export const useEndpointActionItems = ( 'data-test-subj': 'agentPolicyLink', navigateAppId: 'fleet', navigateOptions: { - path: `#${ + path: `${ pagePathGetters.policy_details({ policyId: fleetAgentPolicies[endpointPolicyId], })[1] }`, }, - href: `${getAppUrl({ appId: 'fleet' })}#${ + href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.policy_details({ policyId: fleetAgentPolicies[endpointPolicyId], })[1] @@ -145,13 +145,13 @@ export const useEndpointActionItems = ( 'data-test-subj': 'agentDetailsLink', navigateAppId: 'fleet', navigateOptions: { - path: `#${ + path: `${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] }`, }, - href: `${getAppUrl({ appId: 'fleet' })}#${ + href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] @@ -169,17 +169,17 @@ export const useEndpointActionItems = ( 'data-test-subj': 'agentPolicyReassignLink', navigateAppId: 'fleet', navigateOptions: { - path: `#${ + path: `${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] - }/activity?openReassignFlyout=true`, + }?openReassignFlyout=true`, }, - href: `${getAppUrl({ appId: 'fleet' })}#${ + href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] - }/activity?openReassignFlyout=true`, + }?openReassignFlyout=true`, children: ( { }); it('navigates to the Ingest Agent Policy page', async () => { const agentPolicyLink = await renderResult.findByTestId('agentPolicyLink'); - expect(agentPolicyLink.getAttribute('href')).toEqual(`/app/fleet#/policies/${agentPolicyId}`); + expect(agentPolicyLink.getAttribute('href')).toEqual(`/app/fleet/policies/${agentPolicyId}`); }); it('navigates to the Ingest Agent Details page', async () => { const agentDetailsLink = await renderResult.findByTestId('agentDetailsLink'); - expect(agentDetailsLink.getAttribute('href')).toEqual(`/app/fleet#/agents/${agentId}`); + expect(agentDetailsLink.getAttribute('href')).toEqual(`/app/fleet/agents/${agentId}`); }); it('navigates to the Ingest Agent Details page with policy reassign', async () => { const agentPolicyReassignLink = await renderResult.findByTestId('agentPolicyReassignLink'); expect(agentPolicyReassignLink.getAttribute('href')).toEqual( - `/app/fleet#/agents/${agentId}/activity?openReassignFlyout=true` + `/app/fleet/agents/${agentId}?openReassignFlyout=true` ); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index c78d4ca6af634..74f5b15a72727 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -156,7 +156,7 @@ export const EndpointList = () => { const handleCreatePolicyClick = useNavigateToAppEventHandler( 'fleet', { - path: `#/integrations/${ + path: `/integrations/${ endpointPackageVersion ? `/endpoint-${endpointPackageVersion}` : '' }/add-integration`, state: { @@ -203,7 +203,7 @@ export const EndpointList = () => { const handleDeployEndpointsClick = useNavigateToAppEventHandler( 'fleet', { - path: `#/policies/${selectedPolicyId}?openEnrollmentFlyout=true`, + path: `/policies/${selectedPolicyId}?openEnrollmentFlyout=true`, state: { onDoneNavigateTo: [ 'securitySolution', diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx index db5c42241a0cc..29723a5fd3cf8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx @@ -31,16 +31,10 @@ import { ExceptionBuilder } from '../../../../../../shared_imports'; import { useEventFiltersSelector } from '../../hooks'; import { getFormEntryStateMutable, getHasNameError, getNewComment } from '../../../store/selector'; -import { - FORM_DESCRIPTION, - NAME_LABEL, - NAME_ERROR, - NAME_PLACEHOLDER, - OS_LABEL, - RULE_NAME, -} from './translations'; +import { NAME_LABEL, NAME_ERROR, NAME_PLACEHOLDER, OS_LABEL, RULE_NAME } from './translations'; import { OS_TITLES } from '../../../../../common/translations'; import { ENDPOINT_EVENT_FILTERS_LIST_ID, EVENT_FILTER_LIST_TYPE } from '../../../constants'; +import { ABOUT_EVENT_FILTERS } from '../../translations'; const OPERATING_SYSTEMS: readonly OperatingSystem[] = [ OperatingSystem.MAC, @@ -205,8 +199,12 @@ export const EventFiltersForm: React.FC = memo( return !isIndexPatternLoading && exception ? ( - {FORM_DESCRIPTION} - + {!exception || !exception.item_id ? ( + + {ABOUT_EVENT_FILTERS} + + + ) : null} {nameInputMemo} {allowSelectOs ? ( diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/translations.ts b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/translations.ts index 7391251a936e6..bfb828699118e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/translations.ts @@ -7,13 +7,6 @@ import { i18n } from '@kbn/i18n'; -export const FORM_DESCRIPTION = i18n.translate( - 'xpack.securitySolution.eventFilter.modal.description', - { - defaultMessage: "Events are filtered when the rule's conditions are met:", - } -); - export const NAME_PLACEHOLDER = i18n.translate( 'xpack.securitySolution.eventFilter.form.name.placeholder', { diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx index 2d608bdc6e157..95f3e856a6ff6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx @@ -44,6 +44,7 @@ import { EventFilterDeleteModal } from './components/event_filter_delete_modal'; import { SearchBar } from '../../../components/search_bar'; import { BackToExternalAppButton } from '../../../components/back_to_external_app_button'; +import { ABOUT_EVENT_FILTERS } from './translations'; type EventListPaginatedContent = PaginatedContentProps< Immutable, @@ -195,11 +196,7 @@ export const EventFiltersListPage = memo(() => { defaultMessage="Event Filters" /> } - subtitle={i18n.translate('xpack.securitySolution.eventFilters.aboutInfo', { - defaultMessage: - 'Add an event filter to exclude high volume or unwanted events from being written to Elasticsearch. Event ' + - 'filters are processed by the Endpoint Security integration, and are applied to hosts running this integration on their agents.', - })} + subtitle={ABOUT_EVENT_FILTERS} actions={ doesDataExist && ( { values: { error: getError.message }, }); }; + +export const ABOUT_EVENT_FILTERS = i18n.translate('xpack.securitySolution.eventFilters.aboutInfo', { + defaultMessage: + 'Add an event filter to exclude high volume or unwanted events from being written to Elasticsearch. Event ' + + 'filters are processed by the Endpoint Security integration, and are applied to hosts running this integration on their agents.', +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx index 7af9f84ad0875..2d21ec9565476 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx @@ -63,7 +63,7 @@ describe('OverviewEmpty', () => { fill: false, label: 'Add Endpoint Security', onClick: undefined, - url: `#/integrations/endpoint-${endpointPackageVersion}/add-integration`, + url: `/integrations/endpoint-${endpointPackageVersion}/add-integration`, }, }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx index c75438e18f5d5..6f885b348cdeb 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx @@ -36,7 +36,7 @@ const OverviewEmptyComponent: React.FC = () => { const endpointIntegrationUrlPath = endpointPackageVersion ? `/endpoint-${endpointPackageVersion}/add-integration` : ''; - const endpointIntegrationUrl = `#/integrations${endpointIntegrationUrlPath}`; + const endpointIntegrationUrl = `/integrations${endpointIntegrationUrlPath}`; const handleEndpointClick = useNavigateToAppEventHandler('fleet', { path: endpointIntegrationUrl, }); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5a09667e2a327..81b6f83654647 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6703,10 +6703,10 @@ "xpack.canvas.functions.if.args.elseHelpText": "条件が {BOOLEAN_FALSE} の場合の戻り値です。指定されておらず、条件が満たされていない場合は、元の {CONTEXT} が戻されます。", "xpack.canvas.functions.if.args.thenHelpText": "条件が {BOOLEAN_TRUE} の場合の戻り値です。指定されておらず、条件が満たされている場合は、元の {CONTEXT} が戻されます。", "xpack.canvas.functions.ifHelpText": "条件付きロジックを実行します。", - "xpack.canvas.functions.image.args.dataurlHelpText": "画像の {https} {URL} または {BASE64} データ {URL} です。", - "xpack.canvas.functions.image.args.modeHelpText": "{contain} はサイズに合わせて拡大・縮小して画像全体を表示し、{cover} はコンテナーを画像で埋め、必要に応じて両端や下をクロップします。{stretch} は画像の高さと幅をコンテナーの 100% になるよう変更します。", - "xpack.canvas.functions.image.invalidImageModeErrorMessage": "「mode」は「{contain}」、「{cover}」、または「{stretch}」でなければなりません", - "xpack.canvas.functions.imageHelpText": "画像を表示します。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", + "expressionImage.functions.image.args.dataurlHelpText": "画像の {https} {URL} または {BASE64} データ {URL} です。", + "expressionImage.functions.image.args.modeHelpText": "{contain} はサイズに合わせて拡大・縮小して画像全体を表示し、{cover} はコンテナーを画像で埋め、必要に応じて両端や下をクロップします。{stretch} は画像の高さと幅をコンテナーの 100% になるよう変更します。", + "expressionImage.functions.image.invalidImageModeErrorMessage": "「mode」は「{contain}」、「{cover}」、または「{stretch}」でなければなりません", + "expressionImage.functions.imageHelpText": "画像を表示します。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", "xpack.canvas.functions.joinRows.args.columnHelpText": "値を抽出する列またはフィールド。", "xpack.canvas.functions.joinRows.args.distinctHelpText": "一意の値のみを抽出しますか?", "xpack.canvas.functions.joinRows.args.quoteHelpText": "各抽出された値を囲む引用符文字。", @@ -6959,8 +6959,8 @@ "xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "すべて", "xpack.canvas.renderer.embeddable.displayName": "埋め込み可能", "xpack.canvas.renderer.embeddable.helpDescription": "Kibana の他の部分から埋め込み可能な保存済みオブジェクトをレンダリングします", - "xpack.canvas.renderer.image.displayName": "画像", - "xpack.canvas.renderer.image.helpDescription": "画像をレンダリングします", + "expressionImage.renderer.image.displayName": "画像", + "expressionImage.renderer.image.helpDescription": "画像をレンダリングします", "xpack.canvas.renderer.markdown.displayName": "マークダウン", "xpack.canvas.renderer.markdown.helpDescription": "{MARKDOWN} インプットを使用して {HTML} を表示", "xpack.canvas.renderer.metric.displayName": "メトリック", @@ -6973,6 +6973,8 @@ "xpack.canvas.renderer.progress.helpDescription": "エレメントのパーセンテージを示す進捗インジケーターをレンダリングします", "expressionRepeatImage.renderer.repeatImage.displayName": "画像の繰り返し", "expressionRepeatImage.renderer.repeatImage.helpDescription": "画像を指定回数繰り返し表示します", + "expressionShape.renderer.shape.displayName": "形状", + "expressionShape.renderer.shape.helpDescription": "基本的な図形をレンダリングします", "xpack.canvas.renderer.table.displayName": "データテーブル", "xpack.canvas.renderer.table.helpDescription": "表形式データを {HTML} としてレンダリングします", "xpack.canvas.renderer.text.displayName": "プレインテキスト", @@ -21816,7 +21818,6 @@ "xpack.securitySolution.eventFilter.form.updateSuccessToastTitle": "\"{name}\"が正常に更新されました", "xpack.securitySolution.eventFilter.modal.actions.cancel": "キャンセル", "xpack.securitySolution.eventFilter.modal.actions.confirm": "エンドポイントイベントフィルターを追加", - "xpack.securitySolution.eventFilter.modal.description": "ルールの条件が満たされたときにイベントがフィルタリングされます。", "xpack.securitySolution.eventFilter.modal.subtitle": "Endpoint Security", "xpack.securitySolution.eventFilter.modal.title": "エンドポイントイベントフィルターを追加", "xpack.securitySolution.eventFilter.search.placeholder": "次のフィールドで検索:名前、コメント、値", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index de212d601660d..81c28d517c4f0 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6744,10 +6744,10 @@ "xpack.canvas.functions.if.args.elseHelpText": "条件为 {BOOLEAN_FALSE} 时的返回值。未指定且条件未满足时,将返回原始 {CONTEXT}。", "xpack.canvas.functions.if.args.thenHelpText": "条件为 {BOOLEAN_TRUE} 时的返回值。未指定且条件满足时,将返回原始 {CONTEXT}。", "xpack.canvas.functions.ifHelpText": "执行条件逻辑。", - "xpack.canvas.functions.image.args.dataurlHelpText": "图像的 {https} {URL} 或 {BASE64} 数据 {URL}。", - "xpack.canvas.functions.image.args.modeHelpText": "{contain} 将显示整个图像,图像缩放至适合大小。{cover} 将使用该图像填充容器,根据需要在两边或底部裁剪图像。{stretch} 将图像的高和宽调整为容器的 100%。", - "xpack.canvas.functions.image.invalidImageModeErrorMessage": "“mode”必须为“{contain}”、“{cover}”或“{stretch}”", - "xpack.canvas.functions.imageHelpText": "显示图像。以 {BASE64} 数据 {URL} 的形式提供图像资产或传入子表达式。", + "expressionImage.functions.image.args.dataurlHelpText": "图像的 {https} {URL} 或 {BASE64} 数据 {URL}。", + "expressionImage.functions.image.args.modeHelpText": "{contain} 将显示整个图像,图像缩放至适合大小。{cover} 将使用该图像填充容器,根据需要在两边或底部裁剪图像。{stretch} 将图像的高和宽调整为容器的 100%。", + "expressionImage.functions.image.invalidImageModeErrorMessage": "“mode”必须为“{contain}”、“{cover}”或“{stretch}”", + "expressionImage.functions.imageHelpText": "显示图像。以 {BASE64} 数据 {URL} 的形式提供图像资产或传入子表达式。", "xpack.canvas.functions.joinRows.args.columnHelpText": "从其中提取值的列或字段。", "xpack.canvas.functions.joinRows.args.distinctHelpText": "仅提取唯一值?", "xpack.canvas.functions.joinRows.args.quoteHelpText": "要将每个提取的值引起来的引号字符。", @@ -7000,8 +7000,8 @@ "xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "任意", "xpack.canvas.renderer.embeddable.displayName": "可嵌入", "xpack.canvas.renderer.embeddable.helpDescription": "从 Kibana 的其他部分呈现可嵌入的已保存对象", - "xpack.canvas.renderer.image.displayName": "图像", - "xpack.canvas.renderer.image.helpDescription": "呈现图像", + "expressionImage.renderer.image.displayName": "图像", + "expressionImage.renderer.image.helpDescription": "呈现图像", "xpack.canvas.renderer.markdown.displayName": "Markdown", "xpack.canvas.renderer.markdown.helpDescription": "使用 {MARKDOWN} 输入呈现 {HTML}", "xpack.canvas.renderer.metric.displayName": "指标", @@ -7016,6 +7016,8 @@ "expressionRepeatImage.renderer.repeatImage.helpDescription": "重复图像给定次数", "expressionRevealImage.renderer.revealImage.displayName": "图像显示", "expressionRevealImage.renderer.revealImage.helpDescription": "显示一定百分比的图像,以制作定制的仪表样式图表", + "expressionShape.renderer.shape.displayName": "形状", + "expressionShape.renderer.shape.helpDescription": "呈现基本形状", "xpack.canvas.renderer.table.displayName": "数据表", "xpack.canvas.renderer.table.helpDescription": "将表格数据呈现为 {HTML}", "xpack.canvas.renderer.text.displayName": "纯文本", @@ -22133,7 +22135,6 @@ "xpack.securitySolution.eventFilter.form.updateSuccessToastTitle": "“{name}”已成功更新。", "xpack.securitySolution.eventFilter.modal.actions.cancel": "取消", "xpack.securitySolution.eventFilter.modal.actions.confirm": "添加终端事件筛选", - "xpack.securitySolution.eventFilter.modal.description": "满足规则的条件时将筛选事件:", "xpack.securitySolution.eventFilter.modal.subtitle": "Endpoint Security", "xpack.securitySolution.eventFilter.modal.title": "添加终端事件筛选", "xpack.securitySolution.eventFilter.search.placeholder": "搜索下面的字段:name、comments、value", diff --git a/x-pack/test/functional/page_objects/synthetics_integration_page.ts b/x-pack/test/functional/page_objects/synthetics_integration_page.ts index 3321234a345e4..81ddaf06febd9 100644 --- a/x-pack/test/functional/page_objects/synthetics_integration_page.ts +++ b/x-pack/test/functional/page_objects/synthetics_integration_page.ts @@ -24,25 +24,17 @@ export function SyntheticsIntegrationPageProvider({ * */ async navigateToPackagePage(packageVersion: string) { - await pageObjects.common.navigateToUrl( + await pageObjects.common.navigateToUrlWithBrowserHistory( 'fleet', - `/integrations/synthetics-${packageVersion}/add-integration`, - { - shouldUseHashForSubUrl: true, - useActualUrl: true, - } + `/integrations/synthetics-${packageVersion}/add-integration` ); await pageObjects.header.waitUntilLoadingHasFinished(); }, async navigateToPackageEditPage(packageId: string, agentId: string) { - await pageObjects.common.navigateToUrl( + await pageObjects.common.navigateToUrlWithBrowserHistory( 'fleet', - `/policies/${agentId}/edit-integration/${packageId}`, - { - shouldUseHashForSubUrl: true, - useActualUrl: true, - } + `/policies/${agentId}/edit-integration/${packageId}` ); await pageObjects.header.waitUntilLoadingHasFinished(); }, diff --git a/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts b/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts index 5abc842ddc9c9..81e868c5a38cb 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts @@ -17,9 +17,10 @@ export function FleetIntegrations({ getService, getPageObjects }: FtrProviderCon return { async navigateToIntegrationDetails(pkgkey: string) { - await pageObjects.common.navigateToApp(INTEGRATIONS_PLUGIN_ID, { - hash: pagePathGetters.integration_details_overview({ pkgkey })[1], - }); + await pageObjects.common.navigateToUrlWithBrowserHistory( + INTEGRATIONS_PLUGIN_ID, + pagePathGetters.integration_details_overview({ pkgkey })[1] + ); }, async integrationDetailCustomTabExistsOrFail() { diff --git a/yarn.lock b/yarn.lock index 4d7370d1f527b..04261da41ea05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1617,11 +1617,6 @@ history "^4.9.0" qs "^6.7.0" -"@elastic/ui-ace@0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@elastic/ui-ace/-/ui-ace-0.2.3.tgz#5281aed47a79b7216c55542b0675e435692f20cd" - integrity sha512-Nti5s2dplBPhSKRwJxG9JXTMOev4jVOWcnTJD1TOkJr1MUBYKVZcNcJtIVMSvahWGmP0B/UfO9q9lyRqdivkvQ== - "@emotion/babel-plugin-jsx-pragmatic@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin-jsx-pragmatic/-/babel-plugin-jsx-pragmatic-0.1.5.tgz#27debfe9c27c4d83574d509787ae553bf8a34d7e" @@ -6951,11 +6946,6 @@ angular-sortable-view@^0.0.17: resolved "https://registry.yarnpkg.com/angular-sortable-view/-/angular-sortable-view-0.0.17.tgz#99e2679951a86b6ee6ff27b099022943c683fb4f" integrity sha512-2WkhM0Lt/wyMyrX/+7ve9ejSegBd7A4eRBNHEIJz8XMBIOjt+3oM1WpcAm+qNThkmNmmQaDeaYv0TQZw/WDMBw== -angular-ui-ace@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/angular-ui-ace/-/angular-ui-ace-0.2.3.tgz#3cb903428100621a367fc7f641440e97a42a26d0" - integrity sha1-PLkDQoEAYho2f8f2QUQOl6QqJtA= - angular@>=1.0.6, angular@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/angular/-/angular-1.8.0.tgz#b1ec179887869215cab6dfd0df2e42caa65b1b51"