diff --git a/x-pack/plugins/apm/jsconfig.json b/x-pack/plugins/apm/jsconfig.json index 89cd3675a9bd83..bdf4e8b91f067f 100644 --- a/x-pack/plugins/apm/jsconfig.json +++ b/x-pack/plugins/apm/jsconfig.json @@ -1,11 +1,4 @@ { - "compilerOptions": { - "target": "es6", - "module": "commonjs", - "baseUrl": "../../../.", - "paths": { - "ui/*": ["src/ui/public/*"] - } - }, + "extends": "../../tsconfig.json", "exclude": ["node_modules", "**/node_modules/*", "build"] } diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.js b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.js index 6f6e2c60aa6710..1bc7b30e7b521c 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.js +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.js @@ -22,7 +22,7 @@ import { Tab, HeaderMedium } from '../../../shared/UIComponents'; import DiscoverButton from '../../../shared/DiscoverButton'; import { PropertiesTable, - getLevelOneProps + getPropertyTabNames } from '../../../shared/PropertiesTable'; import Stacktrace from '../../../shared/Stacktrace'; import { @@ -72,7 +72,7 @@ function getTabs(context, logStackframes) { return [ ...(logStackframes ? [LOG_STACKTRACE_TAB] : []), EXC_STACKTRACE_TAB, - ...getLevelOneProps(dynamicProps) + ...getPropertyTabNames(dynamicProps) ]; } diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/Transaction/view.js b/x-pack/plugins/apm/public/components/app/TransactionDetails/Transaction/view.js index 19e1f42d16024c..0844cc054cad81 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/Transaction/view.js +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/Transaction/view.js @@ -20,7 +20,7 @@ import { isEmpty, capitalize, get, sortBy, last } from 'lodash'; import StickyTransactionProperties from './StickyTransactionProperties'; import { PropertiesTable, - getLevelOneProps + getPropertyTabNames } from '../../../shared/PropertiesTable'; import Spans from './Spans'; import DiscoverButton from '../../../shared/DiscoverButton'; @@ -111,7 +111,7 @@ function getCurrentTab(tabs = [], detailTab) { function getTabs(transactionData) { const dynamicProps = Object.keys(transactionData.context || {}); - return getLevelOneProps(dynamicProps); + return getPropertyTabNames(dynamicProps); } function Transaction({ transaction, location, urlParams }) { diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/NestedKeyValueTable.tsx b/x-pack/plugins/apm/public/components/shared/PropertiesTable/NestedKeyValueTable.tsx new file mode 100644 index 00000000000000..37671e7975f915 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/PropertiesTable/NestedKeyValueTable.tsx @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import React from 'react'; +import styled from 'styled-components'; +import { + colors, + fontFamilyCode, + fontSizes, + px, + units +} from '../../../style/variables'; + +const Table = styled.table` + font-family: ${fontFamilyCode}; + font-size: ${fontSizes.small}; + width: 100%; +`; + +const Row = styled.tr` + border-bottom: ${px(1)} solid ${colors.gray4}; + &:last-child { + border: 0; + } +`; + +const Cell = styled.td` + vertical-align: top; + padding: ${px(units.half)} 0; + + ${Row}:first-child> & { + padding-top: 0; + } + + ${Row}:last-child> & { + padding-bottom: 0; + } + + &:first-child { + width: ${px(units.unit * 20)}; + font-weight: bold; + } +`; + +const EmptyValue = styled.span` + color: ${colors.gray3}; +`; + +export function FormattedKey({ + k, + value +}: { + k: string; + value: any; +}): JSX.Element { + if (value == null) { + return {k}; + } + + return {k}; +} + +export function FormattedValue({ value }: { value: any }): JSX.Element { + if (_.isObject(value)) { + return
{JSON.stringify(value, null, 4)}
; + } else if (_.isBoolean(value) || _.isNumber(value)) { + return {String(value)}; + } else if (!value) { + return N/A; + } + + return {value}; +} + +export function NestedValue({ + parentKey, + value, + depth, + keySorter +}: { + value: any; + depth: number; + parentKey?: string; + keySorter?: KeySorter; +}): JSX.Element { + if (depth > 0 && _.isObject(value)) { + return ( + + ); + } + + return ; +} + +export function NestedKeyValueTable({ + data, + parentKey, + keySorter = Object.keys, + depth = 0 +}: { + data: StringMap; + parentKey?: string; + keySorter?: KeySorter; + depth?: number; +}): JSX.Element { + return ( + + + {keySorter(data, parentKey).map(key => ( + + + + + + + + + ))} + +
+ ); +} diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/NestedKeyValueTable.test.js b/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/NestedKeyValueTable.test.js new file mode 100644 index 00000000000000..7eaec721adc0e9 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/NestedKeyValueTable.test.js @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow, mount } from 'enzyme'; +import 'jest-styled-components'; +import { + NestedKeyValueTable, + NestedValue, + FormattedValue, + FormattedKey +} from '../NestedKeyValueTable'; + +describe('NestedKeyValueTable component', () => { + it('should render with data', () => { + const testData = { + a: 1, + b: 2, + c: [3, 4, 5], + d: { aa: 1, bb: 2 } + }; + expect(shallow()).toMatchSnapshot(); + }); + it('should render an empty table if there is no data', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); + +describe('NestedValue component', () => { + let props; + + beforeEach(() => { + props = { + value: { a: 'hello' }, + depth: 0, + keySorter: jest.fn(), + parentKey: 'who_cares' + }; + }); + + it('should render a formatted value when depth is 0', () => { + expect(shallow()).toMatchSnapshot(); + }); + + it('should render a formatted value when depth > 0 but value is not an object', () => { + props.value = 2; + props.depth = 3; + expect(shallow()).toMatchSnapshot(); + }); + + it('should render a nested KV Table when depth > 0 and value is an object', () => { + props.depth = 1; + expect(shallow()).toMatchSnapshot(); + }); +}); + +describe('FormattedValue component', () => { + it('should render an object', () => { + expect(mount()).toMatchSnapshot(); + }); + + it('should render an array', () => { + expect(mount()).toMatchSnapshot(); + }); + + it('should render a boolean', () => { + expect(mount()).toMatchSnapshot(); + expect(mount()).toMatchSnapshot(); + }); + + it('should render a number', () => { + expect(mount()).toMatchSnapshot(); + }); + + it('should render a string', () => { + expect(mount()).toMatchSnapshot(); + }); + + it('should render null', () => { + expect(mount()).toMatchSnapshot(); + }); + + it('should render undefined', () => { + let b; + expect(mount()).toMatchSnapshot(); + expect(mount()).toMatchSnapshot(); + }); +}); + +describe('FormattedKey component', () => { + it('should render when the value is null or undefined', () => { + let nope; + expect(mount()).toMatchSnapshot(); + expect(mount()).toMatchSnapshot(); + expect(mount()).toMatchSnapshot(); + }); + + it('should render when the value is defined', () => { + expect(mount()).toMatchSnapshot(); + expect(mount()).toMatchSnapshot(); + expect(mount()).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/PropertiesTable.test.js b/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/PropertiesTable.test.js new file mode 100644 index 00000000000000..7cee0b14c57af9 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/PropertiesTable.test.js @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { + PropertiesTable, + AgentFeatureTipMessage, + sortKeysByConfig, + getPropertyTabNames +} from '..'; +import { getFeatureDocs } from '../../../../utils/documentation'; + +jest.mock('../../../../utils/documentation'); +jest.mock('../propertyConfig.json', () => [ + { + key: 'testProperty', + required: false, + presortedKeys: ['name', 'age'] + }, + { + key: 'optionalProperty', + required: false + }, + { + key: 'requiredProperty', + required: true + } +]); + +describe('PropertiesTable component', () => { + it('should render with data', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); + }); + + it("should render empty when data isn't present", () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); + }); + + it('should render empty when data has no keys', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); + }); +}); + +describe('sortKeysByConfig', () => { + const testData = { + color: 'blue', + name: 'Jess', + age: '39', + numbers: [1, 2, 3], + _id: '44x099z' + }; + + it('should sort with presorted keys first', () => { + expect(sortKeysByConfig(testData, 'testProperty')).toEqual([ + 'name', + 'age', + '_id', + 'color', + 'numbers' + ]); + }); + + it('should alpha-sort keys when there is no config value found', () => { + expect(sortKeysByConfig(testData, 'nonExistentKey')).toEqual([ + '_id', + 'age', + 'color', + 'name', + 'numbers' + ]); + }); +}); + +describe('getPropertyTabNames', () => { + it('should return selected and required keys only', () => { + expect(getPropertyTabNames(['testProperty'])).toEqual([ + 'testProperty', + 'requiredProperty' + ]); + }); +}); + +describe('AgentFeatureTipMessage component', () => { + let mockDocs; + const featureName = ''; + const agentName = ''; + + beforeEach(() => { + mockDocs = { + text: 'Mock Docs Text', + url: 'mock-url' + }; + getFeatureDocs.mockImplementation(() => mockDocs); + }); + + it('should render when docs are returned', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); + expect(getFeatureDocs).toHaveBeenCalledWith(featureName, agentName); + }); + + it('should render when docs are returned, but missing a url', () => { + delete mockDocs.url; + expect( + shallow( + + ) + ).toMatchSnapshot(); + }); + + it('should render null empty string when no docs are returned', () => { + mockDocs = null; + expect( + shallow( + + ) + ).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/__snapshots__/NestedKeyValueTable.test.js.snap b/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/__snapshots__/NestedKeyValueTable.test.js.snap new file mode 100644 index 00000000000000..513b985da9da91 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/__snapshots__/NestedKeyValueTable.test.js.snap @@ -0,0 +1,337 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FormattedKey component should render when the value is defined 1`] = ` + + testKey + +`; + +exports[`FormattedKey component should render when the value is defined 2`] = ` + + testKey + +`; + +exports[`FormattedKey component should render when the value is defined 3`] = ` + + testKey + +`; + +exports[`FormattedKey component should render when the value is null or undefined 1`] = ` +.c0 { + color: #999999; +} + + + + + testKey + + + +`; + +exports[`FormattedKey component should render when the value is null or undefined 2`] = ` +.c0 { + color: #999999; +} + + + + + testKey + + + +`; + +exports[`FormattedKey component should render when the value is null or undefined 3`] = ` +.c0 { + color: #999999; +} + + + + + testKey + + + +`; + +exports[`FormattedValue component should render a boolean 1`] = ` + + true + +`; + +exports[`FormattedValue component should render a boolean 2`] = ` + + false + +`; + +exports[`FormattedValue component should render a number 1`] = ` + + 243 + +`; + +exports[`FormattedValue component should render a string 1`] = ` + + hey ok cool + +`; + +exports[`FormattedValue component should render an array 1`] = ` + +
+    [
+    1,
+    2,
+    3
+]
+  
+
+`; + +exports[`FormattedValue component should render an object 1`] = ` + +
+    {
+    "a": "ok"
+}
+  
+
+`; + +exports[`FormattedValue component should render null 1`] = ` +.c0 { + color: #999999; +} + + + + + N/A + + + +`; + +exports[`FormattedValue component should render undefined 1`] = ` +.c0 { + color: #999999; +} + + + + + N/A + + + +`; + +exports[`FormattedValue component should render undefined 2`] = ` +.c0 { + color: #999999; +} + + + + + N/A + + + +`; + +exports[`NestedKeyValueTable component should render an empty table if there is no data 1`] = ` + + + +`; + +exports[`NestedKeyValueTable component should render with data 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`NestedValue component should render a formatted value when depth > 0 but value is not an object 1`] = ` + +`; + +exports[`NestedValue component should render a formatted value when depth is 0 1`] = ` + +`; + +exports[`NestedValue component should render a nested KV Table when depth > 0 and value is an object 1`] = ` + +`; diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/__snapshots__/PropertiesTable.test.js.snap b/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/__snapshots__/PropertiesTable.test.js.snap new file mode 100644 index 00000000000000..c31169eeada0db --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/PropertiesTable/__test__/__snapshots__/PropertiesTable.test.js.snap @@ -0,0 +1,74 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AgentFeatureTipMessage component should render null empty string when no docs are returned 1`] = `""`; + +exports[`AgentFeatureTipMessage component should render when docs are returned 1`] = ` + + + Mock Docs Text + + + Learn more in the documentation. + + +`; + +exports[`AgentFeatureTipMessage component should render when docs are returned, but missing a url 1`] = ` + + + Mock Docs Text + + +`; + +exports[`PropertiesTable component should render empty when data has no keys 1`] = ` + + + + No data available + + +`; + +exports[`PropertiesTable component should render empty when data isn't present 1`] = ` + + + + No data available + + +`; + +exports[`PropertiesTable component should render with data 1`] = ` + + + + +`; diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/index.js b/x-pack/plugins/apm/public/components/shared/PropertiesTable/index.js deleted file mode 100644 index 329b9e4941f734..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/PropertiesTable/index.js +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import styled from 'styled-components'; -import _ from 'lodash'; -import STATIC_PROPS from './staticProperties.json'; -import { - units, - colors, - px, - fontFamilyCode, - fontSizes, - unit, - fontSize -} from '../../../style/variables'; -import { EuiIcon } from '@elastic/eui'; - -import { getFeatureDocs } from '../../../utils/documentation'; -import { ExternalLink } from '../../../utils/url'; - -const TableContainer = styled.div` - padding-bottom: ${px(units.double)}; -`; - -const Table = styled.table` - font-family: ${fontFamilyCode}; - font-size: ${fontSizes.small}; - width: 100%; -`; - -const Row = styled.tr` - border-bottom: 1px solid ${colors.gray4}; - &:last-child { - border: 0; - } -`; - -const Cell = styled.td` - vertical-align: top; - padding: ${units.half}px 0; - - ${Row}:first-child> & { - padding-top: 0; - } - - ${Row}:last-child> & { - padding-bottom: 0; - } - - &:first-child { - width: 300px; - font-weight: bold; - } -`; - -const TableInfo = styled.div` - padding: ${px(unit)} 0 0; - text-align: center; - font-size: ${fontSize}; - color: ${colors.gray2}; - line-height: 1.5; -`; - -const EmptyValue = styled.span` - color: ${colors.gray3}; -`; - -function getSortedProps(propData, levelTwoKey, level) { - if (level === 2) { - return getLevelTwoProps(propData, levelTwoKey); - } - - return _.sortBy(_.map(propData, (value, key) => ({ value, key })), 'key'); -} - -function formatValue(value) { - if (_.isObject(value)) { - return
{JSON.stringify(value, null, 4)}
; - } else if (_.isBoolean(value) || _.isNumber(value)) { - return String(value); - } else if (!value) { - return N/A; - } - - return value; -} - -function formatKey(key, value) { - if (value == null) { - return {key}; - } - - return key; -} - -export function getLevelOneProps(dynamicProps) { - return STATIC_PROPS.filter( - ({ key, required }) => required || dynamicProps.includes(key) - ).map(({ key }) => key); -} - -function getLevelTwoProps(dynamicProps, currentKey) { - const staticProps = _.get( - _.find(STATIC_PROPS, { key: currentKey }), - 'children' - ); - const dynamicPropsSorted = Object.keys(dynamicProps).sort(); - return _.uniq([...staticProps, ...dynamicPropsSorted]).map(key => ({ - key, - value: dynamicProps[key] - })); -} - -function recursiveSort(propData, levelTwoKey, level) { - return ( - - - {getSortedProps(propData, levelTwoKey, level).map(({ key, value }) => { - return ( - - {formatKey(key, value)} - - {level < 3 && _.isObject(value) - ? recursiveSort(value, levelTwoKey, level + 1) - : formatValue(value)} - - - ); - })} - -
- ); -} - -function AgentFeatureTipMessage({ featureName, agentName }) { - const docs = getFeatureDocs(featureName, agentName); - - if (!docs) { - return null; - } - - return ( - - - {docs.text}{' '} - {docs.url && ( - - Learn more in the documentation. - - )} - - ); -} - -export function PropertiesTable({ propData = {}, propKey, agentName }) { - if (!propData) { - return ( - - - No data available - - - ); - } - - return ( - - {recursiveSort(propData, propKey, 2, agentName)} - - - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/index.tsx b/x-pack/plugins/apm/public/components/shared/PropertiesTable/index.tsx new file mode 100644 index 00000000000000..979a3cab39097e --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/PropertiesTable/index.tsx @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiIcon } from '@elastic/eui'; +import _ from 'lodash'; +import React from 'react'; +import styled from 'styled-components'; +import { colors, fontSize, px, unit, units } from '../../../style/variables'; +import { getFeatureDocs } from '../../../utils/documentation'; +// @ts-ignore +import { ExternalLink } from '../../../utils/url'; +import { NestedKeyValueTable } from './NestedKeyValueTable'; +import PROPERTY_CONFIG from './propertyConfig.json'; + +const indexedPropertyConfig = _.indexBy(PROPERTY_CONFIG, 'key'); + +const TableContainer = styled.div` + padding-bottom: ${px(units.double)}; +`; + +const TableInfo = styled.div` + padding: ${px(unit)} 0 0; + text-align: center; + font-size: ${fontSize}; + color: ${colors.gray2}; + line-height: 1.5; +`; + +export function getPropertyTabNames(selected: string[]): string[] { + return PROPERTY_CONFIG.filter( + ({ key, required }: { key: string; required: boolean }) => + required || selected.includes(key) + ).map(({ key }: { key: string }) => key); +} + +export function AgentFeatureTipMessage({ + featureName, + agentName +}: { + featureName: string; + agentName: string; +}): JSX.Element | null { + const docs = getFeatureDocs(featureName, agentName); + + if (!docs) { + return null; + } + + return ( + + + {docs.text}{' '} + {docs.url && ( + + Learn more in the documentation. + + )} + + ); +} + +export const sortKeysByConfig: KeySorter = (object, currentKey) => { + const presorted = _.get( + indexedPropertyConfig, + `${currentKey}.presortedKeys`, + [] + ); + return _.uniq([...presorted, ...Object.keys(object).sort()]); +}; + +export function PropertiesTable({ + propData, + propKey, + agentName +}: { + propData: StringMap; + propKey: string; + agentName: string; +}) { + if (_.isEmpty(propData)) { + return ( + + + No data available + + + ); + } + + return ( + + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/propertyConfig.json b/x-pack/plugins/apm/public/components/shared/PropertiesTable/propertyConfig.json new file mode 100644 index 00000000000000..0f1d44e09d5700 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/PropertiesTable/propertyConfig.json @@ -0,0 +1,49 @@ +[ + { + "key": "request", + "required": false, + "presortedKeys": [ + "http_version", + "method", + "url", + "socket", + "headers", + "body" + ] + }, + { + "key": "response", + "required": false, + "presortedKeys": ["status_code", "headers", "headers_sent", "finished"] + }, + { + "key": "system", + "required": false, + "presortedKeys": ["hostname", "architecture", "platform"] + }, + { + "key": "service", + "required": false, + "presortedKeys": ["runtime", "framework", "agent", "version"] + }, + { + "key": "process", + "required": false, + "presortedKeys": ["pid", "title", "argv"] + }, + { + "key": "user", + "required": true, + "presortedKeys": ["id", "username", "email"] + }, + { + "key": "tags", + "required": true, + "presortedKeys": [] + }, + { + "key": "custom", + "required": true, + "presortedKeys": [] + } +] diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/staticProperties.json b/x-pack/plugins/apm/public/components/shared/PropertiesTable/staticProperties.json deleted file mode 100644 index f7403cd0a7a441..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/PropertiesTable/staticProperties.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "key": "request", - "required": false, - "children": ["http_version", "method", "url", "socket", "headers", "body"] - }, - { - "key": "response", - "required": false, - "children": ["status_code", "headers", "headers_sent", "finished"] - }, - { - "key": "system", - "required": false, - "children": ["hostname", "architecture", "platform"] - }, - { - "key": "service", - "required": false, - "children": ["runtime", "framework", "agent", "version"] - }, - { - "key": "process", - "required": false, - "children": ["pid", "title", "argv"] - }, - { "key": "user", "required": true, "children": ["id", "username", "email"] }, - { "key": "tags", "required": true, "children": [] }, - { "key": "custom", "required": true, "children": [] } -] diff --git a/x-pack/plugins/apm/public/components/shared/PropertiesTable/types.d.ts b/x-pack/plugins/apm/public/components/shared/PropertiesTable/types.d.ts new file mode 100644 index 00000000000000..057011fc8ab98a --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/PropertiesTable/types.d.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +type KeySorter = (data: StringMap, parentKey?: string) => string[]; diff --git a/x-pack/plugins/apm/public/style/variables.js b/x-pack/plugins/apm/public/style/variables.ts similarity index 93% rename from x-pack/plugins/apm/public/style/variables.js rename to x-pack/plugins/apm/public/style/variables.ts index e4fc7d765ab3b3..7757e04426503d 100644 --- a/x-pack/plugins/apm/public/style/variables.js +++ b/x-pack/plugins/apm/public/style/variables.ts @@ -19,11 +19,11 @@ export const units = { quadruple: unit * 4 }; -export function px(value) { +export function px(value: number): string { return `${value}px`; } -export function pct(value) { +export function pct(value: number): string { return `${value}%`; } @@ -82,7 +82,7 @@ export const fontSizes = { xxlarge: '30px' }; -export function truncate(width) { +export function truncate(width: string) { return ` max-width: ${width}; white-space: nowrap; diff --git a/x-pack/plugins/apm/public/utils/documentation.js b/x-pack/plugins/apm/public/utils/documentation.ts similarity index 96% rename from x-pack/plugins/apm/public/utils/documentation.js rename to x-pack/plugins/apm/public/utils/documentation.ts index cf285d064e9cf6..0ad3ce722d8195 100644 --- a/x-pack/plugins/apm/public/utils/documentation.js +++ b/x-pack/plugins/apm/public/utils/documentation.ts @@ -5,6 +5,7 @@ */ import { get } from 'lodash'; +// @ts-ignore import { metadata } from 'ui/metadata'; const STACK_VERSION = metadata.branch; @@ -157,7 +158,7 @@ export const ELASTIC_DOCS = { // // Helper methods // -function translateAgentName(agentName) { +function translateAgentName(agentName: string): string { switch (agentName) { case 'js-react': case 'js-base': @@ -168,7 +169,13 @@ function translateAgentName(agentName) { } } -export function getFeatureDocs(featureName, agentName) { +export function getFeatureDocs( + featureName: string, + agentName: string +): { + url: string; + text?: string; +} { const translatedAgentName = translateAgentName(agentName); return get(APM_AGENT_DOCS, `${featureName}.${translatedAgentName}`); } diff --git a/x-pack/plugins/apm/types.d.ts b/x-pack/plugins/apm/types.d.ts new file mode 100644 index 00000000000000..e538124ba47eb0 --- /dev/null +++ b/x-pack/plugins/apm/types.d.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; + * you may not use this file except in compliance with the Elastic License. + */ + +// allow JSON files to be imported directly without TSLint errors +// see: https://github.com/palantir/tslint/issues/1264#issuecomment-228433367 +// and: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#arbitrary-expressions-are-forbidden-in-export-assignments-in-ambient-contexts +declare module '*.json' { + const json: any; + export default json; +} + +interface StringMap { + [key: string]: T; +}