diff --git a/.eslintrc.js b/.eslintrc.js index f32b6498d998..51cd3440b87d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -897,6 +897,18 @@ module.exports = { ], }, }, + { + files: [ + 'x-pack/plugins/apm/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/observability/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/ux/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/synthetics/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/infra/**/*.{js,mjs,ts,tsx}', + ], + rules: { + '@kbn/telemetry/event_generating_elements_should_be_instrumented': 'error', + }, + }, { // require explicit return types in route handlers for performance reasons files: ['x-pack/plugins/apm/server/**/route.ts'], diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 914d4e78e1d0..2d003e7d696b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -334,6 +334,7 @@ packages/kbn-eslint-config @elastic/kibana-operations packages/kbn-eslint-plugin-disable @elastic/kibana-operations packages/kbn-eslint-plugin-eslint @elastic/kibana-operations packages/kbn-eslint-plugin-imports @elastic/kibana-operations +packages/kbn-eslint-plugin-telemetry @elastic/actionable-observability x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin @elastic/kibana-security src/plugins/event_annotation @elastic/kibana-visualizations x-pack/test/plugin_api_integration/plugins/event_log @elastic/response-ops diff --git a/package.json b/package.json index 967ffe0dd015..9f85e93bf8b4 100644 --- a/package.json +++ b/package.json @@ -1059,6 +1059,7 @@ "@kbn/eslint-plugin-disable": "link:packages/kbn-eslint-plugin-disable", "@kbn/eslint-plugin-eslint": "link:packages/kbn-eslint-plugin-eslint", "@kbn/eslint-plugin-imports": "link:packages/kbn-eslint-plugin-imports", + "@kbn/eslint-plugin-telemetry": "link:packages/kbn-eslint-plugin-telemetry", "@kbn/expect": "link:packages/kbn-expect", "@kbn/failed-test-reporter-cli": "link:packages/kbn-failed-test-reporter-cli", "@kbn/find-used-node-modules": "link:packages/kbn-find-used-node-modules", diff --git a/packages/kbn-eslint-config/.eslintrc.js b/packages/kbn-eslint-config/.eslintrc.js index 79369e3ed2ca..36504cb8b835 100644 --- a/packages/kbn-eslint-config/.eslintrc.js +++ b/packages/kbn-eslint-config/.eslintrc.js @@ -1,22 +1,18 @@ const { USES_STYLED_COMPONENTS } = require('@kbn/babel-preset/styled_components_files'); module.exports = { - extends: [ - './javascript.js', - './typescript.js', - './jest.js', - './react.js', - ], + extends: ['./javascript.js', './typescript.js', './jest.js', './react.js'], plugins: [ '@kbn/eslint-plugin-disable', '@kbn/eslint-plugin-eslint', '@kbn/eslint-plugin-imports', + '@kbn/eslint-plugin-telemetry', 'prettier', ], parserOptions: { - ecmaVersion: 2018 + ecmaVersion: 2018, }, env: { @@ -41,7 +37,7 @@ module.exports = { { from: 'mkdirp', to: false, - disallowedMessage: `Don't use 'mkdirp', use the new { recursive: true } option of Fs.mkdir instead` + disallowedMessage: `Don't use 'mkdirp', use the new { recursive: true } option of Fs.mkdir instead`, }, { from: 'numeral', @@ -50,7 +46,7 @@ module.exports = { { from: '@kbn/elastic-idx', to: false, - disallowedMessage: `Don't use idx(), use optional chaining syntax instead https://ela.st/optchain` + disallowedMessage: `Don't use idx(), use optional chaining syntax instead https://ela.st/optchain`, }, { from: 'x-pack', @@ -67,46 +63,45 @@ module.exports = { { from: 'monaco-editor', to: false, - disallowedMessage: `Don't import monaco directly, use or add exports to @kbn/monaco` + disallowedMessage: `Don't import monaco directly, use or add exports to @kbn/monaco`, }, { from: 'tinymath', to: '@kbn/tinymath', - disallowedMessage: `Don't use 'tinymath', use '@kbn/tinymath'` + disallowedMessage: `Don't use 'tinymath', use '@kbn/tinymath'`, }, { from: '@kbn/test/types/ftr', to: '@kbn/test', - disallowedMessage: `import from the root of @kbn/test instead` + disallowedMessage: `import from the root of @kbn/test instead`, }, { from: 'react-intl', to: '@kbn/i18n-react', - disallowedMessage: `import from @kbn/i18n-react instead` + disallowedMessage: `import from @kbn/i18n-react instead`, }, { from: 'styled-components', to: false, exclude: USES_STYLED_COMPONENTS, - disallowedMessage: `Prefer using @emotion/react instead. To use styled-components, ensure you plugin is enabled in packages/kbn-babel-preset/styled_components_files.js.` + disallowedMessage: `Prefer using @emotion/react instead. To use styled-components, ensure you plugin is enabled in packages/kbn-babel-preset/styled_components_files.js.`, }, - ...[ - '@elastic/eui/dist/eui_theme_light.json', - '@elastic/eui/dist/eui_theme_dark.json', - ].map(from => ({ - from, - to: false, - disallowedMessage: `Use "@kbn/ui-theme" to access theme vars.` - })), + ...['@elastic/eui/dist/eui_theme_light.json', '@elastic/eui/dist/eui_theme_dark.json'].map( + (from) => ({ + from, + to: false, + disallowedMessage: `Use "@kbn/ui-theme" to access theme vars.`, + }) + ), { from: '@kbn/test/jest', to: '@kbn/test-jest-helpers', - disallowedMessage: `import from @kbn/test-jest-helpers instead` + disallowedMessage: `import from @kbn/test-jest-helpers instead`, }, { from: '@kbn/utility-types/jest', to: '@kbn/utility-types-jest', - disallowedMessage: `import from @kbn/utility-types-jest instead` + disallowedMessage: `import from @kbn/utility-types-jest instead`, }, { from: '@kbn/inspector-plugin', @@ -149,142 +144,126 @@ module.exports = { * of the file being linted so that we could re-route imports from `plugin-client` types to a different package * than `plugin-server` types. */ - '@kbn/imports/exports_moved_packages': ['error', [ - { - from: '@kbn/dev-utils', - to: '@kbn/tooling-log', - exportNames: [ - 'DEFAULT_LOG_LEVEL', - 'getLogLevelFlagsHelp', - 'LOG_LEVEL_FLAGS', - 'LogLevel', - 'Message', - 'ParsedLogLevel', - 'parseLogLevel', - 'pickLevelFromFlags', - 'ToolingLog', - 'ToolingLogCollectingWriter', - 'ToolingLogOptions', - 'ToolingLogTextWriter', - 'ToolingLogTextWriterConfig', - 'Writer', - ] - }, - { - from: '@kbn/dev-utils', - to: '@kbn/ci-stats-reporter', - exportNames: [ - 'CiStatsMetric', - 'CiStatsReporter', - 'CiStatsReportTestsOptions', - 'CiStatsTestGroupInfo', - 'CiStatsTestResult', - 'CiStatsTestRun', - 'CiStatsTestType', - 'CiStatsTiming', - 'getTimeReporter', - 'MetricsOptions', - 'TimingsOptions', - ] - }, - { - from: '@kbn/dev-utils', - to: '@kbn/ci-stats-core', - exportNames: [ - 'Config', - ] - }, - { - from: '@kbn/dev-utils', - to: '@kbn/jest-serializers', - exportNames: [ - 'createAbsolutePathSerializer', - 'createStripAnsiSerializer', - 'createRecursiveSerializer', - 'createAnyInstanceSerializer', - 'createReplaceSerializer', - ] - }, - { - from: '@kbn/dev-utils', - to: '@kbn/stdio-dev-helpers', - exportNames: [ - 'observeReadable', - 'observeLines', - ] - }, - { - from: '@kbn/dev-utils', - to: '@kbn/sort-package-json', - exportNames: [ - 'sortPackageJson', - ] - }, - { - from: '@kbn/dev-utils', - to: '@kbn/dev-cli-runner', - exportNames: [ - 'run', - 'Command', - 'RunWithCommands', - 'CleanupTask', - 'Command', - 'CommandRunFn', - 'FlagOptions', - 'Flags', - 'RunContext', - 'RunFn', - 'RunOptions', - 'RunWithCommands', - 'RunWithCommandsOptions', - 'getFlags', - 'mergeFlagOptions' - ] - }, - { - from: '@kbn/dev-utils', - to: '@kbn/dev-cli-errors', - exportNames: [ - 'createFailError', - 'createFlagError', - 'isFailError', - ] - }, - { - from: '@kbn/dev-utils', - to: '@kbn/dev-proc-runner', - exportNames: [ - 'withProcRunner', - 'ProcRunner', - ] - }, - { - from: '@kbn/utils', - to: '@kbn/repo-info', - exportNames: [ - 'REPO_ROOT', - 'UPSTREAM_BRANCH', - 'kibanaPackageJson', - 'isKibanaDistributable', - 'fromRoot', - ] - }, - { - from: '@kbn/presentation-util-plugin/common', - to: '@kbn/presentation-util-plugin/test_helpers', - exportNames: [ - 'functionWrapper', - 'fontStyle' - ] - }, - { - from: '@kbn/fleet-plugin/common', - to: '@kbn/fleet-plugin/common/mocks', - exportNames: [ - 'createFleetAuthzMock' - ] - } - ]], + '@kbn/imports/exports_moved_packages': [ + 'error', + [ + { + from: '@kbn/dev-utils', + to: '@kbn/tooling-log', + exportNames: [ + 'DEFAULT_LOG_LEVEL', + 'getLogLevelFlagsHelp', + 'LOG_LEVEL_FLAGS', + 'LogLevel', + 'Message', + 'ParsedLogLevel', + 'parseLogLevel', + 'pickLevelFromFlags', + 'ToolingLog', + 'ToolingLogCollectingWriter', + 'ToolingLogOptions', + 'ToolingLogTextWriter', + 'ToolingLogTextWriterConfig', + 'Writer', + ], + }, + { + from: '@kbn/dev-utils', + to: '@kbn/ci-stats-reporter', + exportNames: [ + 'CiStatsMetric', + 'CiStatsReporter', + 'CiStatsReportTestsOptions', + 'CiStatsTestGroupInfo', + 'CiStatsTestResult', + 'CiStatsTestRun', + 'CiStatsTestType', + 'CiStatsTiming', + 'getTimeReporter', + 'MetricsOptions', + 'TimingsOptions', + ], + }, + { + from: '@kbn/dev-utils', + to: '@kbn/ci-stats-core', + exportNames: ['Config'], + }, + { + from: '@kbn/dev-utils', + to: '@kbn/jest-serializers', + exportNames: [ + 'createAbsolutePathSerializer', + 'createStripAnsiSerializer', + 'createRecursiveSerializer', + 'createAnyInstanceSerializer', + 'createReplaceSerializer', + ], + }, + { + from: '@kbn/dev-utils', + to: '@kbn/stdio-dev-helpers', + exportNames: ['observeReadable', 'observeLines'], + }, + { + from: '@kbn/dev-utils', + to: '@kbn/sort-package-json', + exportNames: ['sortPackageJson'], + }, + { + from: '@kbn/dev-utils', + to: '@kbn/dev-cli-runner', + exportNames: [ + 'run', + 'Command', + 'RunWithCommands', + 'CleanupTask', + 'Command', + 'CommandRunFn', + 'FlagOptions', + 'Flags', + 'RunContext', + 'RunFn', + 'RunOptions', + 'RunWithCommands', + 'RunWithCommandsOptions', + 'getFlags', + 'mergeFlagOptions', + ], + }, + { + from: '@kbn/dev-utils', + to: '@kbn/dev-cli-errors', + exportNames: ['createFailError', 'createFlagError', 'isFailError'], + }, + { + from: '@kbn/dev-utils', + to: '@kbn/dev-proc-runner', + exportNames: ['withProcRunner', 'ProcRunner'], + }, + { + from: '@kbn/utils', + to: '@kbn/repo-info', + exportNames: [ + 'REPO_ROOT', + 'UPSTREAM_BRANCH', + 'kibanaPackageJson', + 'isKibanaDistributable', + 'fromRoot', + ], + }, + { + from: '@kbn/presentation-util-plugin/common', + to: '@kbn/presentation-util-plugin/test_helpers', + exportNames: ['functionWrapper', 'fontStyle'], + }, + { + from: '@kbn/fleet-plugin/common', + to: '@kbn/fleet-plugin/common/mocks', + exportNames: ['createFleetAuthzMock'], + }, + ], + ], '@kbn/disable/no_protected_eslint_disable': 'error', '@kbn/disable/no_naked_eslint_disable': 'error', diff --git a/packages/kbn-eslint-plugin-telemetry/README.mdx b/packages/kbn-eslint-plugin-telemetry/README.mdx new file mode 100644 index 000000000000..11127b2492a5 --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/README.mdx @@ -0,0 +1,13 @@ +--- +id: kibDevDocsOpsEslintPluginTelemetry +slug: /kibana-dev-docs/ops/kbn-eslint-plugin-telemetry +title: '@kbn/eslint-plugin-telemetry' +description: Custom ESLint rules to support telemetry in the Kibana repository +tags: ['kibana', 'dev', 'contributor', 'operations', 'eslint', 'telemetry'] +--- + +`@kbn/eslint-plugin-telemetry` is an ESLint plugin providing custom rules for validating JSXCode in the Kibana repo to make sure it can be instrumented for the purposes of telemetry. + +## `@kbn/telemetry/instrumentable_elements_should_be_instrumented` + +This rule warns engineers to add `data-test-subj` to instrumentable components. It currently suggests the most widely used EUI components (`EuiButton`, `EuiFieldText`, etc), but can be expanded with often-used specific components used in the Kibana repo. diff --git a/packages/kbn-eslint-plugin-telemetry/helpers/check_node_for_existing_data_test_subj_prop.ts b/packages/kbn-eslint-plugin-telemetry/helpers/check_node_for_existing_data_test_subj_prop.ts new file mode 100644 index 000000000000..b739dc5116c1 --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/helpers/check_node_for_existing_data_test_subj_prop.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 type { Scope } from 'eslint'; +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; + +export function checkNodeForExistingDataTestSubjProp( + node: TSESTree.JSXOpeningElement, + getScope: () => Scope.Scope +): boolean { + const hasJsxDataTestSubjProp = node.attributes.find( + (attr) => attr.type === AST_NODE_TYPES.JSXAttribute && attr.name.name === 'data-test-subj' + ); + + if (hasJsxDataTestSubjProp) { + return true; + } + + const spreadedVariable = node.attributes.find( + (attr) => attr.type === AST_NODE_TYPES.JSXSpreadAttribute + ); + + if ( + !spreadedVariable || + !('argument' in spreadedVariable) || + !('name' in spreadedVariable.argument) + ) { + return false; + } + + const { name } = spreadedVariable.argument; // The name of the spreaded variable + + const variable = getScope().variables.find((v) => v.name === name); // the variable definition of the spreaded variable + + return variable && variable.defs.length > 0 + ? variable.defs[0].node.init.properties.find((property: TSESTree.Property) => { + if ('value' in property.key) { + return property.key.value === 'data-test-subj'; + } + return false; + }) + : false; +} diff --git a/packages/kbn-eslint-plugin-telemetry/helpers/get_app_name.test.ts b/packages/kbn-eslint-plugin-telemetry/helpers/get_app_name.test.ts new file mode 100644 index 000000000000..36790e883cce --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/helpers/get_app_name.test.ts @@ -0,0 +1,28 @@ +/* + * 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 { getAppName } from './get_app_name'; + +const SYSTEMPATH = 'systemPath'; + +const testMap = [ + ['x-pack/plugins/observability/foo/bar/baz/header_actions.tsx', 'o11y'], + ['x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx', 'apm'], + ['x-pack/plugins/cases/public/components/foo.tsx', 'cases'], + ['packages/kbn-alerts-ui-shared/src/alert_lifecycle_status_badge/index.tsx', 'kbnAlertsUiShared'], +]; + +describe('Get App Name', () => { + test.each(testMap)( + 'should get the responsible app name from a file path', + (path, expectedValue) => { + const appName = getAppName(`${SYSTEMPATH}/${path}`, SYSTEMPATH); + expect(appName).toBe(expectedValue); + } + ); +}); diff --git a/packages/kbn-eslint-plugin-telemetry/helpers/get_app_name.ts b/packages/kbn-eslint-plugin-telemetry/helpers/get_app_name.ts new file mode 100644 index 000000000000..e483a572be89 --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/helpers/get_app_name.ts @@ -0,0 +1,49 @@ +/* + * 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 { camelCase } from 'lodash'; +import path from 'path'; +import { getPkgDirMap } from '@kbn/repo-packages'; +import { REPO_ROOT } from '@kbn/repo-info'; + +export function getAppName(fileName: string, cwd: string) { + const { dir } = path.parse(fileName); + const relativePathToFile = dir.replace(cwd, ''); + + const packageDirs = Array.from( + Array.from(getPkgDirMap(REPO_ROOT).values()).reduce((acc, currentDir) => { + const topDirectory = currentDir.normalizedRepoRelativeDir.split('/')[0]; + + if (topDirectory) { + acc.add(topDirectory); + } + + return acc; + }, new Set()) + ); + + const relativePathArray = relativePathToFile.split('/'); + + const appName = camelCase( + packageDirs.reduce((acc, repoPath) => { + if (!relativePathArray[1]) return ''; + + if (relativePathArray[1] === 'x-pack') { + return relativePathArray[3]; + } + + if (relativePathArray[1].includes(repoPath)) { + return relativePathArray[2]; + } + + return acc; + }, '') + ); + + return appName === 'observability' ? 'o11y' : appName; +} diff --git a/packages/kbn-eslint-plugin-telemetry/helpers/get_function_name.ts b/packages/kbn-eslint-plugin-telemetry/helpers/get_function_name.ts new file mode 100644 index 000000000000..40bb3e8a17e9 --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/helpers/get_function_name.ts @@ -0,0 +1,34 @@ +/* + * 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 { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; + +export function getFunctionName(func: TSESTree.FunctionDeclaration | TSESTree.Node): string { + if ( + 'id' in func && + func.id && + func.type === AST_NODE_TYPES.FunctionDeclaration && + func.id.type === AST_NODE_TYPES.Identifier + ) { + return func.id.name; + } + + if ( + func.parent && + (func.parent.type !== AST_NODE_TYPES.VariableDeclarator || + func.parent.id.type !== AST_NODE_TYPES.Identifier) + ) { + return getFunctionName(func.parent); + } + + if (func.parent?.id && 'name' in func.parent.id) { + return func.parent.id.name; + } + + return ''; +} diff --git a/packages/kbn-eslint-plugin-telemetry/helpers/get_intent_from_node.ts b/packages/kbn-eslint-plugin-telemetry/helpers/get_intent_from_node.ts new file mode 100644 index 000000000000..5df44e5dd0f7 --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/helpers/get_intent_from_node.ts @@ -0,0 +1,133 @@ +/* + * 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 { TSESTree } from '@typescript-eslint/typescript-estree'; +import camelCase from 'lodash/camelCase'; + +/* + Attempts to get a string representation of the intent + out of an array of nodes. + + Currently supported node types in the array: + * String literal text (JSXText) + * Translated text via component -> uses prop `defaultMessage` + * Translated text via {i18n.translate} call -> uses passed options object key `defaultMessage` +*/ +export function getIntentFromNode(originalNode: TSESTree.JSXOpeningElement): string { + const parent = originalNode.parent as TSESTree.JSXElement; + + const node = Array.isArray(parent.children) ? parent.children : []; + + if (node.length === 0) { + return ''; + } + + /* + In order to satisfy TS we need to do quite a bit of defensive programming. + This is my best attempt at providing the minimum amount of typeguards and + keeping the code readable. In the cases where types are explicitly set to + variables, it was done to help the compiler when it couldn't infer the type. + */ + return node.reduce((acc: string, currentNode) => { + switch (currentNode.type) { + case 'JSXText': + // When node is a string primitive + return `${acc}${strip(currentNode.value)}`; + + case 'JSXElement': + // Determining whether node is of form `` + const name: TSESTree.JSXTagNameExpression = currentNode.openingElement.name; + const attributes: Array = + currentNode.openingElement.attributes; + + if (!('name' in name) || name.name !== 'FormattedMessage') { + return ''; + } + + const defaultMessageProp = attributes.find( + (attribute) => 'name' in attribute && attribute.name.name === 'defaultMessage' + ); + + if ( + !defaultMessageProp || + !('value' in defaultMessageProp) || + !('type' in defaultMessageProp.value!) || + defaultMessageProp.value.type !== 'Literal' || + typeof defaultMessageProp.value.value !== 'string' + ) { + return ''; + } + + return `${acc}${strip(defaultMessageProp.value.value)}`; + + case 'JSXExpressionContainer': + // Determining whether node is of form `{i18n.translate('foo', { defaultMessage: 'message'})}` + const expression: TSESTree.JSXEmptyExpression | TSESTree.Expression = + currentNode.expression; + + if (!('arguments' in expression)) { + return ''; + } + + const args: TSESTree.CallExpressionArgument[] = expression.arguments; + const callee: TSESTree.LeftHandSideExpression = expression.callee; + + if (!('object' in callee)) { + return ''; + } + + const object: TSESTree.LeftHandSideExpression = callee.object; + const property: TSESTree.Expression | TSESTree.PrivateIdentifier = callee.property; + + if (!('name' in object) || !('name' in property)) { + return ''; + } + + if (object.name !== 'i18n' || property.name !== 'translate') { + return ''; + } + + const callExpressionArgument: TSESTree.CallExpressionArgument | undefined = args.find( + (arg) => arg.type === 'ObjectExpression' + ); + + if (!callExpressionArgument || callExpressionArgument.type !== 'ObjectExpression') { + return ''; + } + + const defaultMessageValue: TSESTree.ObjectLiteralElement | undefined = + callExpressionArgument.properties.find( + (prop) => + prop.type === 'Property' && 'name' in prop.key && prop.key.name === 'defaultMessage' + ); + + if ( + !defaultMessageValue || + !('value' in defaultMessageValue) || + defaultMessageValue.value.type !== 'Literal' || + typeof defaultMessageValue.value.value !== 'string' + ) { + return ''; + } + + return `${acc}${strip(defaultMessageValue.value.value)}`; + + default: + break; + } + + return acc; + }, ''); +} + +function strip(input: string): string { + if (!input) return ''; + + const cleanedString = camelCase(input); + + return `${cleanedString.charAt(0).toUpperCase()}${cleanedString.slice(1)}`; +} diff --git a/packages/kbn-eslint-plugin-telemetry/index.ts b/packages/kbn-eslint-plugin-telemetry/index.ts new file mode 100644 index 000000000000..ea68eae7fdfa --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/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 { EventGeneratingElementsShouldBeInstrumented } from './rules/event_generating_elements_should_be_instrumented'; + +/** + * Custom ESLint rules, add `'@kbn/eslint-plugin-telemetry'` to your eslint config to use them + * @internal + */ +export const rules = { + event_generating_elements_should_be_instrumented: EventGeneratingElementsShouldBeInstrumented, +}; diff --git a/packages/kbn-eslint-plugin-telemetry/jest.config.js b/packages/kbn-eslint-plugin-telemetry/jest.config.js new file mode 100644 index 000000000000..f2778eaf7dda --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/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/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-eslint-plugin-telemetry'], +}; diff --git a/packages/kbn-eslint-plugin-telemetry/kibana.jsonc b/packages/kbn-eslint-plugin-telemetry/kibana.jsonc new file mode 100644 index 000000000000..79c8fbf8adb2 --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/kibana.jsonc @@ -0,0 +1,6 @@ +{ + "type": "shared-common", + "id": "@kbn/eslint-plugin-telemetry", + "owner": "@elastic/actionable-observability", + "devOnly": true +} diff --git a/packages/kbn-eslint-plugin-telemetry/package.json b/packages/kbn-eslint-plugin-telemetry/package.json new file mode 100644 index 000000000000..9632bf6d0f5c --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/eslint-plugin-telemetry", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-eslint-plugin-telemetry/rules/event_generating_elements_should_be_instrumented.test.ts b/packages/kbn-eslint-plugin-telemetry/rules/event_generating_elements_should_be_instrumented.test.ts new file mode 100644 index 000000000000..c8829f05efd2 --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/rules/event_generating_elements_should_be_instrumented.test.ts @@ -0,0 +1,71 @@ +/* + * 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 { RuleTester } from 'eslint'; +import { + EventGeneratingElementsShouldBeInstrumented, + EVENT_GENERATING_ELEMENTS, +} from './event_generating_elements_should_be_instrumented'; + +const tsTester = [ + '@typescript-eslint/parser', + new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + }, + }, + }), +] as const; + +const babelTester = [ + '@babel/eslint-parser', + new RuleTester({ + parser: require.resolve('@babel/eslint-parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + requireConfigFile: false, + babelOptions: { + presets: ['@kbn/babel-preset/node_preset'], + }, + }, + }), +] as const; + +for (const [name, tester] of [tsTester, babelTester]) { + describe(name, () => { + tester.run( + '@kbn/event_generating_elements_should_be_instrumented', + EventGeneratingElementsShouldBeInstrumented, + { + valid: EVENT_GENERATING_ELEMENTS.map((element) => ({ + filename: 'foo.tsx', + code: `<${element} data-test-subj="foo" />`, + })), + + invalid: EVENT_GENERATING_ELEMENTS.map((element) => ({ + filename: 'foo.tsx', + code: `<${element}>Value`, + errors: [ + { + line: 1, + message: `<${element}> should have a \`data-test-subj\` for telemetry purposes. Use the autofix suggestion or add your own.`, + }, + ], + output: `<${element} data-test-subj="Value${element + .replace('Eui', '') + .replace('Empty', '')}">Value`, + })), + } + ); + }); +} diff --git a/packages/kbn-eslint-plugin-telemetry/rules/event_generating_elements_should_be_instrumented.ts b/packages/kbn-eslint-plugin-telemetry/rules/event_generating_elements_should_be_instrumented.ts new file mode 100644 index 000000000000..d2069d2845e5 --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/rules/event_generating_elements_should_be_instrumented.ts @@ -0,0 +1,92 @@ +/* + * 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 type { Rule } from 'eslint'; +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; + +import { checkNodeForExistingDataTestSubjProp } from '../helpers/check_node_for_existing_data_test_subj_prop'; +import { getIntentFromNode } from '../helpers/get_intent_from_node'; +import { getAppName } from '../helpers/get_app_name'; +import { getFunctionName } from '../helpers/get_function_name'; + +export const EVENT_GENERATING_ELEMENTS = [ + 'EuiButton', + 'EuiButtonEmpty', + 'EuiLink', + 'EuiFieldText', + 'EuiFieldSearch', + 'EuiFieldNumber', + 'EuiSelect', + 'EuiRadioGroup', + 'EuiTextArea', +]; + +export const EventGeneratingElementsShouldBeInstrumented: Rule.RuleModule = { + meta: { + type: 'suggestion', + fixable: 'code', + }, + create(context) { + const { getCwd, getFilename, getScope, report } = context; + + return { + JSXIdentifier: (node: TSESTree.Node) => { + if (!('name' in node)) { + return; + } + + const name = String(node.name); + const range = node.range; + const parent = node.parent; + + if ( + parent?.type !== AST_NODE_TYPES.JSXOpeningElement || + !EVENT_GENERATING_ELEMENTS.includes(name) + ) { + return; + } + + const hasDataTestSubjProp = checkNodeForExistingDataTestSubjProp(parent, getScope); + + if (hasDataTestSubjProp) { + // JSXOpeningElement already has a prop for data-test-subj. Bail. + return; + } + + // Start building the suggestion. + + // 1. The app name + const cwd = getCwd(); + const fileName = getFilename(); + const appName = getAppName(fileName, cwd); + + // 2. Component name + const functionDeclaration = getScope().block as TSESTree.FunctionDeclaration; + const functionName = getFunctionName(functionDeclaration); + const componentName = `${functionName.charAt(0).toUpperCase()}${functionName.slice(1)}`; + + // 3. The intention of the element (i.e. "Select date", "Submit", "Cancel") + const intent = getIntentFromNode(parent); + + // 4. The element name that generates the events + const element = name.replace('Eui', '').replace('Empty', ''); + + const suggestion = `${appName}${componentName}${intent}${element}`; // 'o11yHeaderActionsSubmitButton' + + // 6. Report feedback to engineer + report({ + node: node as any, + message: `<${name}> should have a \`data-test-subj\` for telemetry purposes. Use the autofix suggestion or add your own.`, + fix(fixer) { + return fixer.insertTextAfterRange(range, ` data-test-subj="${suggestion}"`); + }, + }); + }, + } as Rule.RuleListener; + }, +}; diff --git a/packages/kbn-eslint-plugin-telemetry/tsconfig.json b/packages/kbn-eslint-plugin-telemetry/tsconfig.json new file mode 100644 index 000000000000..75014884aa31 --- /dev/null +++ b/packages/kbn-eslint-plugin-telemetry/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": ["jest", "node"], + "lib": ["es2021"] + }, + "include": ["**/*.ts"], + "exclude": ["target/**/*"], + "kbn_references": ["@kbn/repo-packages", "@kbn/repo-info"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index e4ad01b21f5a..c697ddcb1a1a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -662,6 +662,8 @@ "@kbn/eslint-plugin-eslint/*": ["packages/kbn-eslint-plugin-eslint/*"], "@kbn/eslint-plugin-imports": ["packages/kbn-eslint-plugin-imports"], "@kbn/eslint-plugin-imports/*": ["packages/kbn-eslint-plugin-imports/*"], + "@kbn/eslint-plugin-telemetry": ["packages/kbn-eslint-plugin-telemetry"], + "@kbn/eslint-plugin-telemetry/*": ["packages/kbn-eslint-plugin-telemetry/*"], "@kbn/eso-plugin": ["x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin"], "@kbn/eso-plugin/*": ["x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin/*"], "@kbn/event-annotation-plugin": ["src/plugins/event_annotation"], diff --git a/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_duration_rule_type/index.tsx b/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_duration_rule_type/index.tsx index 1045eff2cc6c..a94cad876736 100644 --- a/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_duration_rule_type/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_duration_rule_type/index.tsx @@ -171,6 +171,7 @@ export function TransactionDurationRuleType(props: Props) { })} > { return { diff --git a/x-pack/plugins/apm/public/components/alerting/utils/fields.tsx b/x-pack/plugins/apm/public/components/alerting/utils/fields.tsx index 3ff3275f8dc8..a385bfaa4d95 100644 --- a/x-pack/plugins/apm/public/components/alerting/utils/fields.tsx +++ b/x-pack/plugins/apm/public/components/alerting/utils/fields.tsx @@ -156,6 +156,7 @@ export function IsAboveField({ })} > onChange(parseInt(e.target.value, 10))} diff --git a/x-pack/plugins/apm/public/components/app/correlations/progress_controls.tsx b/x-pack/plugins/apm/public/components/app/correlations/progress_controls.tsx index 101713c34597..8dc8669c9ecd 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/progress_controls.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/progress_controls.tsx @@ -56,7 +56,11 @@ export function CorrelationsProgressControls({ {!isRunning && ( - + )} {isRunning && ( - + {spanName}; + return ( + + {spanName} + + ); } diff --git a/x-pack/plugins/apm/public/components/app/dependency_operation_detail_view/detail_view_header/index.tsx b/x-pack/plugins/apm/public/components/app/dependency_operation_detail_view/detail_view_header/index.tsx index 8e8ed13b9b3f..47becdf9b306 100644 --- a/x-pack/plugins/apm/public/components/app/dependency_operation_detail_view/detail_view_header/index.tsx +++ b/x-pack/plugins/apm/public/components/app/dependency_operation_detail_view/detail_view_header/index.tsx @@ -26,7 +26,7 @@ export function DetailViewHeader({ return ( - + diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx index 5366f467826c..4251d777602b 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx @@ -187,7 +187,10 @@ export function ErrorSampleDetails({ {isTraceExplorerEnabled && ( - + diff --git a/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx b/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx index 57f419747624..3a972b44f9a4 100644 --- a/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx +++ b/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx @@ -37,6 +37,7 @@ export function HelpPopoverButton({ if (buttonTextEnabled) { return ( - + {i18n.translate('xpack.apm.serverlessMetrics.summary.feedback', { defaultMessage: 'Give feedback', })} diff --git a/x-pack/plugins/apm/public/components/app/metrics_details/service_node_metrics/index.tsx b/x-pack/plugins/apm/public/components/app/metrics_details/service_node_metrics/index.tsx index 1db9c8690891..28865a8ad5f1 100644 --- a/x-pack/plugins/apm/public/components/app/metrics_details/service_node_metrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/metrics_details/service_node_metrics/index.tsx @@ -127,7 +127,10 @@ export function ServiceNodeMetrics({ serviceNodeName }: Props) { defaultMessage="We could not identify which JVMs these metrics belong to. This is likely caused by running a version of APM Server that is older than 7.5. Upgrading to APM Server 7.5 or higher should resolve this issue. For more information on upgrading, see the {link}. As an alternative, you can use the Kibana Query bar to filter by hostname, container ID or other fields." values={{ link: ( - + {i18n.translate( 'xpack.apm.serviceNodeMetrics.unidentifiedServiceNodesWarningDocumentationLink', { defaultMessage: 'documentation of APM Server' } diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/filters/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/filters/index.tsx index 0fbab1465226..9dcad10a9fa7 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/service_overview/filters/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/service_overview/filters/index.tsx @@ -133,6 +133,7 @@ export function MobileFilters() { style={isLarge ? {} : { width: '225px' }} > + {i18n.translate( 'xpack.apm.serviceOverview.mobileCallOutLink', { @@ -320,7 +323,10 @@ export function MobileServiceOverview() { fixedHeight={true} showPerPageOptions={false} link={ - + {i18n.translate( 'xpack.apm.serviceOverview.dependenciesTableTabLink', { defaultMessage: 'View dependencies' } diff --git a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/edit_button.tsx b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/edit_button.tsx index 8325ffd40195..07b8d8c87f3d 100644 --- a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/edit_button.tsx +++ b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/edit_button.tsx @@ -32,6 +32,7 @@ export function EditButton({ onClick }: Props) { )} > { dismissTour(); diff --git a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/group_details.tsx b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/group_details.tsx index 36f429b63409..8bad458d5e0e 100644 --- a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/group_details.tsx +++ b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/group_details.tsx @@ -149,6 +149,7 @@ export function GroupDetails({ } > { @@ -181,7 +182,11 @@ export function GroupDetails({ )} - + {i18n.translate( 'xpack.apm.serviceGroups.groupDetailsForm.cancel', { defaultMessage: 'Cancel' } @@ -190,6 +195,7 @@ export function GroupDetails({ { setKuery(stagedKuery); }} @@ -244,6 +245,7 @@ export function SelectServices({
- + {i18n.translate( 'xpack.apm.serviceGroups.selectServicesForm.cancel', { @@ -268,6 +274,7 @@ export function SelectServices({ { onSaveClick({ ...serviceGroup, kuery }); diff --git a/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/index.tsx b/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/index.tsx index cad9cdb6c7db..cde5fa6733ce 100644 --- a/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/index.tsx @@ -106,6 +106,7 @@ export function ServiceGroupsList() { } > diff --git a/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/sort.tsx b/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/sort.tsx index f87a7b767c93..6b635798c135 100644 --- a/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/sort.tsx +++ b/x-pack/plugins/apm/public/components/app/service_groups/service_groups_list/sort.tsx @@ -35,6 +35,7 @@ const options: Array<{ export function Sort({ type, onChange }: Props) { return ( onChange(e.target.value as ServiceGroupsSortType)} diff --git a/x-pack/plugins/apm/public/components/app/service_groups/service_groups_tour.tsx b/x-pack/plugins/apm/public/components/app/service_groups/service_groups_tour.tsx index 4f7a457be49b..21cdc434a205 100644 --- a/x-pack/plugins/apm/public/components/app/service_groups/service_groups_tour.tsx +++ b/x-pack/plugins/apm/public/components/app/service_groups/service_groups_tour.tsx @@ -71,7 +71,12 @@ export function ServiceGroupsTour({ title={title} anchorPosition={anchorPosition} footerAction={ - + {i18n.translate('xpack.apm.serviceGroups.tour.dismiss', { defaultMessage: 'Dismiss', })} diff --git a/x-pack/plugins/apm/public/components/app/service_map/__stories__/cytoscape_example_data.stories.tsx b/x-pack/plugins/apm/public/components/app/service_map/__stories__/cytoscape_example_data.stories.tsx index 2b3daac15dc0..995b82d960e6 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/__stories__/cytoscape_example_data.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/__stories__/cytoscape_example_data.stories.tsx @@ -67,6 +67,7 @@ export const GenerateMap: Story<{}> = () => { { setElements( generateServiceMapElements({ size, hasAnomalies: true }) @@ -80,6 +81,7 @@ export const GenerateMap: Story<{}> = () => { setSize(e.target.valueAsNumber)} @@ -88,6 +90,7 @@ export const GenerateMap: Story<{}> = () => { { setJson(JSON.stringify({ elements }, null, 2)); }} @@ -183,6 +186,7 @@ export const MapFromJSON: Story<{}> = () => { /> { updateRenderedElements(); }} diff --git a/x-pack/plugins/apm/public/components/app/service_map/empty_banner.tsx b/x-pack/plugins/apm/public/components/app/service_map/empty_banner.tsx index 84b0bcbd0dbc..2e582551c8a0 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/empty_banner.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/empty_banner.tsx @@ -68,7 +68,10 @@ export function EmptyBanner() { defaultMessage: "We will map out connected services and external requests if we can detect them. Please make sure you're running the latest version of the APM agent.", })}{' '} - + {i18n.translate('xpack.apm.serviceMap.emptyBanner.docsLink', { defaultMessage: 'Learn more in the docs', })} diff --git a/x-pack/plugins/apm/public/components/app/service_map/popover/dependency_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/popover/dependency_contents.tsx index 7141c856a36f..9ec7773f91c8 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/popover/dependency_contents.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/popover/dependency_contents.tsx @@ -92,6 +92,7 @@ export function DependencyContents({ {/* eslint-disable-next-line @elastic/eui/href-or-on-click*/} { diff --git a/x-pack/plugins/apm/public/components/app/service_map/popover/edge_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/popover/edge_contents.tsx index aeb9a771bf31..e5e3de188de4 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/popover/edge_contents.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/popover/edge_contents.tsx @@ -67,6 +67,7 @@ export function EdgeContents({ elementData }: ContentsProps) { {/* eslint-disable-next-line @elastic/eui/href-or-on-click*/} { diff --git a/x-pack/plugins/apm/public/components/app/service_map/popover/service_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/popover/service_contents.tsx index f5310fad5b22..917fdf651a78 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/popover/service_contents.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/popover/service_contents.tsx @@ -129,14 +129,23 @@ export function ServiceContents({ - + {i18n.translate('xpack.apm.serviceMap.serviceDetailsButtonText', { defaultMessage: 'Service Details', })} - + {i18n.translate('xpack.apm.serviceMap.focusMapButtonText', { defaultMessage: 'Focus map', })} diff --git a/x-pack/plugins/apm/public/components/app/service_map/timeout_prompt.tsx b/x-pack/plugins/apm/public/components/app/service_map/timeout_prompt.tsx index 31c51f773ab0..cec31e599bb2 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/timeout_prompt.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/timeout_prompt.tsx @@ -46,7 +46,10 @@ export function TimeoutPrompt({ function ApmSettingsDocLink() { const { docLinks } = useApmPluginContext().core; return ( - + {i18n.translate('xpack.apm.serviceMap.timeoutPrompt.docsLink', { defaultMessage: 'Learn more about APM settings in the docs', })} diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index 3e2d178357a8..40580ef70014 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -174,7 +174,10 @@ export function ServiceOverview() { fixedHeight={true} showPerPageOptions={false} link={ - + {i18n.translate( 'xpack.apm.serviceOverview.dependenciesTableTabLink', { defaultMessage: 'View dependencies' } diff --git a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx index a7603163f371..27e942ba49a9 100644 --- a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx @@ -188,7 +188,10 @@ export function ServicePage({ newConfig, setNewConfig, onClickNext }: Props) { {/* Cancel button */} - + {i18n.translate( 'xpack.apm.agentConfig.servicePage.cancelButton', { defaultMessage: 'Cancel' } @@ -200,6 +203,7 @@ export function ServicePage({ newConfig, setNewConfig, onClickNext }: Props) { {/* Next button */} onChange(setting.key, e.target.value)} @@ -51,6 +52,7 @@ function FormRow({ case 'integer': { return ( diff --git a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/settings_page/settings_page.tsx b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/settings_page/settings_page.tsx index 60ea88028509..0130e8051057 100644 --- a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/settings_page/settings_page.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/settings_page/settings_page.tsx @@ -160,7 +160,11 @@ export function SettingsPage({ {!isEditMode && ( - + {i18n.translate( 'xpack.apm.agentConfig.chooseService.editButton', { defaultMessage: 'Edit' } diff --git a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/index.tsx b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/index.tsx index 48b421ca0461..74359d03e1a2 100644 --- a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/index.tsx @@ -96,6 +96,7 @@ function CreateConfigurationButton() { } > ( - + {i18n.translate( 'xpack.apm.settings.agentKeys.createKeyFlyout.cancelButton', { @@ -219,6 +223,7 @@ export function CreateAgentKeyFlyout({ onCancel, onSuccess, onError }: Props) { setIsFlyoutVisible(true)} fill={true} iconType="plusInCircle" @@ -238,6 +239,7 @@ function AgentKeysContent({ } actions={ diff --git a/x-pack/plugins/apm/public/components/app/settings/anomaly_detection/add_environments.tsx b/x-pack/plugins/apm/public/components/app/settings/anomaly_detection/add_environments.tsx index f3afd70cfbba..7fbbedfeca27 100644 --- a/x-pack/plugins/apm/public/components/app/settings/anomaly_detection/add_environments.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/anomaly_detection/add_environments.tsx @@ -136,7 +136,11 @@ export function AddEnvironments({ - + {i18n.translate( 'xpack.apm.settings.anomalyDetection.addEnvironments.cancelButtonText', { @@ -147,6 +151,7 @@ export function AddEnvironments({ - + {i18n.translate( 'xpack.apm.settings.anomalyDetection.jobList.manageMlJobsButtonText', { @@ -241,7 +245,12 @@ export function JobsList({ - + {i18n.translate( 'xpack.apm.settings.anomalyDetection.jobList.addEnvironments', { diff --git a/x-pack/plugins/apm/public/components/app/settings/apm_indices/index.tsx b/x-pack/plugins/apm/public/components/app/settings/apm_indices/index.tsx index f551a9b6c51f..1ea865b533ab 100644 --- a/x-pack/plugins/apm/public/components/app/settings/apm_indices/index.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/apm_indices/index.tsx @@ -242,6 +242,7 @@ export function ApmIndices() { fullWidth > - + {i18n.translate( 'xpack.apm.settings.apmIndices.cancelButton', { defaultMessage: 'Cancel' } @@ -276,6 +280,7 @@ export function ApmIndices() { } > - + {i18n.translate( 'xpack.apm.bottomBarActions.discardChangesButton', { @@ -64,6 +68,7 @@ export function BottomBarActions({ {label}; + return ( + + {label} + + ); } diff --git a/x-pack/plugins/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/filters_section.tsx b/x-pack/plugins/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/filters_section.tsx index 5b2ceb5f353d..901e25f52eb8 100644 --- a/x-pack/plugins/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/filters_section.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/filters_section.tsx @@ -137,6 +137,7 @@ export function FiltersSection({ onRemoveFilter(idx)} disabled={!value && !key && filters.length === 1} @@ -166,6 +167,7 @@ function AddFilterButton({ }) { return ( - + {i18n.translate('xpack.apm.settings.customLink.flyout.close', { defaultMessage: 'Close', })} @@ -44,6 +49,7 @@ export function FlyoutFooter({ )} setSearchTerm(e.target.value)} placeholder={i18n.translate( diff --git a/x-pack/plugins/apm/public/components/app/settings/custom_link/empty_prompt.tsx b/x-pack/plugins/apm/public/components/app/settings/custom_link/empty_prompt.tsx index fd7a3d258719..791a26dda7bb 100644 --- a/x-pack/plugins/apm/public/components/app/settings/custom_link/empty_prompt.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/custom_link/empty_prompt.tsx @@ -39,6 +39,7 @@ export function EmptyPrompt({ values={{ customLinkDocLinkText: ( diff --git a/x-pack/plugins/apm/public/components/app/settings/general_settings/index.tsx b/x-pack/plugins/apm/public/components/app/settings/general_settings/index.tsx index 670826e43ccc..a578dd585cb0 100644 --- a/x-pack/plugins/apm/public/components/app/settings/general_settings/index.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/general_settings/index.tsx @@ -84,6 +84,7 @@ export function GeneralSettings() { values={{ link: ( - + {i18n.translate( 'xpack.apm.settings.schema.success.viewIntegrationInFleet.buttonText', { defaultMessage: 'View the APM integration in Fleet' } diff --git a/x-pack/plugins/apm/public/components/app/settings/schema/migrated/upgrade_available_card.tsx b/x-pack/plugins/apm/public/components/app/settings/schema/migrated/upgrade_available_card.tsx index 8e7444c1a776..dcdf55508a72 100644 --- a/x-pack/plugins/apm/public/components/app/settings/schema/migrated/upgrade_available_card.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/schema/migrated/upgrade_available_card.tsx @@ -35,7 +35,10 @@ export function UpgradeAvailableCard({ defaultMessage="Even though your APM integration is setup, a new version of the APM integration is available for upgrade with your package policy. {upgradePackagePolicyLink} to get the most out of your setup." values={{ upgradePackagePolicyLink: ( - + {i18n.translate( 'xpack.apm.settings.schema.upgradeAvailable.upgradePackagePolicyLink', { defaultMessage: 'Upgrade your APM integration' } diff --git a/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx b/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx index 1f17b9f63c0a..be52301aaaa4 100644 --- a/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx @@ -162,6 +162,7 @@ export function SchemaOverview({ })} > ), elasticAgentDocLink: ( - + {i18n.translate( 'xpack.apm.settings.schema.descriptionText.elasticAgentDocLinkText', { defaultMessage: 'Elastic Agent' } diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx index 98992e97988f..084f11a85f62 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/index.tsx @@ -123,7 +123,10 @@ export function StorageExplorer() { defaultMessage="Enable progressive loading of data and optimized sorting for services list in {kibanaAdvancedSettingsLink}." values={{ kibanaAdvancedSettingsLink: ( - + {i18n.translate( 'xpack.apm.storageExplorer.longLoadingTimeCalloutLink', { @@ -136,6 +139,7 @@ export function StorageExplorer() { />

setCalloutDismissed({ ...calloutDismissed, @@ -171,6 +175,7 @@ export function StorageExplorer() { )}

setCalloutDismissed({ ...calloutDismissed, diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/resources/tips_and_resources.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/resources/tips_and_resources.tsx index 29553bf1ecca..4c1b283f0562 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/resources/tips_and_resources.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/resources/tips_and_resources.tsx @@ -170,7 +170,11 @@ export function TipsAndResources() { title={title} description={description} footer={ - + {i18n.translate( 'xpack.apm.storageExplorer.resources.learnMoreButton', { diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx index ba8005f12759..0e47d657da30 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx @@ -169,7 +169,10 @@ export function StorageDetailsPerService({
- + {i18n.translate( 'xpack.apm.storageExplorer.serviceDetails.serviceOverviewLink', { diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx index 506b966f0d3c..37b6fb872ed1 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/summary_stats.tsx @@ -186,7 +186,10 @@ export function SummaryStats() { - + {i18n.translate( 'xpack.apm.storageExplorer.summary.serviceInventoryLink', { @@ -196,7 +199,10 @@ export function SummaryStats() { - + {i18n.translate( 'xpack.apm.storageExplorer.summary.indexManagementLink', { diff --git a/x-pack/plugins/apm/public/components/app/trace_explorer/trace_search_box/index.tsx b/x-pack/plugins/apm/public/components/app/trace_explorer/trace_search_box/index.tsx index 782197828b09..84d72000c7b8 100644 --- a/x-pack/plugins/apm/public/components/app/trace_explorer/trace_search_box/index.tsx +++ b/x-pack/plugins/apm/public/components/app/trace_explorer/trace_search_box/index.tsx @@ -162,6 +162,7 @@ export function TraceSearchBox({ { @@ -189,6 +190,7 @@ export function TraceSearchBox({ { onQueryCommit(); diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx index af9365b14be0..72b06bdef41f 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx @@ -25,6 +25,7 @@ function FullTraceButton({ }) { return (
{ diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/truncate_height_section.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/truncate_height_section.tsx index aac15e48d884..a8e483963934 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/truncate_height_section.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/truncate_height_section.tsx @@ -48,6 +48,7 @@ export function TruncateHeightSection({ children, previewHeight }: Props) { {showToggle ? ( { setIsOpen(!isOpen); }} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/transaction_flyout/dropped_spans_warning.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/transaction_flyout/dropped_spans_warning.tsx index 2c6dbe99b606..61bfa995a069 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/transaction_flyout/dropped_spans_warning.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/transaction_flyout/dropped_spans_warning.tsx @@ -33,7 +33,10 @@ export function DroppedSpansWarning({ values: { dropped }, } )}{' '} - + {i18n.translate( 'xpack.apm.transactionDetails.transFlyout.callout.learnMoreAboutDroppedSpansLinkText', { diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/edit_discovery_rule.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/edit_discovery_rule.tsx index 5059bbabfce9..f5a8498b62a0 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/edit_discovery_rule.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/edit_discovery_rule.tsx @@ -64,6 +64,7 @@ export function EditDiscoveryRule({ }} > ({ text: item.operation.label, value: item.operation.value, @@ -145,6 +146,7 @@ export function EditDiscoveryRule({ )} > onChangeProbe(e.target.value)} @@ -156,10 +158,16 @@ export function EditDiscoveryRule({ )} - Cancel + + Cancel + diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/runtime_attachment.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/runtime_attachment.tsx index 8ae285a95268..8c04ce3464d6 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/runtime_attachment.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/runtime_attachment/runtime_attachment.tsx @@ -147,6 +147,7 @@ export function RuntimeAttachment({ - + {i18n.translate( 'xpack.apm.fleetIntegration.enrollmentFlyout.installApmAgentButtonText', { defaultMessage: 'Install APM Agent' } diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tail_sampling_settings.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tail_sampling_settings.tsx index 7af79bb1d6c9..9d98277a63b7 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tail_sampling_settings.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tail_sampling_settings.tsx @@ -77,7 +77,11 @@ export function getTailSamplingSettings(docsLinks?: string): SettingsRow[] { defaultMessage="Learn more about tail sampling policies in our {link}." values={{ link: ( - + {i18n.translate( 'xpack.apm.fleet_integration.settings.tailSamplingDocsHelpTextLink', { diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx index d32ace193342..098dbc988c6a 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx @@ -167,6 +167,7 @@ function AdvancedOptions({ children }: { children: React.ReactNode }) { { setIsOpen((state) => !state); diff --git a/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/labs/index.tsx b/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/labs/index.tsx index c2049f1db623..ac506495ceff 100644 --- a/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/labs/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/labs/index.tsx @@ -27,7 +27,11 @@ export function Labs() { return ( <> - + {i18n.translate('xpack.apm.labs', { defaultMessage: 'Labs' })} {isOpen && } diff --git a/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/labs/labs_flyout.tsx b/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/labs/labs_flyout.tsx index cda57400b599..a7dfc481e70b 100644 --- a/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/labs/labs_flyout.tsx +++ b/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/labs/labs_flyout.tsx @@ -133,14 +133,22 @@ export function LabsFlyout({ onClose }: Props) { - + {i18n.translate('xpack.apm.labs.cancel', { defaultMessage: 'Cancel', })} - + {i18n.translate('xpack.apm.labs.reload', { defaultMessage: 'Reload to apply changes', })} diff --git a/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx b/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx index e971002dfbcd..807c8249985c 100644 --- a/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx +++ b/x-pack/plugins/apm/public/components/routing/home/storage_explorer.tsx @@ -53,7 +53,11 @@ export const storageExplorer = { ), rightSideItems: [ - + {i18n.translate( 'xpack.apm.views.storageExplorer.giveFeedback', { diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx index d1c1cfba9f73..0c1e86aa5fa2 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx @@ -85,7 +85,11 @@ export function AnalyzeDataButton() { 'Explore Data allows you to select and filter result data in any dimension, and look for the cause or impact of performance problems', })} > - + {i18n.translate('xpack.apm.analyzeDataButton.label', { defaultMessage: 'Explore data', })} diff --git a/x-pack/plugins/apm/public/components/shared/charts/latency_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/latency_chart/index.tsx index 65285ea4fbe6..e2c1def6e0df 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/latency_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/latency_chart/index.tsx @@ -102,6 +102,7 @@ export function LatencyChart({ height, kuery }: Props) { + {i18n.translate('xpack.apm.dependenciesTable.serviceMapLinkText', { defaultMessage: 'View service map', })} diff --git a/x-pack/plugins/apm/public/components/shared/license_prompt/index.tsx b/x-pack/plugins/apm/public/components/shared/license_prompt/index.tsx index a76743d6ff4a..38f348dab53d 100644 --- a/x-pack/plugins/apm/public/components/shared/license_prompt/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/license_prompt/index.tsx @@ -49,7 +49,11 @@ export function LicensePrompt({ titleElement="h2" description={{text}} footer={ - + {i18n.translate('xpack.apm.license.button', { defaultMessage: 'Start trial', })} diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/apm_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/apm_link.tsx index a0ba4f907cbc..9cd7e65ab6b8 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/apm_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/apm_link.tsx @@ -100,5 +100,7 @@ export function LegacyAPMLink({ const href = getLegacyApmHref({ basePath, path, search, query: mergedQuery }); - return ; + return ( + + ); } diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/error_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/error_overview_link.tsx index bfa2067e9e7e..a1f9892b79e3 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/error_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/error_overview_link.tsx @@ -27,5 +27,11 @@ export function ErrorOverviewLink({ serviceName, query, ...rest }: Props) { query, }); - return ; + return ( + + ); } diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/service_map_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/service_map_link.tsx index 84eff7eb444b..066dd0c02e6e 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/service_map_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/service_map_link.tsx @@ -22,5 +22,7 @@ interface ServiceMapLinkProps extends APMLinkExtendProps { export function ServiceMapLink({ serviceName, ...rest }: ServiceMapLinkProps) { const href = useServiceMapHref(serviceName); - return ; + return ( + + ); } diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx index 8cb3bce94fc9..3a988750ab57 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/service_node_metric_overview_link.tsx @@ -46,5 +46,11 @@ export function ServiceNodeMetricOverviewLink({ serviceName, serviceNodeName, }); - return ; + return ( + + ); } diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx index b33f22cd9faf..6fb1cc998ab2 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/service_transactions_overview_link.tsx @@ -50,5 +50,11 @@ export function ServiceOrTransactionsOverviewLink({ environment, transactionType, }); - return ; + return ( + + ); } diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx index 517b97ac7797..21b18e673099 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/transaction_detail_link/index.tsx @@ -81,7 +81,13 @@ export function TransactionDetailLink({ return ( } + content={ + + } /> ); } diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx index bd0ac78b855f..87d43236787f 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/transaction_overview_link.tsx @@ -48,5 +48,11 @@ export function TransactionOverviewLink({ latencyAggregationType, transactionType, }); - return ; + return ( + + ); } diff --git a/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_link.tsx b/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_link.tsx index 1455e53730a7..dccce740b62d 100644 --- a/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_link.tsx @@ -69,5 +69,5 @@ export function DiscoverLink({ query = {}, ...rest }: Props) { location, }); - return ; + return ; } diff --git a/x-pack/plugins/apm/public/components/shared/links/elastic_docs_link.tsx b/x-pack/plugins/apm/public/components/shared/links/elastic_docs_link.tsx index 5a7cc4623ea7..4b97e6653ac7 100644 --- a/x-pack/plugins/apm/public/components/shared/links/elastic_docs_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/elastic_docs_link.tsx @@ -32,7 +32,7 @@ export function ElasticDocsLink({ section, path, children, ...rest }: Props) { return typeof children === 'function' ? ( children(href) ) : ( - + {children} ); diff --git a/x-pack/plugins/apm/public/components/shared/links/infra_link.tsx b/x-pack/plugins/apm/public/components/shared/links/infra_link.tsx index 1e5c3a6748a1..fcc3f2ce9d72 100644 --- a/x-pack/plugins/apm/public/components/shared/links/infra_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/infra_link.tsx @@ -48,5 +48,5 @@ export const getInfraHref = ({ export function InfraLink({ app, path, query = {}, ...rest }: Props) { const { core } = useApmPluginContext(); const href = getInfraHref({ app, basePath: core.http.basePath, query, path }); - return ; + return ; } diff --git a/x-pack/plugins/apm/public/components/shared/links/machine_learning_links/mlexplorer_link.tsx b/x-pack/plugins/apm/public/components/shared/links/machine_learning_links/mlexplorer_link.tsx index 48bdfc69150a..af5ec49635db 100644 --- a/x-pack/plugins/apm/public/components/shared/links/machine_learning_links/mlexplorer_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/machine_learning_links/mlexplorer_link.tsx @@ -23,6 +23,7 @@ export function MLExplorerLink({ jobId, external, children }: Props) { return ( + {buttonFill ? ( - + {SETUP_INSTRUCTIONS_LABEL} ) : ( - + {ADD_DATA_LABEL} )} diff --git a/x-pack/plugins/apm/public/components/shared/metadata_table/index.tsx b/x-pack/plugins/apm/public/components/shared/metadata_table/index.tsx index f78bc13ca026..3126a89ed774 100644 --- a/x-pack/plugins/apm/public/components/shared/metadata_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/metadata_table/index.tsx @@ -62,6 +62,7 @@ export function MetadataTable({ sections, isLoading }: Props) { - + {i18n.translate('xpack.apm.metadata.help', { defaultMessage: 'How to add labels and other data', diff --git a/x-pack/plugins/apm/public/components/shared/ml_callout/index.tsx b/x-pack/plugins/apm/public/components/shared/ml_callout/index.tsx index 12b4e021e230..474ef3fc327e 100644 --- a/x-pack/plugins/apm/public/components/shared/ml_callout/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/ml_callout/index.tsx @@ -57,7 +57,7 @@ export function MLCallout({ | undefined; const getLearnMoreLink = (color: 'primary' | 'success') => ( - + { onCreateJobClick?.(); @@ -113,6 +114,7 @@ export function MLCallout({ icon: 'wrench', primaryAction: isOnSettingsPage ? ( { @@ -147,7 +149,10 @@ export function MLCallout({ icon: 'iInCircle', color: 'primary', primaryAction: ( - + {i18n.translate( 'xpack.apm.settings.anomaly_detection.legacy_jobs.button', { defaultMessage: 'Review jobs' } @@ -173,7 +178,11 @@ export function MLCallout({ )} {dismissable && ( - + {i18n.translate('xpack.apm.mlCallout.dismissButton', { defaultMessage: `Dismiss`, })} diff --git a/x-pack/plugins/apm/public/components/shared/select_with_placeholder/index.tsx b/x-pack/plugins/apm/public/components/shared/select_with_placeholder/index.tsx index 4255ca98d4ea..18f59ac4b731 100644 --- a/x-pack/plugins/apm/public/components/shared/select_with_placeholder/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/select_with_placeholder/index.tsx @@ -25,6 +25,7 @@ export const SelectWithPlaceholder: typeof EuiSelect = (props) => { const placeholder = props.placeholder || DEFAULT_PLACEHOLDER; return (

{ dismissCallout(); }} diff --git a/x-pack/plugins/apm/public/components/shared/span_links/span_links_table.tsx b/x-pack/plugins/apm/public/components/shared/span_links/span_links_table.tsx index 6753c0fd9f2f..f223cf003611 100644 --- a/x-pack/plugins/apm/public/components/shared/span_links/span_links_table.tsx +++ b/x-pack/plugins/apm/public/components/shared/span_links/span_links_table.tsx @@ -102,6 +102,7 @@ export function SpanLinksTable({ items }: Props) {
{(copy) => ( { copy(); setIdActionMenuOpen(undefined); @@ -189,6 +192,7 @@ export function SpanLinksTable({ items }: Props) { {details?.transactionId && ( {(copy) => ( { copy(); setIdActionMenuOpen(undefined); diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/__snapshots__/transaction_action_menu.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/__snapshots__/transaction_action_menu.test.tsx.snap index 2b5427b321ce..05ac11ba496e 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/__snapshots__/transaction_action_menu.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/__snapshots__/transaction_action_menu.test.tsx.snap @@ -11,6 +11,7 @@ exports[`TransactionActionMenu component matches the snapshot 1`] = ` >
- + setDurationPopoverOpenIndex(step.synthetics.step?.index ?? null)} iconType={compactView ? undefined : 'visArea'} > diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx index e03656ed562d..f337a9ba0bed 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx @@ -73,7 +73,13 @@ export function StepFieldTrend({ + {EXPLORE_LABEL} } diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/alert_messages.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/alert_messages.tsx index c5e3f301ed21..847de4412a10 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/alert_messages.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/alert_messages.tsx @@ -42,7 +42,7 @@ export const simpleAlertEnabled = ( /> - + {i18n.translate('xpack.synthetics.enableAlert.editAlert', { defaultMessage: 'Edit alert', })} diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/pages/mapping_error.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/mapping_error.tsx index 9923e102d530..db403eb0dcd9 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/pages/mapping_error.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/mapping_error.tsx @@ -61,6 +61,7 @@ export const MappingErrorPage = () => { values={{ docsLink: ( diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/disabled_callout.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/disabled_callout.tsx index 48e5564621a1..fd0a0feba5a1 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/disabled_callout.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/disabled_callout.tsx @@ -28,6 +28,7 @@ export const DisabledCallout = () => {

{CALLOUT_MANAGEMENT_DESCRIPTION}

{enablement.canEnable ? ( { @@ -39,7 +40,7 @@ export const DisabledCallout = () => { ) : (

{CALLOUT_MANAGEMENT_CONTACT_ADMIN}{' '} - + {LEARN_MORE_LABEL}

diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/invalid_api_key_callout.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/invalid_api_key_callout.tsx index aa8b3112955c..f19c2e2af70a 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/invalid_api_key_callout.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/invalid_api_key_callout.tsx @@ -23,6 +23,7 @@ export const InvalidApiKeyCalloutCallout = () => {

{CALLOUT_MANAGEMENT_DESCRIPTION}

{enablement.canEnable ? ( { @@ -34,7 +35,11 @@ export const InvalidApiKeyCalloutCallout = () => { ) : (

{CALLOUT_MANAGEMENT_CONTACT_ADMIN}{' '} - + {LEARN_MORE_LABEL}

diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/service_allowed_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/service_allowed_wrapper.tsx index ff74fd97f0b3..1418380bfd6a 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/service_allowed_wrapper.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/service_allowed_wrapper.tsx @@ -20,7 +20,13 @@ export const ServiceAllowedWrapper: React.FC = ({ children }) => { title={

{MONITOR_MANAGEMENT_LABEL}

} body={

{PUBLIC_BETA_DESCRIPTION}

} actions={[ - + {REQUEST_ACCESS_LABEL} , ]} diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/pages/not_found.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/not_found.tsx index c94a7a7a06b6..1d6738210379 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/pages/not_found.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/not_found.tsx @@ -37,7 +37,10 @@ export const NotFoundPage = () => { } body={ - + { { ( {errorMessage} diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx b/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx index 776830fd10a7..c1c5503eb1dc 100644 --- a/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx +++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx @@ -79,6 +79,7 @@ export function SelectedFilters({ - + {I18LABELS.resetZoom} diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/url_filter/service_name_filter/index.tsx b/x-pack/plugins/ux/public/components/app/rum_dashboard/url_filter/service_name_filter/index.tsx index 674b9593610a..57bd4a51729e 100644 --- a/x-pack/plugins/ux/public/components/app/rum_dashboard/url_filter/service_name_filter/index.tsx +++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/url_filter/service_name_filter/index.tsx @@ -66,6 +66,7 @@ function ServiceNameFilter({ loading, serviceNames }: Props) { return (