From 631bc22f74076c28150d30abbd3ff1eb35b441b1 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Tue, 24 Jul 2018 13:42:53 -0400 Subject: [PATCH 01/18] Add BeatsTable and control bar components. --- x-pack/package.json | 9 +- .../beats_management/common/domain_types.ts | 7 + .../components/table/beats_table.test.tsx | 39 ++ .../public/components/table/beats_table.tsx | 186 ++++++ .../public/components/table/controls.tsx | 136 +++++ .../public/components/table/index.ts | 7 + .../lib/adapters/beats/adapter_types.ts | 7 +- x-pack/plugins/beats_management/wallaby.js | 3 +- x-pack/yarn.lock | 531 ++++++++++++++++-- 9 files changed, 871 insertions(+), 54 deletions(-) create mode 100644 x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx create mode 100644 x-pack/plugins/beats_management/public/components/table/beats_table.tsx create mode 100644 x-pack/plugins/beats_management/public/components/table/controls.tsx create mode 100644 x-pack/plugins/beats_management/public/components/table/index.ts diff --git a/x-pack/package.json b/x-pack/package.json index 440056842f8a5..b27012e3b55bd 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -29,6 +29,7 @@ "@kbn/test": "link:../packages/kbn-test", "@types/boom": "^4.3.8", "@types/chance": "^1.0.1", + "@types/enzyme": "^3.1.12", "@types/history": "^4.6.2", "@types/jest": "^22.2.3", "@types/joi": "^10.4.0", @@ -41,7 +42,7 @@ "aws-sdk": "2.2.33", "axios": "^0.18.0", "babel-jest": "^22.4.3", - "chalk": "^2.3.2", + "chalk": "^2.4.1", "chance": "1.0.10", "checksum": "0.1.1", "commander": "2.12.2", @@ -77,14 +78,14 @@ "supertest-as-promised": "4.0.2", "tmp": "0.0.31", "tree-kill": "^1.1.0", - "typescript": "^2.8.3", + "typescript": "^2.9.2", "vinyl-fs": "^3.0.2", "xml-crypto": "^0.10.1", "xml2js": "^0.4.19", "yargs": "4.7.1" }, "dependencies": { - "@elastic/eui": "1.1.0", + "@elastic/eui": "3.0.0", "@elastic/node-crypto": "0.1.2", "@elastic/node-phantom-simple": "2.2.4", "@elastic/numeral": "2.3.2", @@ -113,7 +114,7 @@ "d3-scale": "1.0.6", "dedent": "^0.7.0", "dragselect": "1.7.17", - "elasticsearch": "13.0.1", + "elasticsearch": "^14.1.0", "extract-zip": "1.5.0", "font-awesome": "4.4.0", "get-port": "2.1.0", diff --git a/x-pack/plugins/beats_management/common/domain_types.ts b/x-pack/plugins/beats_management/common/domain_types.ts index 9411aca413840..a553f987a233e 100644 --- a/x-pack/plugins/beats_management/common/domain_types.ts +++ b/x-pack/plugins/beats_management/common/domain_types.ts @@ -19,13 +19,20 @@ export interface CMBeat { host_ip: string; host_name: string; ephemeral_id?: string; + last_updated?: string; + event_rate?: string; local_configuration_yml?: string; tags?: string[]; central_configuration_yml?: string; metadata?: {}; } +export interface CMPopulatedBeat extends CMBeat { + full_tags: BeatTag[]; +} + export interface BeatTag { id: string; configuration_blocks: ConfigurationBlock[]; + color?: string; } diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx new file mode 100644 index 0000000000000..b241e04da1915 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx @@ -0,0 +1,39 @@ +/* + * 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 { shallow } from 'enzyme'; +import React from 'react'; +import { CMPopulatedBeat } from '../../../common/domain_types'; +import { BeatsTable } from './beats_table'; + +describe('BeatsTable component', () => { + let items: CMPopulatedBeat[]; + let beat; + let onBulkAction: any; + + beforeEach(() => { + beat = { + id: 'beatid', + access_token: 'access', + type: 'type', + host_ip: 'ip', + host_name: 'name', + full_tags: [ + { + id: 'Production', + configuration_blocks: [], + }, + ], + }; + items = [beat]; + onBulkAction = jest.fn(); + }); + + it('matches snapshot', () => { + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx new file mode 100644 index 0000000000000..4543462efa719 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx @@ -0,0 +1,186 @@ +/* + * 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 { + // @ts-ignore + EuiBadge, + EuiFlexGroup, + // @ts-ignore + EuiInMemoryTable, + EuiLink, +} from '@elastic/eui'; +import { flatten, uniq } from 'lodash'; +import React from 'react'; +import styled from 'styled-components'; +import { CMPopulatedBeat } from '../../../common/domain_types'; +import { BulkActionControlBar } from './controls'; + +const columns = [ + { + field: 'id', + name: 'Beat name', + render: (id: string) => {id}, + sortable: true, + }, + { + field: 'type', + name: 'Type', + sortable: true, + }, + { + field: 'full_tags', + name: 'Tags', + render: (value: string, beat: CMPopulatedBeat) => ( + + {beat.full_tags.map(tag => ( + + {tag.id} + + ))} + + ), + sortable: false, + }, + { + // TODO: update to use actual metadata field + field: 'event_rate', + name: 'Event rate', + sortable: true, + }, + { + // TODO: update to use actual metadata field + field: 'last_updated', + name: 'Last config update', + sortable: true, + }, +]; + +interface BeatsTableProps { + items: CMPopulatedBeat[]; + onBulkAction: any; +} + +interface BeatsTableState { + itemsToRender: CMPopulatedBeat[]; + pageIndex: number; + pageSize: number; + search?: any; + selection: CMPopulatedBeat[]; +} + +const TableContainer = styled.div` + padding: 16px; +`; + +export class BeatsTable extends React.Component { + constructor(props: BeatsTableProps) { + super(props); + + this.state = { + itemsToRender: props.items, + pageIndex: 0, + pageSize: 5, + selection: [], + }; + } + + public render() { + const { onBulkAction } = this.props; + const { itemsToRender, pageIndex, pageSize } = this.state; + + const pagination = { + pageIndex, + pageSize, + totalItemCount: itemsToRender.length, + pageSizeOptions: [3, 5, 8], + }; + + const selectionOptions = { + onSelectionChange: this.setSelection, + selectable: () => true, + selectableMessage: () => null, + }; + + const tagOptions = this.getTagsOptions(); + const typeOptions = this.getTypeOptions(); + + return ( + + { + const { selection } = this.state; + onBulkAction(action, selection); + }} + onSearchQueryChange={this.onQueryChange} + tagOptions={tagOptions} + typeOptions={typeOptions} + /> + + + ); + } + + private getClauseValuesForField = (ast: any, fieldName: string) => { + const clauses = ast.getFieldClauses(fieldName); + return clauses ? clauses.map((clause: any) => clause.value) : []; + }; + + private onQueryChange = (search: any) => { + const { items } = this.props; + let itemsToRender = items; + + if (search && !search.error) { + const { query } = search; + const types = this.getClauseValuesForField(query.ast, 'type'); + const tags = this.getClauseValuesForField(query.ast, 'tag'); + const terms = query.ast.getTermClauses().map((clause: any) => clause.value); + if (types.length) { + itemsToRender = itemsToRender.filter(item => types.includes(item.type)); + } + if (tags.length) { + itemsToRender = itemsToRender.filter(item => + item.full_tags.some(({ id }) => tags.includes(id)) + ); + } + if (terms.length) { + itemsToRender = itemsToRender.filter(item => + terms.some((term: string) => item.id.includes(term)) + ); + } + } + + this.setState({ + itemsToRender, + }); + }; + + private getTagsOptions = () => { + const { items } = this.props; + const ids = flatten(items.map(({ full_tags }) => full_tags.map(({ id }) => id))); + return uniq(ids).map(id => ({ + value: id, + })); + }; + + private getTypeOptions = () => { + const { items } = this.props; + return uniq(items.map(({ type }) => type)).map(type => ({ value: type })); + }; + + private setSelection = (selection: any) => { + this.setState({ + selection, + }); + }; +} diff --git a/x-pack/plugins/beats_management/public/components/table/controls.tsx b/x-pack/plugins/beats_management/public/components/table/controls.tsx new file mode 100644 index 0000000000000..2602da4be6221 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/controls.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 { + EuiButton, + EuiContextMenu, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPopover, + // @ts-ignore + EuiSearchBar, +} from '@elastic/eui'; +import React from 'react'; + +// TODO: move to constants +const BULK_ASSIGN_TAG = 'BULK_ASSIGN_TAG'; +const BULK_DELETE = 'BULK_DELETE'; +const BULK_EDIT = 'BULK_EDIT'; + +interface BulkActionControlBarState { + isPopoverVisible: boolean; +} + +interface FilterOption { + value: string; +} + +interface BulkActionControlBarProps { + onBulkAction: any; + onSearchQueryChange: any; + tagOptions: FilterOption[]; + typeOptions: FilterOption[]; +} + +export class BulkActionControlBar extends React.Component< + BulkActionControlBarProps, + BulkActionControlBarState +> { + constructor(props: BulkActionControlBarProps) { + super(props); + + this.state = { + isPopoverVisible: false, + }; + } + + public render() { + const { isPopoverVisible } = this.state; + + const bulkActionButton = ( + + Bulk Action + + ); + const { onBulkAction, onSearchQueryChange, tagOptions, typeOptions } = this.props; + const panels = [ + { + id: 0, + title: 'Bulk Action', + items: [ + { + name: 'Bulk Edit', + icon: , + onClick: () => onBulkAction(BULK_EDIT), + }, + { + name: 'Bulk Delete', + icon: , + onClick: () => onBulkAction(BULK_DELETE), + }, + { + name: 'Bulk Assign Tags', + icon: , + onClick: () => onBulkAction(BULK_ASSIGN_TAG), + }, + ], + }, + ]; + + return ( + + + + + + + + + + + ); + } + + private showPopover = () => { + this.setState({ + isPopoverVisible: true, + }); + }; + + private hidePopover = () => { + this.setState({ + isPopoverVisible: false, + }); + }; +} diff --git a/x-pack/plugins/beats_management/public/components/table/index.ts b/x-pack/plugins/beats_management/public/components/table/index.ts new file mode 100644 index 0000000000000..73facdbdb2ca0 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/index.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. + */ + +export { BeatsTable } from './beats_table'; diff --git a/x-pack/plugins/beats_management/server/lib/adapters/beats/adapter_types.ts b/x-pack/plugins/beats_management/server/lib/adapters/beats/adapter_types.ts index 8812d9d39bd41..d2c625bf424df 100644 --- a/x-pack/plugins/beats_management/server/lib/adapters/beats/adapter_types.ts +++ b/x-pack/plugins/beats_management/server/lib/adapters/beats/adapter_types.ts @@ -7,13 +7,12 @@ import { CMBeat } from '../../../../common/domain_types'; import { FrameworkUser } from '../framework/adapter_types'; -// FIXME: fix getBeatsWithIds return type export interface CMBeatsAdapter { insert(beat: CMBeat): Promise; update(beat: CMBeat): Promise; - get(id: string): any; - getAll(user: FrameworkUser): any; - getWithIds(user: FrameworkUser, beatIds: string[]): any; + get(id: string): Promise; + getAll(user: FrameworkUser): Promise; + getWithIds(user: FrameworkUser, beatIds: string[]): Promise; removeTagsFromBeats( user: FrameworkUser, removals: BeatsTagAssignment[] diff --git a/x-pack/plugins/beats_management/wallaby.js b/x-pack/plugins/beats_management/wallaby.js index 8c0c4aa355925..e9901ff5bf23e 100644 --- a/x-pack/plugins/beats_management/wallaby.js +++ b/x-pack/plugins/beats_management/wallaby.js @@ -14,10 +14,11 @@ module.exports = function (wallaby) { //'plugins/beats/public/**/*.+(js|jsx|ts|tsx|json|snap|css|less|sass|scss|jpg|jpeg|gif|png|svg)', 'server/**/*.+(js|jsx|ts|tsx|json|snap|css|less|sass|scss|jpg|jpeg|gif|png|svg)', 'common/**/*.+(js|jsx|ts|tsx|json|snap|css|less|sass|scss|jpg|jpeg|gif|png|svg)', + 'public/**/*.+(js|jsx|ts|tsx|json|snap|css|less|sass|scss|jpg|jpeg|gif|png|svg)', '!**/*.test.ts', ], - tests: ['**/*.test.ts'], + tests: ['**/*.test.ts', '**/*.test.tsx'], env: { type: 'node', runner: 'node', diff --git a/x-pack/yarn.lock b/x-pack/yarn.lock index 1f1b195edbdc1..0cbd969eac352 100644 --- a/x-pack/yarn.lock +++ b/x-pack/yarn.lock @@ -10,9 +10,9 @@ esutils "^2.0.2" js-tokens "^3.0.0" -"@elastic/eui@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-1.1.0.tgz#f4aa5702358133dd8c26295ba7c6a243c35616be" +"@elastic/eui@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-3.0.0.tgz#daa51d5ef32e0c00bcf7b53b7acea91df3a3704c" dependencies: classnames "^2.2.5" core-js "^2.5.1" @@ -28,6 +28,8 @@ react-datepicker v1.4.1 react-input-autosize "^2.2.1" react-virtualized "^9.18.5" + react-vis "1.10.2" + serve "^6.3.1" tabbable "^1.1.0" uuid "^3.1.0" @@ -126,6 +128,10 @@ version "1.0.1" resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.0.1.tgz#c10703020369602c40dd9428cc6e1437027116df" +"@types/cheerio@*": + version "0.22.8" + resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.8.tgz#5702f74f78b73e13f1eb1bd435c2c9de61a250d4" + "@types/delay@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/delay/-/delay-2.0.1.tgz#61bcf318a74b61e79d1658fbf054f984c90ef901" @@ -134,6 +140,13 @@ version "5.0.24" resolved "https://registry.yarnpkg.com/@types/elasticsearch/-/elasticsearch-5.0.24.tgz#b09082d2ba3d8ae1627ea771bd2fbd2851e4a035" +"@types/enzyme@^3.1.12": + version "3.1.12" + resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.12.tgz#293bb07c1ef5932d37add3879e72e0f5bc614f3c" + dependencies: + "@types/cheerio" "*" + "@types/react" "*" + "@types/events@*": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" @@ -271,6 +284,13 @@ accept@2.x.x: boom "5.x.x" hoek "4.x.x" +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + acorn-globals@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538" @@ -285,9 +305,9 @@ add-event-listener@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/add-event-listener/-/add-event-listener-0.0.1.tgz#a76229ebc64c8aefae204a16273a2f255abea2d0" -agentkeepalive@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" +address@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" agentkeepalive@^3.4.1: version "3.4.1" @@ -350,6 +370,12 @@ angular-ui-bootstrap@1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/angular-ui-bootstrap/-/angular-ui-bootstrap-1.2.5.tgz#b0c1eff0bf3b7a65668984a1b81820a90dc60995" +ansi-align@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" + dependencies: + string-width "^2.0.0" + ansi-cyan@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" @@ -435,6 +461,10 @@ aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" +arch@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" + archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" @@ -446,6 +476,10 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545" + argparse@^1.0.7: version "1.0.9" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" @@ -459,6 +493,15 @@ argparse@~0.1.15: underscore "~1.7.0" underscore.string "~2.4.0" +args@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/args/-/args-4.0.0.tgz#5ca24cdba43d4b17111c56616f5f2e9d91933954" + dependencies: + camelcase "5.0.0" + chalk "2.3.2" + leven "2.1.0" + mri "1.1.0" + argv-split@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argv-split/-/argv-split-2.0.1.tgz#be264117790dbd5ccd63ec3f449a1804814ac4c5" @@ -1101,6 +1144,12 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +basic-auth@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" + dependencies: + safe-buffer "5.1.1" + bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -1127,7 +1176,7 @@ bluebird@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.1.1.tgz#7e2e4318d62ae72a674f6aea6357bb4def1a6e41" -bluebird@^3.3.1: +bluebird@3.5.1, bluebird@^3.3.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -1165,6 +1214,18 @@ boom@5.x.x: dependencies: hoek "4.x.x" +boxen@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" + dependencies: + ansi-align "^2.0.0" + camelcase "^4.0.0" + chalk "^2.0.1" + cli-boxes "^1.0.0" + string-width "^2.0.0" + term-size "^1.2.0" + widest-line "^2.0.0" + brace-expansion@^1.0.0, brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" @@ -1282,6 +1343,10 @@ builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -1326,6 +1391,10 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" +camelcase@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -1338,7 +1407,7 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" -camelcase@^4.1.0: +camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -1378,6 +1447,30 @@ chai@~1.9.2: assertion-error "1.0.0" deep-eql "0.1.3" +chalk@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.0.tgz#a060a297a6b57e15b61ca63ce84995daa0fe6e52" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@2.4.1, chalk@^2.3.1, chalk@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -1396,22 +1489,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" -chalk@^2.3.1, chalk@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chance@1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/chance/-/chance-1.0.10.tgz#03500b04ad94e778dd2891b09ec73a6ad87b1996" @@ -1462,6 +1539,10 @@ classnames@2.2.5, classnames@^2.1.2, classnames@^2.2.3, classnames@^2.2.4, class version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" +cli-boxes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + cli-cursor@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" @@ -1480,6 +1561,13 @@ clipboard@^1.6.1: select "^1.1.2" tiny-emitter "^2.0.0" +clipboardy@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" + dependencies: + arch "^2.1.0" + execa "^0.8.0" + cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -1607,6 +1695,24 @@ component-emitter@^1.2.0, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" +compressible@~2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.14.tgz#326c5f507fbb055f54116782b969a81b67a29da7" + dependencies: + mime-db ">= 1.34.0 < 2" + +compression@^1.6.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db" + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.14" + debug "2.6.9" + on-headers "~1.0.1" + safe-buffer "5.1.2" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1643,6 +1749,10 @@ content-type-parser@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" +content-type@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + content@3.x.x: version "3.0.6" resolved "https://registry.yarnpkg.com/content/-/content-3.0.6.tgz#9c2e301e9ae515ed65a4b877d78aa5659bb1b809" @@ -1918,6 +2028,10 @@ d3@3.5.6: version "3.5.6" resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.6.tgz#9451c651ca733fb9672c81fb7f2655164a73a42d" +dargs@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829" + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1951,7 +2065,7 @@ debug@2.6.0: dependencies: ms "0.7.2" -debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -1991,6 +2105,10 @@ deep-equal@^1.0.0, deep-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" @@ -2071,10 +2189,22 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" +depd@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + deprecated@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" @@ -2093,6 +2223,13 @@ detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" +detect-port@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.2.3.tgz#15bf49820d02deb84bfee0a74876b32d791bf610" + dependencies: + address "^1.0.1" + debug "^2.6.0" + dfa@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.1.0.tgz#d30218bd10d030fa421df3ebbc82285463a31781" @@ -2209,16 +2346,9 @@ ecdsa-sig-formatter@1.0.10: dependencies: safe-buffer "^5.0.1" -elasticsearch@13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-13.0.1.tgz#fa58204233052c4cd221e8721e48f3906b385b32" - dependencies: - agentkeepalive "^2.2.0" - chalk "^1.0.0" - lodash "2.4.2" - lodash.get "^4.4.2" - lodash.isempty "^4.4.0" - lodash.trimend "^4.5.1" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" elasticsearch@^14.1.0: version "14.2.2" @@ -2231,6 +2361,10 @@ elasticsearch@^14.1.0: lodash.isempty "^4.4.0" lodash.trimend "^4.5.1" +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" @@ -2323,6 +2457,10 @@ es6-promise@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.0.1.tgz#ccc4963e679f0ca9fb187c777b9e583d3c7573c2" +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + escape-string-regexp@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" @@ -2407,6 +2545,10 @@ esutils@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + eventemitter3@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" @@ -2441,6 +2583,18 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -2638,6 +2792,10 @@ fileset@^2.0.2: glob "^7.0.3" minimatch "^3.0.3" +filesize@3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + fill-keys@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" @@ -2813,6 +2971,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + from2@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -2824,6 +2986,14 @@ from@^0.1.3: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" +fs-extra@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-mkdirp-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" @@ -3326,7 +3496,7 @@ gulplog@^1.0.0: dependencies: glogg "^1.0.0" -handlebars@^4.0.3: +handlebars@4.0.11, handlebars@^4.0.3: version "4.0.11" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: @@ -3528,6 +3698,10 @@ hoek@3.x.x: version "3.0.4" resolved "https://registry.yarnpkg.com/hoek/-/hoek-3.0.4.tgz#268adff66bb6695c69b4789a88b1e0847c3f3123" +hoek@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" + hoek@4.x.x: version "4.2.0" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" @@ -3584,6 +3758,15 @@ http-cache-semantics@3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" +http-errors@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" + dependencies: + depd "1.1.1" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + http-errors@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.4.0.tgz#6c0242dea6b3df7afda153c71089b31c6e82aabf" @@ -3591,6 +3774,15 @@ http-errors@~1.4.0: inherits "2.0.1" statuses ">= 1.2.1 < 2" +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -3667,7 +3859,7 @@ inherits@1: version "1.0.2" resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -3719,6 +3911,10 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" +ip@1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + iron@4.x.x: version "4.0.5" resolved "https://registry.yarnpkg.com/iron/-/iron-4.0.5.tgz#4f042cceb8b9738f346b59aa734c83a89bc31428" @@ -3964,7 +4160,7 @@ is-retry-allowed@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" -is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@1.1.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -3998,6 +4194,10 @@ is-windows@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.1.tgz#310db70f742d259a16a369202b51af84233310d9" +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -4520,6 +4720,12 @@ json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -4646,7 +4852,7 @@ left-pad@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.2.0.tgz#d30a73c6b8201d8f7d8e7956ba9616087a68e0ee" -leven@^2.1.0: +leven@2.1.0, leven@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" @@ -5103,6 +5309,22 @@ methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" +micro-compress@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micro-compress/-/micro-compress-1.0.0.tgz#53f5a80b4ad0320ca165a559b6e3df145d4f704f" + dependencies: + compression "^1.6.2" + +micro@9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/micro/-/micro-9.3.1.tgz#0c37eba0171554b1beccda5215ff8ea4e7aa59d6" + dependencies: + arg "2.0.0" + chalk "2.4.0" + content-type "1.0.4" + is-stream "1.1.0" + raw-body "2.3.2" + micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -5143,16 +5365,40 @@ mime-db@1.x.x: version "1.32.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.32.0.tgz#485b3848b01a3cda5f968b4882c0771e58e09414" +"mime-db@>= 1.34.0 < 2", mime-db@~1.35.0: + version "1.35.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47" + mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + dependencies: + mime-db "~1.33.0" + mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" dependencies: mime-db "~1.30.0" +mime-types@~2.1.18: + version "2.1.19" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0" + dependencies: + mime-db "~1.35.0" + +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -5288,6 +5534,10 @@ moment@2.x.x, "moment@>= 2.9.0", moment@^2.13.0, moment@^2.20.1: version "2.20.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd" +mri@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a" + ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -5366,6 +5616,10 @@ nearley@^2.7.10: railroad-diagrams "^1.0.0" randexp "^0.4.2" +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + ngreact@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/ngreact/-/ngreact-0.5.1.tgz#2dcccc1541771796689d13e51bb8d5010af41c57" @@ -5472,6 +5726,10 @@ node-sass@^4.9.0: stdout-stream "^1.4.0" "true-case-path" "^1.0.2" +node-version@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/node-version/-/node-version-1.1.3.tgz#1081c87cce6d2dbbd61d0e51e28c287782678496" + nodemailer@^4.6.4: version "4.6.4" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-4.6.4.tgz#f0d72d0c6a6ec5f4369fa8f4bf5127a31baa2014" @@ -5663,6 +5921,16 @@ object.values@^1.0.4: function-bind "^1.1.0" has "^1.0.1" +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5679,6 +5947,16 @@ onetime@^1.0.0: version "1.1.0" resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" +openssl-self-signed-certificate@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/openssl-self-signed-certificate/-/openssl-self-signed-certificate-1.1.6.tgz#9d3a4776b1a57e9847350392114ad2f915a83dd4" + +opn@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" + dependencies: + is-wsl "^1.1.0" + optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -5876,7 +6154,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1: +path-is-inside@1.0.2, path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -5911,6 +6189,12 @@ path-to-regexp@^1.0.0, path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-type@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -6273,6 +6557,28 @@ randomatic@^1.1.3: is-number "^3.0.0" kind-of "^4.0.0" +range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-body@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + +rc@^1.0.1, rc@^1.1.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + rc@^1.1.7: version "1.2.3" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.3.tgz#51575a900f8dd68381c710b4712c2154c3e2035b" @@ -6377,6 +6683,14 @@ react-motion@^0.4.8: prop-types "^15.5.8" raf "^3.1.0" +react-motion@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316" + dependencies: + performance-now "^0.2.0" + prop-types "^15.5.8" + raf "^3.1.0" + react-onclickoutside@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.7.1.tgz#6a5b5b8b4eae6b776259712c89c8a2b36b17be93" @@ -6490,6 +6804,28 @@ react-virtualized@^9.18.5: loose-envify "^1.3.0" prop-types "^15.6.0" +react-vis@1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/react-vis/-/react-vis-1.10.2.tgz#7520bd31bb2f81a8faef49cc285f678fd0795242" + dependencies: + d3-array "^1.2.0" + d3-collection "^1.0.3" + d3-color "^1.0.3" + d3-contour "^1.1.0" + d3-format "^1.2.0" + d3-geo "^1.6.4" + d3-hierarchy "^1.1.4" + d3-interpolate "^1.1.4" + d3-sankey "^0.7.1" + d3-scale "^1.0.5" + d3-shape "^1.1.0" + d3-voronoi "^1.1.2" + deep-equal "^1.0.1" + global "^4.3.1" + hoek "4.2.1" + prop-types "^15.5.8" + react-motion "^0.5.2" + react-vis@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/react-vis/-/react-vis-1.8.2.tgz#0e0aebc427e50856a01b666569ffad0411ef050f" @@ -6686,6 +7022,19 @@ regexpu-core@^2.0.0: regjsgen "^0.2.0" regjsparser "^0.1.4" +registry-auth-token@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + dependencies: + rc "^1.0.1" + regjsgen@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" @@ -7003,10 +7352,14 @@ rxjs@^6.2.1: dependencies: tslib "^1.9.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + samsam@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" @@ -7081,10 +7434,55 @@ semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + sequencify@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" +serve@^6.3.1: + version "6.5.8" + resolved "https://registry.yarnpkg.com/serve/-/serve-6.5.8.tgz#fd7ad6b9c10ba12084053030cc1a8b636c0a10a7" + dependencies: + args "4.0.0" + basic-auth "2.0.0" + bluebird "3.5.1" + boxen "1.3.0" + chalk "2.4.1" + clipboardy "1.2.3" + dargs "5.1.0" + detect-port "1.2.3" + filesize "3.6.1" + fs-extra "6.0.1" + handlebars "4.0.11" + ip "1.1.5" + micro "9.3.1" + micro-compress "1.0.0" + mime-types "2.1.18" + node-version "1.1.3" + openssl-self-signed-certificate "1.1.6" + opn "5.3.0" + path-is-inside "1.0.2" + path-type "3.0.0" + send "0.16.2" + update-check "1.5.1" + set-blocking@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-1.0.0.tgz#cd5e5d938048df1ac92dfe92e1f16add656f5ec5" @@ -7121,6 +7519,14 @@ setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + shallow-copy@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" @@ -7377,10 +7783,14 @@ static-module@^1.1.0: static-eval "~0.2.0" through2 "~0.4.1" -"statuses@>= 1.2.1 < 2": +"statuses@>= 1.2.1 < 2", statuses@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" +"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + stdout-stream@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" @@ -7665,6 +8075,12 @@ temp@^0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" +term-size@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" + dependencies: + execa "^0.7.0" + test-exclude@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26" @@ -7904,9 +8320,9 @@ typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@^2.8.3: - version "2.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.3.tgz#5d817f9b6f31bb871835f4edf0089f21abe6c170" +typescript@^2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" ua-parser-js@^0.7.9: version "0.7.17" @@ -7994,6 +8410,14 @@ unique-stream@^2.0.2: json-stable-stringify "^1.0.0" through2-filter "^2.0.0" +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -8001,6 +8425,13 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +update-check@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.1.tgz#24fc52266273cb8684d2f1bf9687c0e52dcf709f" + dependencies: + registry-auth-token "3.3.2" + registry-url "3.1.0" + urix@^0.1.0, urix@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -8071,6 +8502,10 @@ value-or-function@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + venn.js@0.2.9: version "0.2.9" resolved "https://registry.yarnpkg.com/venn.js/-/venn.js-0.2.9.tgz#33c29075efa484731d59d884752900cc33033656" @@ -8269,6 +8704,12 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2" +widest-line@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273" + dependencies: + string-width "^2.1.1" + window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" From 31b09edadb72b39379675107a18eff31002b91fa Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Tue, 24 Jul 2018 17:17:48 -0400 Subject: [PATCH 02/18] Clean yarn.lock. --- yarn.lock | 378 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 320 insertions(+), 58 deletions(-) diff --git a/yarn.lock b/yarn.lock index df64bec876ca3..9b99898299fab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -98,6 +98,29 @@ tabbable "^1.1.0" uuid "^3.1.0" +"@elastic/eui@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-3.0.0.tgz#daa51d5ef32e0c00bcf7b53b7acea91df3a3704c" + dependencies: + classnames "^2.2.5" + core-js "^2.5.1" + focus-trap-react "^3.0.4" + highlight.js "^9.12.0" + html "^1.0.0" + keymirror "^0.1.1" + lodash "^3.10.1" + numeral "^2.0.6" + prop-types "^15.6.0" + react-ace "^5.5.0" + react-color "^2.13.8" + react-datepicker v1.4.1 + react-input-autosize "^2.2.1" + react-virtualized "^9.18.5" + react-vis "1.10.2" + serve "^6.3.1" + tabbable "^1.1.0" + uuid "^3.1.0" + "@elastic/filesaver@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@elastic/filesaver/-/filesaver-1.1.2.tgz#1998ffb3cd89c9da4ec12a7793bfcae10e30c77a" @@ -590,6 +613,13 @@ accepts@1.3.3: mime-types "~2.1.11" negotiator "0.6.1" +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" @@ -638,6 +668,10 @@ add-event-listener@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/add-event-listener/-/add-event-listener-0.0.1.tgz#a76229ebc64c8aefae204a16273a2f255abea2d0" +address@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" + adm-zip@0.4.7: version "0.4.7" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1" @@ -652,10 +686,6 @@ agent-base@4, agent-base@^4.1.0: dependencies: es6-promisify "^5.0.0" -agentkeepalive@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" - agentkeepalive@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.4.1.tgz#aa95aebc3a749bca5ed53e3880a09f5235b48f0c" @@ -861,6 +891,10 @@ aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" +arch@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" + are-we-there-yet@~1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" @@ -868,6 +902,10 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545" + argparse@^1.0.2, argparse@^1.0.7, argparse@~1.0.2: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -881,6 +919,15 @@ argparse@~0.1.15: underscore "~1.7.0" underscore.string "~2.4.0" +args@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/args/-/args-4.0.0.tgz#5ca24cdba43d4b17111c56616f5f2e9d91933954" + dependencies: + camelcase "5.0.0" + chalk "2.3.2" + leven "2.1.0" + mri "1.1.0" + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -1940,6 +1987,12 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +basic-auth@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" + dependencies: + safe-buffer "5.1.1" + batch-processor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8" @@ -2000,14 +2053,14 @@ bluebird@3.4.6: version "3.4.6" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" +bluebird@3.5.1, bluebird@^3.3.0, bluebird@^3.3.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + bluebird@^2.10.0: version "2.11.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" -bluebird@^3.3.0, bluebird@^3.3.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - bmp-js@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.3.tgz#64113e9c7cf1202b376ed607bf30626ebe57b18a" @@ -2107,7 +2160,7 @@ bounce@1.x.x: boom "7.x.x" hoek "5.x.x" -boxen@^1.2.1, boxen@^1.2.2: +boxen@1.3.0, boxen@^1.2.1, boxen@^1.2.2: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" dependencies: @@ -2472,6 +2525,10 @@ camelcase-keys@^3.0.0: camelcase "^3.0.0" map-obj "^1.0.0" +camelcase@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -2576,17 +2633,7 @@ chalk@2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3, chalk@~1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2: +chalk@2.3.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" dependencies: @@ -2594,7 +2641,15 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^2.4.1: +chalk@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.0.tgz#a060a297a6b57e15b61ca63ce84995daa0fe6e52" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@2.4.1, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -2602,6 +2657,16 @@ chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3, chalk@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + chalk@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" @@ -2833,6 +2898,13 @@ clipboard@^1.6.1: select "^1.1.2" tiny-emitter "^2.0.0" +clipboardy@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" + dependencies: + arch "^2.1.0" + execa "^0.8.0" + cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -3046,6 +3118,24 @@ component-inherit@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" +compressible@~2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.14.tgz#326c5f507fbb055f54116782b969a81b67a29da7" + dependencies: + mime-db ">= 1.34.0 < 2" + +compression@^1.6.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db" + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.14" + debug "2.6.9" + on-headers "~1.0.1" + safe-buffer "5.1.2" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -3160,7 +3250,7 @@ content-type-parser@^1.0.1, content-type-parser@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" -content-type@~1.0.1, content-type@~1.0.4: +content-type@1.0.4, content-type@~1.0.1, content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -3745,6 +3835,10 @@ d@1: dependencies: es5-ext "^0.10.9" +dargs@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829" + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -3796,7 +3890,7 @@ debug@2.6.0: dependencies: ms "0.7.2" -debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -3993,7 +4087,7 @@ depd@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.0.1.tgz#80aec64c9d6d97e65cc2a9caa93c0aa6abf73aaa" -depd@~1.1.0, depd@~1.1.1: +depd@~1.1.0, depd@~1.1.1, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -4004,6 +4098,10 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" @@ -4018,6 +4116,13 @@ detect-newline@2.X, detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" +detect-port@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.2.3.tgz#15bf49820d02deb84bfee0a74876b32d791bf610" + dependencies: + address "^1.0.1" + debug "^2.6.0" + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -4250,17 +4355,6 @@ elasticsearch-browser@^14.2.1: version "14.2.1" resolved "https://registry.yarnpkg.com/elasticsearch-browser/-/elasticsearch-browser-14.2.1.tgz#5d1a75339ec6caf2a56c7c912639ab9e83b29755" -elasticsearch@13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-13.0.1.tgz#fa58204233052c4cd221e8721e48f3906b385b32" - dependencies: - agentkeepalive "^2.2.0" - chalk "^1.0.0" - lodash "2.4.2" - lodash.get "^4.4.2" - lodash.isempty "^4.4.0" - lodash.trimend "^4.5.1" - elasticsearch@^14.1.0, elasticsearch@^14.2.0, elasticsearch@^14.2.1: version "14.2.1" resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-14.2.1.tgz#d10cb0b9562ca6614d178c30a112b93f6e8570d1" @@ -4306,7 +4400,7 @@ encode-uri-query@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/encode-uri-query/-/encode-uri-query-1.0.0.tgz#d632be4aafe8316c6145007ffb2844c5312b194c" -encodeurl@~1.0.1: +encodeurl@~1.0.1, encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4783,6 +4877,10 @@ esutils@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + even-better@7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/even-better/-/even-better-7.0.2.tgz#d056f429c90ecc20ee9494aca0a751f743504d2e" @@ -4865,6 +4963,18 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.9.0.tgz#adb7ce62cf985071f60580deb4a88b9e34712d01" @@ -5183,6 +5293,10 @@ fileset@^2.0.2: glob "^7.0.3" minimatch "^3.0.3" +filesize@3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + fill-keys@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" @@ -5375,6 +5489,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + from2@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -5404,6 +5522,14 @@ fs-extra@6.0.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" @@ -6062,9 +6188,9 @@ h2o2@5.1.1: joi "9.X.X" wreck "9.X.X" -handlebars@4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.5.tgz#92c6ed6bb164110c50d4d8d0fbddc70806c6f8e7" +handlebars@4.0.11, handlebars@^4.0.1, handlebars@^4.0.3: + version "4.0.11" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: async "^1.4.0" optimist "^0.6.1" @@ -6072,9 +6198,9 @@ handlebars@4.0.5: optionalDependencies: uglify-js "^2.6" -handlebars@^4.0.1, handlebars@^4.0.3: - version "4.0.11" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" +handlebars@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.5.tgz#92c6ed6bb164110c50d4d8d0fbddc70806c6f8e7" dependencies: async "^1.4.0" optimist "^0.6.1" @@ -6356,7 +6482,7 @@ hoek@3.x.x: version "3.0.4" resolved "https://registry.yarnpkg.com/hoek/-/hoek-3.0.4.tgz#268adff66bb6695c69b4789a88b1e0847c3f3123" -hoek@4.X.X, hoek@4.x.x: +hoek@4.2.1, hoek@4.X.X, hoek@4.x.x: version "4.2.1" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" @@ -6739,6 +6865,10 @@ ip-regex@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" +ip@1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + iron@4.x.x: version "4.0.5" resolved "https://registry.yarnpkg.com/iron/-/iron-4.0.5.tgz#4f042cceb8b9738f346b59aa734c83a89bc31428" @@ -7099,7 +7229,7 @@ is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" -is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@1.1.0, is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -7157,6 +7287,10 @@ is-word-character@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.1.tgz#5a03fa1ea91ace8a6eb0c7cd770eb86d65c8befb" +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -8209,7 +8343,7 @@ less@2.7.1: promise "^7.1.1" source-map "^0.5.3" -leven@^2.1.0: +leven@2.1.0, leven@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" @@ -8963,6 +9097,22 @@ methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" +micro-compress@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micro-compress/-/micro-compress-1.0.0.tgz#53f5a80b4ad0320ca165a559b6e3df145d4f704f" + dependencies: + compression "^1.6.2" + +micro@9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/micro/-/micro-9.3.1.tgz#0c37eba0171554b1beccda5215ff8ea4e7aa59d6" + dependencies: + arg "2.0.0" + chalk "2.4.0" + content-type "1.0.4" + is-stream "1.1.0" + raw-body "2.3.2" + micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -9010,7 +9160,11 @@ mime-db@1.x.x, mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" -mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: +"mime-db@>= 1.34.0 < 2": + version "1.35.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47" + +mime-types@2.1.18, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: @@ -9020,6 +9174,10 @@ mime@1.3.x: version "1.3.6" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + mime@^1.2.11, mime@^1.3.4, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -9175,6 +9333,10 @@ moment@2.x.x, "moment@>= 2.9.0", moment@^2.10.6, moment@^2.13.0, moment@^2.20.1: version "2.21.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" +mri@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a" + ms@0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.0.tgz#865be94c2e7397ad8a57da6a633a6e2f30798b83" @@ -9453,6 +9615,10 @@ node-status-codes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" +node-version@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/node-version/-/node-version-1.1.3.tgz#1081c87cce6d2dbbd61d0e51e28c287782678496" + nodemailer@^4.6.4: version "4.6.4" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-4.6.4.tgz#f0d72d0c6a6ec5f4369fa8f4bf5127a31baa2014" @@ -9684,6 +9850,10 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -9700,6 +9870,16 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +openssl-self-signed-certificate@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/openssl-self-signed-certificate/-/openssl-self-signed-certificate-1.1.6.tgz#9d3a4776b1a57e9847350392114ad2f915a83dd4" + +opn@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" + dependencies: + is-wsl "^1.1.0" + optimist@^0.6.1, optimist@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -10009,7 +10189,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1, path-is-absolute@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1, path-is-inside@^1.0.2: +path-is-inside@1.0.2, path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -10034,6 +10214,12 @@ path-to-regexp@^1.0.0, path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-type@3.0.0, path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -10048,12 +10234,6 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - dependencies: - pify "^3.0.0" - pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" @@ -10821,7 +11001,7 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@^1.2.0: +range-parser@^1.2.0, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" @@ -11136,6 +11316,28 @@ react-virtualized@^9.18.5: loose-envify "^1.3.0" prop-types "^15.6.0" +react-vis@1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/react-vis/-/react-vis-1.10.2.tgz#7520bd31bb2f81a8faef49cc285f678fd0795242" + dependencies: + d3-array "^1.2.0" + d3-collection "^1.0.3" + d3-color "^1.0.3" + d3-contour "^1.1.0" + d3-format "^1.2.0" + d3-geo "^1.6.4" + d3-hierarchy "^1.1.4" + d3-interpolate "^1.1.4" + d3-sankey "^0.7.1" + d3-scale "^1.0.5" + d3-shape "^1.1.0" + d3-voronoi "^1.1.2" + deep-equal "^1.0.1" + global "^4.3.1" + hoek "4.2.1" + prop-types "^15.5.8" + react-motion "^0.5.2" + react-vis@^1.8.1: version "1.9.2" resolved "https://registry.yarnpkg.com/react-vis/-/react-vis-1.9.2.tgz#4dbd5d91ac820fd39fa7ad1c892198495194f6e3" @@ -11424,14 +11626,14 @@ regexpu-core@^2.0.0: regjsgen "^0.2.0" regjsparser "^0.1.4" -registry-auth-token@^3.0.1: +registry-auth-token@3.3.2, registry-auth-token@^3.0.1: version "3.3.2" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" dependencies: rc "^1.1.6" safe-buffer "^5.0.1" -registry-url@^3.0.0, registry-url@^3.0.3: +registry-url@3.1.0, registry-url@^3.0.0, registry-url@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" dependencies: @@ -11864,10 +12066,14 @@ rxjs@^6.2.1: dependencies: tslib "^1.9.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + safe-json-stringify@~1: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.1.0.tgz#bd2b6dad1ebafab3c24672a395527f01804b7e19" @@ -11997,6 +12203,51 @@ semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +serve@^6.3.1: + version "6.5.8" + resolved "https://registry.yarnpkg.com/serve/-/serve-6.5.8.tgz#fd7ad6b9c10ba12084053030cc1a8b636c0a10a7" + dependencies: + args "4.0.0" + basic-auth "2.0.0" + bluebird "3.5.1" + boxen "1.3.0" + chalk "2.4.1" + clipboardy "1.2.3" + dargs "5.1.0" + detect-port "1.2.3" + filesize "3.6.1" + fs-extra "6.0.1" + handlebars "4.0.11" + ip "1.1.5" + micro "9.3.1" + micro-compress "1.0.0" + mime-types "2.1.18" + node-version "1.1.3" + openssl-self-signed-certificate "1.1.6" + opn "5.3.0" + path-is-inside "1.0.2" + path-type "3.0.0" + send "0.16.2" + update-check "1.5.1" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -12497,7 +12748,7 @@ static-module@^2.2.0: static-eval "^2.0.0" through2 "~2.0.3" -statuses@1, "statuses@>= 1.2.1 < 2", "statuses@>= 1.3.1 < 2": +statuses@1, "statuses@>= 1.2.1 < 2", "statuses@>= 1.3.1 < 2", statuses@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" @@ -13570,6 +13821,13 @@ upath@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" +update-check@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.1.tgz#24fc52266273cb8684d2f1bf9687c0e52dcf709f" + dependencies: + registry-auth-token "3.3.2" + registry-url "3.1.0" + update-notifier@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" @@ -13723,6 +13981,10 @@ value-or-function@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + vega-canvas@1: version "1.0.1" resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.0.1.tgz#22cfa510af0cfbd920fc6af8b6111d3de5e63c44" From f9d28f59adb71d271858523418ad83bbdbd27cce Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Wed, 25 Jul 2018 11:24:00 -0400 Subject: [PATCH 03/18] Move raw numbers/strings to constants. Remove obsolete state/props. --- .../common/constants/beats_table.ts | 15 ++++++ .../common/constants/index.ts | 1 + .../public/components/table/beats_table.tsx | 50 ++++++++----------- .../public/components/table/controls.tsx | 24 ++++----- 4 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 x-pack/plugins/beats_management/common/constants/beats_table.ts diff --git a/x-pack/plugins/beats_management/common/constants/beats_table.ts b/x-pack/plugins/beats_management/common/constants/beats_table.ts new file mode 100644 index 0000000000000..1de2638df95c2 --- /dev/null +++ b/x-pack/plugins/beats_management/common/constants/beats_table.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const TABLE_CONFIG = { + INITIAL_ROW_SIZE: 5, + PAGE_SIZE_OPTIONS: [3, 5, 10, 20], + ACTIONS: { + BULK_ASSIGN_TAG: 'BULK_ASSIGN_TAG', + BULK_DELETE: 'BULK_DELETE', + BULK_EDIT: 'BULK_EDIT', + }, +}; diff --git a/x-pack/plugins/beats_management/common/constants/index.ts b/x-pack/plugins/beats_management/common/constants/index.ts index b4e919607c604..51ee00558f7ec 100644 --- a/x-pack/plugins/beats_management/common/constants/index.ts +++ b/x-pack/plugins/beats_management/common/constants/index.ts @@ -8,3 +8,4 @@ export { PLUGIN } from './plugin'; export { INDEX_NAMES } from './index_names'; export { UNIQUENESS_ENFORCING_TYPES, ConfigurationBlockTypes } from './configuration_blocks'; export const BASE_PATH = '/management/beats_management/'; +export { TABLE_CONFIG } from './beats_table'; diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx index 4543462efa719..5f14bd2676b34 100644 --- a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx @@ -15,6 +15,7 @@ import { import { flatten, uniq } from 'lodash'; import React from 'react'; import styled from 'styled-components'; +import { TABLE_CONFIG } from '../../../common/constants'; import { CMPopulatedBeat } from '../../../common/domain_types'; import { BulkActionControlBar } from './controls'; @@ -34,7 +35,7 @@ const columns = [ field: 'full_tags', name: 'Tags', render: (value: string, beat: CMPopulatedBeat) => ( - + {beat.full_tags.map(tag => ( {tag.id} @@ -65,9 +66,6 @@ interface BeatsTableProps { interface BeatsTableState { itemsToRender: CMPopulatedBeat[]; - pageIndex: number; - pageSize: number; - search?: any; selection: CMPopulatedBeat[]; } @@ -81,27 +79,22 @@ export class BeatsTable extends React.Component true, - selectableMessage: () => null, + selectableMessage: () => 'Select this beat', }; const tagOptions = this.getTagsOptions(); @@ -110,11 +103,8 @@ export class BeatsTable extends React.Component { - const { selection } = this.state; - onBulkAction(action, selection); - }} - onSearchQueryChange={this.onQueryChange} + onBulkAction={this.handleBulkAction} + onSearchQueryChange={this.onSearchQueryChange} tagOptions={tagOptions} typeOptions={typeOptions} /> @@ -136,15 +126,21 @@ export class BeatsTable extends React.Component clause.value) : []; }; - private onQueryChange = (search: any) => { + private handleBulkAction = (action: string) => { + const { onBulkAction } = this.props; + const { selection } = this.state; + onBulkAction(action, selection); + }; + + private onSearchQueryChange = (search: any) => { const { items } = this.props; let itemsToRender = items; if (search && !search.error) { - const { query } = search; - const types = this.getClauseValuesForField(query.ast, 'type'); - const tags = this.getClauseValuesForField(query.ast, 'tag'); - const terms = query.ast.getTermClauses().map((clause: any) => clause.value); + const { ast } = search.query; + const types = this.getClauseValuesForField(ast, 'type'); + const tags = this.getClauseValuesForField(ast, 'tag'); + const terms = ast.getTermClauses().map((clause: any) => clause.value); if (types.length) { itemsToRender = itemsToRender.filter(item => types.includes(item.type)); } @@ -167,15 +163,13 @@ export class BeatsTable extends React.Component { const { items } = this.props; - const ids = flatten(items.map(({ full_tags }) => full_tags.map(({ id }) => id))); - return uniq(ids).map(id => ({ - value: id, - })); + const fullTags = flatten(items.map(item => item.full_tags)); + return uniq(fullTags.map(tag => ({ value: tag.id })), 'value'); }; private getTypeOptions = () => { const { items } = this.props; - return uniq(items.map(({ type }) => type)).map(type => ({ value: type })); + return uniq(items.map(({ type }) => ({ value: type })), 'value'); }; private setSelection = (selection: any) => { diff --git a/x-pack/plugins/beats_management/public/components/table/controls.tsx b/x-pack/plugins/beats_management/public/components/table/controls.tsx index 2602da4be6221..4be29ca0377ec 100644 --- a/x-pack/plugins/beats_management/public/components/table/controls.tsx +++ b/x-pack/plugins/beats_management/public/components/table/controls.tsx @@ -15,11 +15,7 @@ import { EuiSearchBar, } from '@elastic/eui'; import React from 'react'; - -// TODO: move to constants -const BULK_ASSIGN_TAG = 'BULK_ASSIGN_TAG'; -const BULK_DELETE = 'BULK_DELETE'; -const BULK_EDIT = 'BULK_EDIT'; +import { TABLE_CONFIG } from '../../../common/constants'; interface BulkActionControlBarState { isPopoverVisible: boolean; @@ -56,7 +52,7 @@ export class BulkActionControlBar extends React.Component< Bulk Action ); - const { onBulkAction, onSearchQueryChange, tagOptions, typeOptions } = this.props; + const { onSearchQueryChange, tagOptions, typeOptions } = this.props; const panels = [ { id: 0, @@ -65,17 +61,17 @@ export class BulkActionControlBar extends React.Component< { name: 'Bulk Edit', icon: , - onClick: () => onBulkAction(BULK_EDIT), + onClick: this.getActionHandler(TABLE_CONFIG.ACTIONS.BULK_EDIT), }, { name: 'Bulk Delete', icon: , - onClick: () => onBulkAction(BULK_DELETE), + onClick: this.getActionHandler(TABLE_CONFIG.ACTIONS.BULK_DELETE), }, { name: 'Bulk Assign Tags', icon: , - onClick: () => onBulkAction(BULK_ASSIGN_TAG), + onClick: this.getActionHandler(TABLE_CONFIG.ACTIONS.BULK_ASSIGN_TAG), }, ], }, @@ -122,15 +118,17 @@ export class BulkActionControlBar extends React.Component< ); } - private showPopover = () => { + private hidePopover = () => { this.setState({ - isPopoverVisible: true, + isPopoverVisible: false, }); }; - private hidePopover = () => { + private getActionHandler = (action: string) => () => this.props.onBulkAction(action); + + private showPopover = () => { this.setState({ - isPopoverVisible: false, + isPopoverVisible: true, }); }; } From f26178470b55511b46a0058ce7d9fe029721c3c0 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Wed, 25 Jul 2018 13:54:11 -0400 Subject: [PATCH 04/18] Update/add tests. --- .../__snapshots__/beats_table.test.tsx.snap | 112 +++++++++++++++++ .../__snapshots__/controls.test.tsx.snap | 115 ++++++++++++++++++ .../components/table/beats_table.test.tsx | 48 +++++--- .../public/components/table/controls.test.tsx | 72 +++++++++++ 4 files changed, 330 insertions(+), 17 deletions(-) create mode 100644 x-pack/plugins/beats_management/public/components/table/__snapshots__/beats_table.test.tsx.snap create mode 100644 x-pack/plugins/beats_management/public/components/table/__snapshots__/controls.test.tsx.snap create mode 100644 x-pack/plugins/beats_management/public/components/table/controls.test.tsx diff --git a/x-pack/plugins/beats_management/public/components/table/__snapshots__/beats_table.test.tsx.snap b/x-pack/plugins/beats_management/public/components/table/__snapshots__/beats_table.test.tsx.snap new file mode 100644 index 0000000000000..2a29a5e2d0224 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/__snapshots__/beats_table.test.tsx.snap @@ -0,0 +1,112 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BeatsTable component matches snapshot 1`] = ` + + + + +`; diff --git a/x-pack/plugins/beats_management/public/components/table/__snapshots__/controls.test.tsx.snap b/x-pack/plugins/beats_management/public/components/table/__snapshots__/controls.test.tsx.snap new file mode 100644 index 0000000000000..8f98e147e8392 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/__snapshots__/controls.test.tsx.snap @@ -0,0 +1,115 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BulkActionControlBar component matches snapshot 1`] = ` + + + + Bulk Action + + } + closePopover={[Function]} + id="contextMenu" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + withTitle={true} + > + , + "name": "Bulk Edit", + "onClick": [Function], + }, + Object { + "icon": , + "name": "Bulk Delete", + "onClick": [Function], + }, + Object { + "icon": , + "name": "Bulk Assign Tags", + "onClick": [Function], + }, + ], + "title": "Bulk Action", + }, + ] + } + /> + + + + + + +`; diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx index b241e04da1915..07d56deddd228 100644 --- a/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx +++ b/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx @@ -10,30 +10,44 @@ import { CMPopulatedBeat } from '../../../common/domain_types'; import { BeatsTable } from './beats_table'; describe('BeatsTable component', () => { - let items: CMPopulatedBeat[]; - let beat; + let beats: CMPopulatedBeat[]; let onBulkAction: any; beforeEach(() => { - beat = { - id: 'beatid', - access_token: 'access', - type: 'type', - host_ip: 'ip', - host_name: 'name', - full_tags: [ - { - id: 'Production', - configuration_blocks: [], - }, - ], - }; - items = [beat]; + beats = [ + { + id: 'beatid', + access_token: 'access', + type: 'type', + host_ip: 'ip', + host_name: 'name', + full_tags: [ + { + id: 'Production', + configuration_blocks: [], + }, + ], + }, + { + id: 'beatid2', + access_token: 'access', + type: 'Filebeat v6.3.2', + host_ip: '192.168.1.0', + host_name: 'name', + full_tags: [ + { + id: 'Production', + configuration_blocks: [], + }, + ], + }, + ]; onBulkAction = jest.fn(); }); it('matches snapshot', () => { - const wrapper = shallow(); + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/beats_management/public/components/table/controls.test.tsx b/x-pack/plugins/beats_management/public/components/table/controls.test.tsx new file mode 100644 index 0000000000000..b16fdde8d1700 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/controls.test.tsx @@ -0,0 +1,72 @@ +/* + * 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 { mount, shallow } from 'enzyme'; +import React from 'react'; +import { TABLE_CONFIG } from '../../../common/constants'; +import { BulkActionControlBar } from './controls'; + +describe('BulkActionControlBar component', () => { + let onBulkAction: any; + let onSearchQueryChange: any; + let tagOptions; + let typeOptions; + let props: any; + + beforeEach(() => { + onBulkAction = jest.fn(); + onSearchQueryChange = jest.fn(); + tagOptions = [{ value: 'Production' }]; + typeOptions = [{ value: 'Filebeat v6.3.2' }]; + + props = { + onBulkAction, + onSearchQueryChange, + tagOptions, + typeOptions, + }; + }); + + it('matches snapshot', () => { + const wrapper = shallow(); + + expect(wrapper).toMatchSnapshot(); + }); + + it('bulk action button exposes popover', () => { + const wrapper = mount(); + + wrapper.find('EuiButton').simulate('click'); + // @ts-ignore + expect(wrapper.instance().state.isPopoverVisible).toBe(true); + }); + + it('bulk action context item clicks trigger action handler', () => { + const wrapper = mount(); + + wrapper.find('EuiButton').simulate('click'); + wrapper + .find('EuiContextMenuItem') + .first() + .simulate('click'); + expect(onBulkAction).toHaveBeenCalledTimes(1); + expect(onBulkAction).toBeCalledWith(TABLE_CONFIG.ACTIONS.BULK_EDIT); + + wrapper + .find('EuiContextMenuItem') + .at(1) + .simulate('click'); + expect(onBulkAction).toHaveBeenCalledTimes(2); + expect(onBulkAction).toBeCalledWith(TABLE_CONFIG.ACTIONS.BULK_DELETE); + + wrapper + .find('EuiContextMenuItem') + .last() + .simulate('click'); + expect(onBulkAction).toHaveBeenCalledTimes(3); + expect(onBulkAction).toBeCalledWith(TABLE_CONFIG.ACTIONS.BULK_ASSIGN_TAG); + }); +}); From 8860a2506ab8b2505b74355951c502cbec0cb445 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Wed, 25 Jul 2018 14:39:42 -0400 Subject: [PATCH 05/18] Change prop name from "items" to "beats". --- .../components/table/beats_table.test.tsx | 2 +- .../public/components/table/beats_table.tsx | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx index 07d56deddd228..119ee5389dc74 100644 --- a/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx +++ b/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx @@ -46,7 +46,7 @@ describe('BeatsTable component', () => { }); it('matches snapshot', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx index 5f14bd2676b34..17e5683954c90 100644 --- a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx @@ -60,12 +60,12 @@ const columns = [ ]; interface BeatsTableProps { - items: CMPopulatedBeat[]; + beats: CMPopulatedBeat[]; onBulkAction: any; } interface BeatsTableState { - itemsToRender: CMPopulatedBeat[]; + beatsToRender: CMPopulatedBeat[]; selection: CMPopulatedBeat[]; } @@ -78,13 +78,13 @@ export class BeatsTable extends React.Component { - const { items } = this.props; - let itemsToRender = items; + const { beats } = this.props; + let beatsToRender = beats; if (search && !search.error) { const { ast } = search.query; @@ -142,34 +142,34 @@ export class BeatsTable extends React.Component clause.value); if (types.length) { - itemsToRender = itemsToRender.filter(item => types.includes(item.type)); + beatsToRender = beatsToRender.filter(item => types.includes(item.type)); } if (tags.length) { - itemsToRender = itemsToRender.filter(item => + beatsToRender = beatsToRender.filter(item => item.full_tags.some(({ id }) => tags.includes(id)) ); } if (terms.length) { - itemsToRender = itemsToRender.filter(item => + beatsToRender = beatsToRender.filter(item => terms.some((term: string) => item.id.includes(term)) ); } } this.setState({ - itemsToRender, + beatsToRender, }); }; private getTagsOptions = () => { - const { items } = this.props; - const fullTags = flatten(items.map(item => item.full_tags)); + const { beats } = this.props; + const fullTags = flatten(beats.map(item => item.full_tags)); return uniq(fullTags.map(tag => ({ value: tag.id })), 'value'); }; private getTypeOptions = () => { - const { items } = this.props; - return uniq(items.map(({ type }) => ({ value: type })), 'value'); + const { beats } = this.props; + return uniq(beats.map(({ type }) => ({ value: type })), 'value'); }; private setSelection = (selection: any) => { From 61ec2f24c2deb7d62af2fbbcf57dfa8420b1e665 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Wed, 25 Jul 2018 17:51:52 -0400 Subject: [PATCH 06/18] Add TagsTable component and associated search/action bar. --- .../beats_management/common/domain_types.ts | 1 + .../public/components/table/beats_table.tsx | 2 +- ...t.tsx => bulk_action_control_bar.test.tsx} | 2 +- ...ntrols.tsx => bulk_action_control_bar.tsx} | 0 .../table/tag_action_control_bar.tsx | 42 ++++++ .../public/components/table/tags_table.tsx | 138 ++++++++++++++++++ 6 files changed, 183 insertions(+), 2 deletions(-) rename x-pack/plugins/beats_management/public/components/table/{controls.test.tsx => bulk_action_control_bar.test.tsx} (96%) rename x-pack/plugins/beats_management/public/components/table/{controls.tsx => bulk_action_control_bar.tsx} (100%) create mode 100644 x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx create mode 100644 x-pack/plugins/beats_management/public/components/table/tags_table.tsx diff --git a/x-pack/plugins/beats_management/common/domain_types.ts b/x-pack/plugins/beats_management/common/domain_types.ts index a553f987a233e..59110741c0ac0 100644 --- a/x-pack/plugins/beats_management/common/domain_types.ts +++ b/x-pack/plugins/beats_management/common/domain_types.ts @@ -35,4 +35,5 @@ export interface BeatTag { id: string; configuration_blocks: ConfigurationBlock[]; color?: string; + last_updated: Date; } diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx index 17e5683954c90..cdbb59c581f9b 100644 --- a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx @@ -17,7 +17,7 @@ import React from 'react'; import styled from 'styled-components'; import { TABLE_CONFIG } from '../../../common/constants'; import { CMPopulatedBeat } from '../../../common/domain_types'; -import { BulkActionControlBar } from './controls'; +import { BulkActionControlBar } from './bulk_action_control_bar'; const columns = [ { diff --git a/x-pack/plugins/beats_management/public/components/table/controls.test.tsx b/x-pack/plugins/beats_management/public/components/table/bulk_action_control_bar.test.tsx similarity index 96% rename from x-pack/plugins/beats_management/public/components/table/controls.test.tsx rename to x-pack/plugins/beats_management/public/components/table/bulk_action_control_bar.test.tsx index b16fdde8d1700..d3b87f26e813a 100644 --- a/x-pack/plugins/beats_management/public/components/table/controls.test.tsx +++ b/x-pack/plugins/beats_management/public/components/table/bulk_action_control_bar.test.tsx @@ -7,7 +7,7 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; import { TABLE_CONFIG } from '../../../common/constants'; -import { BulkActionControlBar } from './controls'; +import { BulkActionControlBar } from './bulk_action_control_bar'; describe('BulkActionControlBar component', () => { let onBulkAction: any; diff --git a/x-pack/plugins/beats_management/public/components/table/controls.tsx b/x-pack/plugins/beats_management/public/components/table/bulk_action_control_bar.tsx similarity index 100% rename from x-pack/plugins/beats_management/public/components/table/controls.tsx rename to x-pack/plugins/beats_management/public/components/table/bulk_action_control_bar.tsx diff --git a/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx b/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx new file mode 100644 index 0000000000000..7a9e973ac80d2 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx @@ -0,0 +1,42 @@ +/* + * 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 { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + // @ts-ignore + EuiSearchBar, +} from '@elastic/eui'; +import React from 'react'; + +interface TagActionControlBarProps { + onAddTag: any; + onDeleteSelected: any; + onSearchQueryChange: any; +} + +export const TagActionControlBar: React.SFC = ({ + onAddTag, + onDeleteSelected, + onSearchQueryChange, +}) => { + return ( + + + Add Tag + + + + Delete Tag + + + + + + + ); +}; diff --git a/x-pack/plugins/beats_management/public/components/table/tags_table.tsx b/x-pack/plugins/beats_management/public/components/table/tags_table.tsx new file mode 100644 index 0000000000000..6c0def1856bcf --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/tags_table.tsx @@ -0,0 +1,138 @@ +/* + * 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 { + EuiBadge, + // @ts-ignore + EuiInMemoryTable, + EuiSpacer, +} from '@elastic/eui'; +import moment from 'moment'; +import React from 'react'; +import styled from 'styled-components'; +import { TABLE_CONFIG } from '../../../common/constants'; +import { BeatTag, ConfigurationBlock } from '../../../common/domain_types'; +import { TagActionControlBar } from './tag_action_control_bar'; + +interface TagsTableProps { + onAddTag: any; + onDeleteTags: any; + tags: BeatTag[]; +} + +interface TagsTableState { + selection: BeatTag[]; + tagsToRender: BeatTag[]; +} + +const truncateText = (text: string) => + text.length > TABLE_CONFIG.TRUNCATE_TAG_LENGTH + ? `${text.substring(0, TABLE_CONFIG.TRUNCATE_TAG_LENGTH)}...` + : text; + +const columns = [ + { + field: 'id', + name: 'Tag name', + render: (id: string, tag: BeatTag) => ( + {truncateText(tag.id)} + ), + sortable: true, + width: '70%', + }, + { + align: 'right', + field: 'configuration_blocks', + name: 'Configurations', + render: (configurationBlocks: ConfigurationBlock[]) =>
{configurationBlocks.length}
, + sortable: false, + }, + { + align: 'right', + field: 'last_updated', + name: 'Last update', + render: (lastUpdate: Date) =>
{moment(lastUpdate).fromNow()}
, + sortable: true, + }, +]; + +const TableContainer = styled.div` + padding: 16px; +`; + +export class TagsTable extends React.Component { + constructor(props: TagsTableProps) { + super(props); + + this.state = { + selection: [], + tagsToRender: props.tags, + }; + } + + public render() { + const { onAddTag } = this.props; + const { tagsToRender } = this.state; + + const pagination = { + initialPageSize: TABLE_CONFIG.INITIAL_ROW_SIZE, + pageSizeOptions: TABLE_CONFIG.PAGE_SIZE_OPTIONS, + }; + + const selectionOptions = { + onSelectionChange: this.setSelection, + selectable: () => true, + selectableMessage: () => 'Select this tag', + }; + + return ( + + + + + + ); + } + + private setSelection = (selection: any) => { + this.setState({ + selection, + }); + }; + + private onDeleteSelected = () => { + const { selection } = this.state; + const { onDeleteTags } = this.props; + onDeleteTags(selection); + }; + + private onSearchQueryChange = (search: any) => { + let tagsToRender = this.props.tags; + const { ast } = search.query; + if (search && !search.error && ast.getTermClauses().length) { + const terms = ast.getTermClauses().map((clause: any) => clause.value); + tagsToRender = tagsToRender.filter(tag => + terms.some((term: string) => tag.id.toLowerCase().includes(term.toLowerCase())) + ); + } + + this.setState({ + tagsToRender, + }); + }; +} From f00c9b9c0ca4e39e24490ac7e8b567f48679e7ab Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 26 Jul 2018 10:52:52 -0400 Subject: [PATCH 07/18] Rename some variables. --- .../public/components/table/beats_table.tsx | 10 +++++----- .../public/components/table/controls.tsx | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx index 17e5683954c90..1c6ebd311cf3b 100644 --- a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx @@ -142,16 +142,16 @@ export class BeatsTable extends React.Component clause.value); if (types.length) { - beatsToRender = beatsToRender.filter(item => types.includes(item.type)); + beatsToRender = beatsToRender.filter(beat => types.includes(beat.type)); } if (tags.length) { - beatsToRender = beatsToRender.filter(item => - item.full_tags.some(({ id }) => tags.includes(id)) + beatsToRender = beatsToRender.filter(beat => + beat.full_tags.some(({ id }) => tags.includes(id)) ); } if (terms.length) { - beatsToRender = beatsToRender.filter(item => - terms.some((term: string) => item.id.includes(term)) + beatsToRender = beatsToRender.filter(beat => + terms.some((term: string) => beat.id.includes(term)) ); } } diff --git a/x-pack/plugins/beats_management/public/components/table/controls.tsx b/x-pack/plugins/beats_management/public/components/table/controls.tsx index 4be29ca0377ec..3ae061d12692d 100644 --- a/x-pack/plugins/beats_management/public/components/table/controls.tsx +++ b/x-pack/plugins/beats_management/public/components/table/controls.tsx @@ -81,13 +81,13 @@ export class BulkActionControlBar extends React.Component< From 8035c4b447122c374fecd027b6f8940029600283 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 26 Jul 2018 11:25:51 -0400 Subject: [PATCH 08/18] Add constant after forgetting to save file. --- x-pack/plugins/beats_management/common/constants/beats_table.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/beats_management/common/constants/beats_table.ts b/x-pack/plugins/beats_management/common/constants/beats_table.ts index 1de2638df95c2..ad1a38a05df28 100644 --- a/x-pack/plugins/beats_management/common/constants/beats_table.ts +++ b/x-pack/plugins/beats_management/common/constants/beats_table.ts @@ -12,4 +12,5 @@ export const TABLE_CONFIG = { BULK_DELETE: 'BULK_DELETE', BULK_EDIT: 'BULK_EDIT', }, + TRUNCATE_TAG_LENGTH: 33, }; From 43dfc17f5ae7eb12cd41844653948e29296fd82c Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 26 Jul 2018 14:43:43 -0400 Subject: [PATCH 09/18] Fix design mistake in table component. --- .../public/components/table/tags_table.tsx | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/beats_management/public/components/table/tags_table.tsx b/x-pack/plugins/beats_management/public/components/table/tags_table.tsx index 6c0def1856bcf..9942f0a42f16e 100644 --- a/x-pack/plugins/beats_management/public/components/table/tags_table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/tags_table.tsx @@ -5,6 +5,7 @@ */ import { + // @ts-ignore EuiBadge, // @ts-ignore EuiInMemoryTable, @@ -25,7 +26,7 @@ interface TagsTableProps { interface TagsTableState { selection: BeatTag[]; - tagsToRender: BeatTag[]; + search: any; } const truncateText = (text: string) => @@ -69,13 +70,12 @@ export class TagsTable extends React.Component { this.state = { selection: [], - tagsToRender: props.tags, + search: null, }; } public render() { const { onAddTag } = this.props; - const { tagsToRender } = this.state; const pagination = { initialPageSize: TABLE_CONFIG.INITIAL_ROW_SIZE, @@ -99,7 +99,7 @@ export class TagsTable extends React.Component { { ); } + private getTagsToRender() { + const { search } = this.state; + let tagsToRender = this.props.tags; + if (search && !search.error && search.query.ast.getTermClauses().length) { + const { ast } = search.query; + const terms = ast.getTermClauses().map((clause: any) => clause.value); + tagsToRender = tagsToRender.filter(tag => + terms.some((term: string) => tag.id.toLowerCase().includes(term.toLowerCase())) + ); + } + return tagsToRender; + } + private setSelection = (selection: any) => { this.setState({ selection, @@ -122,17 +135,8 @@ export class TagsTable extends React.Component { }; private onSearchQueryChange = (search: any) => { - let tagsToRender = this.props.tags; - const { ast } = search.query; - if (search && !search.error && ast.getTermClauses().length) { - const terms = ast.getTermClauses().map((clause: any) => clause.value); - tagsToRender = tagsToRender.filter(tag => - terms.some((term: string) => tag.id.toLowerCase().includes(term.toLowerCase())) - ); - } - this.setState({ - tagsToRender, + search, }); }; } From d6ac178502ed465a30ceb58cf06f4ea4fdf8d93d Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 26 Jul 2018 14:51:36 -0400 Subject: [PATCH 10/18] Disable delete button when no tags selected. --- .../public/components/table/tag_action_control_bar.tsx | 4 +++- .../beats_management/public/components/table/tags_table.tsx | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx b/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx index 7a9e973ac80d2..5c28974eca421 100644 --- a/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx +++ b/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx @@ -14,12 +14,14 @@ import { import React from 'react'; interface TagActionControlBarProps { + isDeleteDisabled: boolean; onAddTag: any; onDeleteSelected: any; onSearchQueryChange: any; } export const TagActionControlBar: React.SFC = ({ + isDeleteDisabled, onAddTag, onDeleteSelected, onSearchQueryChange, @@ -30,7 +32,7 @@ export const TagActionControlBar: React.SFC = ({ Add Tag - + Delete Tag diff --git a/x-pack/plugins/beats_management/public/components/table/tags_table.tsx b/x-pack/plugins/beats_management/public/components/table/tags_table.tsx index 9942f0a42f16e..f61a991ed359d 100644 --- a/x-pack/plugins/beats_management/public/components/table/tags_table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/tags_table.tsx @@ -76,6 +76,7 @@ export class TagsTable extends React.Component { public render() { const { onAddTag } = this.props; + const { selection } = this.state; const pagination = { initialPageSize: TABLE_CONFIG.INITIAL_ROW_SIZE, @@ -91,6 +92,7 @@ export class TagsTable extends React.Component { return ( Date: Thu, 26 Jul 2018 15:10:57 -0400 Subject: [PATCH 11/18] Export tags table from index.ts. --- x-pack/plugins/beats_management/public/components/table/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/beats_management/public/components/table/index.ts b/x-pack/plugins/beats_management/public/components/table/index.ts index 73facdbdb2ca0..72a85e300d618 100644 --- a/x-pack/plugins/beats_management/public/components/table/index.ts +++ b/x-pack/plugins/beats_management/public/components/table/index.ts @@ -5,3 +5,4 @@ */ export { BeatsTable } from './beats_table'; +export { TagsTable } from './tags_table'; From eb9495bb68fb1f915219775f60992754b9fc8408 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 26 Jul 2018 16:22:02 -0400 Subject: [PATCH 12/18] Move search bar filter definitions to table render. --- .../public/components/table/beats_table.tsx | 18 +++++++++++-- .../public/components/table/controls.tsx | 25 +++---------------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx index 1c6ebd311cf3b..dcb7b843d079f 100644 --- a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx @@ -100,13 +100,27 @@ export class BeatsTable extends React.Component ); - const { onSearchQueryChange, tagOptions, typeOptions } = this.props; + const { onSearchQueryChange } = this.props; const panels = [ { id: 0, @@ -97,20 +93,7 @@ export class BulkActionControlBar extends React.Component< box={{ incremental: true, }} - filters={[ - { - type: 'field_value_selection', - field: 'type', - name: 'Type', - options: typeOptions, - }, - { - type: 'field_value_selection', - field: 'tag', - name: 'Tags', - options: tagOptions, - }, - ]} + filters={searchBarFilters} onChange={onSearchQueryChange} /> From 9e2a38e0de63d736e7ec901240670d1f51884833 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 2 Aug 2018 16:17:36 -0400 Subject: [PATCH 13/18] Update table to support assignment options. --- .../__snapshots__/beats_table.test.tsx.snap | 112 ---------- .../__snapshots__/controls.test.tsx.snap | 115 ---------- .../components/table/beats_table.test.tsx | 53 ----- .../public/components/table/beats_table.tsx | 194 ----------------- .../public/components/table/controls.test.tsx | 72 ------ .../public/components/table/controls.tsx | 205 ++++++++++++------ .../public/components/table/index.ts | 4 +- .../public/components/table/table.tsx | 88 ++++++++ .../components/table/table_type_configs.tsx | 121 +++++++++++ .../public/pages/main/beats.tsx | 105 ++++++++- 10 files changed, 448 insertions(+), 621 deletions(-) delete mode 100644 x-pack/plugins/beats_management/public/components/table/__snapshots__/beats_table.test.tsx.snap delete mode 100644 x-pack/plugins/beats_management/public/components/table/__snapshots__/controls.test.tsx.snap delete mode 100644 x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx delete mode 100644 x-pack/plugins/beats_management/public/components/table/beats_table.tsx delete mode 100644 x-pack/plugins/beats_management/public/components/table/controls.test.tsx create mode 100644 x-pack/plugins/beats_management/public/components/table/table.tsx create mode 100644 x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx diff --git a/x-pack/plugins/beats_management/public/components/table/__snapshots__/beats_table.test.tsx.snap b/x-pack/plugins/beats_management/public/components/table/__snapshots__/beats_table.test.tsx.snap deleted file mode 100644 index 2a29a5e2d0224..0000000000000 --- a/x-pack/plugins/beats_management/public/components/table/__snapshots__/beats_table.test.tsx.snap +++ /dev/null @@ -1,112 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`BeatsTable component matches snapshot 1`] = ` - - - - -`; diff --git a/x-pack/plugins/beats_management/public/components/table/__snapshots__/controls.test.tsx.snap b/x-pack/plugins/beats_management/public/components/table/__snapshots__/controls.test.tsx.snap deleted file mode 100644 index 8f98e147e8392..0000000000000 --- a/x-pack/plugins/beats_management/public/components/table/__snapshots__/controls.test.tsx.snap +++ /dev/null @@ -1,115 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`BulkActionControlBar component matches snapshot 1`] = ` - - - - Bulk Action - - } - closePopover={[Function]} - id="contextMenu" - isOpen={false} - ownFocus={false} - panelPaddingSize="none" - withTitle={true} - > - , - "name": "Bulk Edit", - "onClick": [Function], - }, - Object { - "icon": , - "name": "Bulk Delete", - "onClick": [Function], - }, - Object { - "icon": , - "name": "Bulk Assign Tags", - "onClick": [Function], - }, - ], - "title": "Bulk Action", - }, - ] - } - /> - - - - - - -`; diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx deleted file mode 100644 index 119ee5389dc74..0000000000000 --- a/x-pack/plugins/beats_management/public/components/table/beats_table.test.tsx +++ /dev/null @@ -1,53 +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 { shallow } from 'enzyme'; -import React from 'react'; -import { CMPopulatedBeat } from '../../../common/domain_types'; -import { BeatsTable } from './beats_table'; - -describe('BeatsTable component', () => { - let beats: CMPopulatedBeat[]; - let onBulkAction: any; - - beforeEach(() => { - beats = [ - { - id: 'beatid', - access_token: 'access', - type: 'type', - host_ip: 'ip', - host_name: 'name', - full_tags: [ - { - id: 'Production', - configuration_blocks: [], - }, - ], - }, - { - id: 'beatid2', - access_token: 'access', - type: 'Filebeat v6.3.2', - host_ip: '192.168.1.0', - host_name: 'name', - full_tags: [ - { - id: 'Production', - configuration_blocks: [], - }, - ], - }, - ]; - onBulkAction = jest.fn(); - }); - - it('matches snapshot', () => { - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx b/x-pack/plugins/beats_management/public/components/table/beats_table.tsx deleted file mode 100644 index dcb7b843d079f..0000000000000 --- a/x-pack/plugins/beats_management/public/components/table/beats_table.tsx +++ /dev/null @@ -1,194 +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 { - // @ts-ignore - EuiBadge, - EuiFlexGroup, - // @ts-ignore - EuiInMemoryTable, - EuiLink, -} from '@elastic/eui'; -import { flatten, uniq } from 'lodash'; -import React from 'react'; -import styled from 'styled-components'; -import { TABLE_CONFIG } from '../../../common/constants'; -import { CMPopulatedBeat } from '../../../common/domain_types'; -import { BulkActionControlBar } from './controls'; - -const columns = [ - { - field: 'id', - name: 'Beat name', - render: (id: string) => {id}, - sortable: true, - }, - { - field: 'type', - name: 'Type', - sortable: true, - }, - { - field: 'full_tags', - name: 'Tags', - render: (value: string, beat: CMPopulatedBeat) => ( - - {beat.full_tags.map(tag => ( - - {tag.id} - - ))} - - ), - sortable: false, - }, - { - // TODO: update to use actual metadata field - field: 'event_rate', - name: 'Event rate', - sortable: true, - }, - { - // TODO: update to use actual metadata field - field: 'last_updated', - name: 'Last config update', - sortable: true, - }, -]; - -interface BeatsTableProps { - beats: CMPopulatedBeat[]; - onBulkAction: any; -} - -interface BeatsTableState { - beatsToRender: CMPopulatedBeat[]; - selection: CMPopulatedBeat[]; -} - -const TableContainer = styled.div` - padding: 16px; -`; - -export class BeatsTable extends React.Component { - constructor(props: BeatsTableProps) { - super(props); - - this.state = { - beatsToRender: props.beats, - selection: [], - }; - } - - public render() { - const { beatsToRender } = this.state; - - const pagination = { - initialPageSize: TABLE_CONFIG.INITIAL_ROW_SIZE, - pageSizeOptions: TABLE_CONFIG.PAGE_SIZE_OPTIONS, - }; - - const selectionOptions = { - onSelectionChange: this.setSelection, - selectable: () => true, - selectableMessage: () => 'Select this beat', - }; - - const tagOptions = this.getTagsOptions(); - const typeOptions = this.getTypeOptions(); - - const searchBarFilters = [ - { - type: 'field_value_selection', - field: 'type', - name: 'Type', - options: typeOptions, - }, - { - type: 'field_value_selection', - field: 'tag', - name: 'Tags', - options: tagOptions, - }, - ]; - - return ( - - - - - ); - } - - private getClauseValuesForField = (ast: any, fieldName: string) => { - const clauses = ast.getFieldClauses(fieldName); - return clauses ? clauses.map((clause: any) => clause.value) : []; - }; - - private handleBulkAction = (action: string) => { - const { onBulkAction } = this.props; - const { selection } = this.state; - onBulkAction(action, selection); - }; - - private onSearchQueryChange = (search: any) => { - const { beats } = this.props; - let beatsToRender = beats; - - if (search && !search.error) { - const { ast } = search.query; - const types = this.getClauseValuesForField(ast, 'type'); - const tags = this.getClauseValuesForField(ast, 'tag'); - const terms = ast.getTermClauses().map((clause: any) => clause.value); - if (types.length) { - beatsToRender = beatsToRender.filter(beat => types.includes(beat.type)); - } - if (tags.length) { - beatsToRender = beatsToRender.filter(beat => - beat.full_tags.some(({ id }) => tags.includes(id)) - ); - } - if (terms.length) { - beatsToRender = beatsToRender.filter(beat => - terms.some((term: string) => beat.id.includes(term)) - ); - } - } - - this.setState({ - beatsToRender, - }); - }; - - private getTagsOptions = () => { - const { beats } = this.props; - const fullTags = flatten(beats.map(item => item.full_tags)); - return uniq(fullTags.map(tag => ({ value: tag.id })), 'value'); - }; - - private getTypeOptions = () => { - const { beats } = this.props; - return uniq(beats.map(({ type }) => ({ value: type })), 'value'); - }; - - private setSelection = (selection: any) => { - this.setState({ - selection, - }); - }; -} diff --git a/x-pack/plugins/beats_management/public/components/table/controls.test.tsx b/x-pack/plugins/beats_management/public/components/table/controls.test.tsx deleted file mode 100644 index b16fdde8d1700..0000000000000 --- a/x-pack/plugins/beats_management/public/components/table/controls.test.tsx +++ /dev/null @@ -1,72 +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 { mount, shallow } from 'enzyme'; -import React from 'react'; -import { TABLE_CONFIG } from '../../../common/constants'; -import { BulkActionControlBar } from './controls'; - -describe('BulkActionControlBar component', () => { - let onBulkAction: any; - let onSearchQueryChange: any; - let tagOptions; - let typeOptions; - let props: any; - - beforeEach(() => { - onBulkAction = jest.fn(); - onSearchQueryChange = jest.fn(); - tagOptions = [{ value: 'Production' }]; - typeOptions = [{ value: 'Filebeat v6.3.2' }]; - - props = { - onBulkAction, - onSearchQueryChange, - tagOptions, - typeOptions, - }; - }); - - it('matches snapshot', () => { - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it('bulk action button exposes popover', () => { - const wrapper = mount(); - - wrapper.find('EuiButton').simulate('click'); - // @ts-ignore - expect(wrapper.instance().state.isPopoverVisible).toBe(true); - }); - - it('bulk action context item clicks trigger action handler', () => { - const wrapper = mount(); - - wrapper.find('EuiButton').simulate('click'); - wrapper - .find('EuiContextMenuItem') - .first() - .simulate('click'); - expect(onBulkAction).toHaveBeenCalledTimes(1); - expect(onBulkAction).toBeCalledWith(TABLE_CONFIG.ACTIONS.BULK_EDIT); - - wrapper - .find('EuiContextMenuItem') - .at(1) - .simulate('click'); - expect(onBulkAction).toHaveBeenCalledTimes(2); - expect(onBulkAction).toBeCalledWith(TABLE_CONFIG.ACTIONS.BULK_DELETE); - - wrapper - .find('EuiContextMenuItem') - .last() - .simulate('click'); - expect(onBulkAction).toHaveBeenCalledTimes(3); - expect(onBulkAction).toBeCalledWith(TABLE_CONFIG.ACTIONS.BULK_ASSIGN_TAG); - }); -}); diff --git a/x-pack/plugins/beats_management/public/components/table/controls.tsx b/x-pack/plugins/beats_management/public/components/table/controls.tsx index ed22553c1f741..db8528f27ef63 100644 --- a/x-pack/plugins/beats_management/public/components/table/controls.tsx +++ b/x-pack/plugins/beats_management/public/components/table/controls.tsx @@ -9,97 +9,162 @@ import { EuiContextMenu, EuiFlexGroup, EuiFlexItem, - EuiIcon, + EuiLoadingSpinner, EuiPopover, // @ts-ignore EuiSearchBar, } from '@elastic/eui'; import React from 'react'; -import { TABLE_CONFIG } from '../../../common/constants'; +import { ControlDefinitions } from './table_type_configs'; -interface BulkActionControlBarState { - isPopoverVisible: boolean; +interface ControlBarProps { + assignmentOptions: any[] | null; + assignmentTitle: string | null; + showAssignmentOptions: boolean; + controlDefinitions: ControlDefinitions; + selectionCount: number; + actionHandler(actionType: string, payload?: any): void; } -interface BulkActionControlBarProps { - onBulkAction: any; - onSearchQueryChange: any; - searchBarFilters: any[]; +interface ControlBarState { + isPopoverVisible: boolean; + isAssignmentPopoverVisible: boolean; } -export class BulkActionControlBar extends React.Component< - BulkActionControlBarProps, - BulkActionControlBarState -> { - constructor(props: BulkActionControlBarProps) { +export class ControlBar extends React.Component { + constructor(props: ControlBarProps) { super(props); this.state = { isPopoverVisible: false, + isAssignmentPopoverVisible: false, }; } public render() { - const { searchBarFilters } = this.props; - const { isPopoverVisible } = this.state; + const { selectionCount, showAssignmentOptions } = this.props; + return selectionCount !== 0 && showAssignmentOptions + ? this.renderAssignmentOptions() + : this.renderDefaultControls(); + } - const bulkActionButton = ( - - Bulk Action - - ); - const { onSearchQueryChange } = this.props; - const panels = [ - { - id: 0, - title: 'Bulk Action', - items: [ - { - name: 'Bulk Edit', - icon: , - onClick: this.getActionHandler(TABLE_CONFIG.ACTIONS.BULK_EDIT), - }, - { - name: 'Bulk Delete', - icon: , - onClick: this.getActionHandler(TABLE_CONFIG.ACTIONS.BULK_DELETE), - }, - { - name: 'Bulk Assign Tags', - icon: , - onClick: this.getActionHandler(TABLE_CONFIG.ACTIONS.BULK_ASSIGN_TAG), - }, - ], - }, - ]; + private renderAssignmentOptions = () => ( + + {this.renderActionButton()} + {this.props.selectionCount} selected + + Disenroll Selected + + + { + this.showAssignmentPopover(); + this.props.actionHandler('loadAssignmentOptions'); + }} + > + {this.props.assignmentTitle} + + } + closePopover={this.hideAssignmentPopover} + id="assignmentList" + isOpen={this.state.isAssignmentPopoverVisible} + panelPaddingSize="s" + withTitle + > + {this.props.assignmentOptions ? ( + // @ts-ignore direction prop not available on current typing + + {this.props.assignmentOptions} + + ) : ( +
+ Loading +
+ )} +
+
+
+ ); + + private renderDefaultControls = () => ( + + {this.renderActionButton()} + + this.props.actionHandler('search', query)} + /> + + + ); + private renderActionButton = () => { + const { + controlDefinitions: { actions }, + actionHandler, + } = this.props; + + if (actions.length === 0) { + return null; + } else if (actions.length === 1) { + const action = actions[0]; + return ( + actionHandler(action.action)} + > + {action.name} + + ); + } return ( - - - - - - - - - - + + Bulk Action + + } + closePopover={this.hidePopover} + id="contextMenu" + isOpen={this.state.isPopoverVisible} + panelPaddingSize="none" + withTitle + > + ({ + ...action, + onClick: () => actionHandler(action.action), + })), + }, + ]} + /> + ); - } + }; + + private hideAssignmentPopover = () => { + this.setState({ + isAssignmentPopoverVisible: false, + }); + }; + + private showAssignmentPopover = () => { + this.setState({ + isAssignmentPopoverVisible: true, + }); + }; private hidePopover = () => { this.setState({ @@ -107,8 +172,6 @@ export class BulkActionControlBar extends React.Component< }); }; - private getActionHandler = (action: string) => () => this.props.onBulkAction(action); - private showPopover = () => { this.setState({ isPopoverVisible: true, diff --git a/x-pack/plugins/beats_management/public/components/table/index.ts b/x-pack/plugins/beats_management/public/components/table/index.ts index 73facdbdb2ca0..0789cad5c3022 100644 --- a/x-pack/plugins/beats_management/public/components/table/index.ts +++ b/x-pack/plugins/beats_management/public/components/table/index.ts @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { BeatsTable } from './beats_table'; +export { Table } from './table'; +export { ControlBar } from './controls'; +export { BeatsTableType } from './table_type_configs'; diff --git a/x-pack/plugins/beats_management/public/components/table/table.tsx b/x-pack/plugins/beats_management/public/components/table/table.tsx new file mode 100644 index 0000000000000..f38feb7ca2ec4 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/table.tsx @@ -0,0 +1,88 @@ +/* + * 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 { + // @ts-ignore + EuiInMemoryTable, + EuiSpacer, +} from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; +import { TABLE_CONFIG } from '../../../common/constants'; +import { CMPopulatedBeat } from '../../../common/domain_types'; +import { ControlBar } from './controls'; +import { TableType } from './table_type_configs'; + +interface BeatsTableProps { + assignmentOptions: any[] | null; + assignmentTitle: string | null; + items: any[]; + type: TableType; + actionHandler(action: string, payload?: any): void; +} + +interface BeatsTableState { + selection: CMPopulatedBeat[]; +} + +const TableContainer = styled.div` + padding: 16px; +`; + +export class Table extends React.Component { + constructor(props: BeatsTableProps) { + super(props); + + this.state = { + selection: [], + }; + } + + public render() { + const { actionHandler, assignmentOptions, assignmentTitle, items, type } = this.props; + + const pagination = { + initialPageSize: TABLE_CONFIG.INITIAL_ROW_SIZE, + pageSizeOptions: TABLE_CONFIG.PAGE_SIZE_OPTIONS, + }; + + const selectionOptions = { + onSelectionChange: this.setSelection, + selectable: () => true, + selectableMessage: () => 'Select this beat', + selection: this.state.selection, + }; + + return ( + + actionHandler(action, payload)} + assignmentOptions={assignmentOptions} + assignmentTitle={assignmentTitle} + controlDefinitions={type.controlDefinitions(items)} + selectionCount={this.state.selection.length} + showAssignmentOptions={true} + /> + + + + ); + } + + private setSelection = (selection: any) => { + this.setState({ + selection, + }); + }; +} diff --git a/x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx b/x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx new file mode 100644 index 0000000000000..484f067b2cb76 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx @@ -0,0 +1,121 @@ +/* + * 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. + */ + +// @ts-ignore +import { EuiBadge, EuiFlexGroup, EuiIcon, EuiLink } from '@elastic/eui'; +import { flatten, uniq } from 'lodash'; +import moment from 'moment'; +import React from 'react'; +import { CMPopulatedBeat } from '../../../common/domain_types'; + +export interface ColumnDefinition { + field: string; + name: string; + sortable?: boolean; + render?(value: any, object?: any): any; +} + +export interface ActionDefinition { + action: string; + danger?: boolean; + icon?: any; + name: string; +} + +interface FilterOption { + value: string; +} + +export interface FilterDefinition { + field: string; + name: string; + options?: FilterOption[]; + type: string; +} + +export interface ControlDefinitions { + actions: ActionDefinition[]; + filters: FilterDefinition[]; +} + +export interface TableType { + columnDefinitions: ColumnDefinition[]; + controlDefinitions(items: any[]): ControlDefinitions; +} + +export const BeatsTableType: TableType = { + columnDefinitions: [ + { + field: 'id', + name: 'Beat name', + render: (id: string) => {id}, + sortable: true, + }, + { + field: 'type', + name: 'Type', + sortable: true, + }, + { + field: 'full_tags', + name: 'Tags', + render: (value: string, beat: CMPopulatedBeat) => ( + + {beat.full_tags.map(tag => ( + + {tag.id} + + ))} + + ), + sortable: false, + }, + { + // TODO: update to use actual metadata field + field: 'event_rate', + name: 'Event rate', + sortable: true, + }, + { + // TODO: update to use actual metadata field + field: 'last_updated', + name: 'Last config update', + render: (value: Date) =>
{moment(value).fromNow()}
, + sortable: true, + }, + ], + controlDefinitions: (data: any) => ({ + actions: [ + { + action: 'edit', + name: 'Bulk Edit', + icon: , + }, + { + action: 'delete', + name: 'Bulk Delete', + icon: , + }, + ], + filters: [ + { + type: 'field_value_selection', + field: 'type', + name: 'Type', + options: uniq(data.map(({ type }: { type: any }) => ({ value: type })), 'value'), + }, + { + type: 'field_value_selection', + field: 'full_tags', + name: 'Tags', + options: uniq( + flatten(data.map((item: any) => item.full_tags)).map((tag: any) => ({ value: tag.id })), + 'value' + ), + }, + ], + }), +}; diff --git a/x-pack/plugins/beats_management/public/pages/main/beats.tsx b/x-pack/plugins/beats_management/public/pages/main/beats.tsx index a8c7518a3e27c..1d8a9e6717605 100644 --- a/x-pack/plugins/beats_management/public/pages/main/beats.tsx +++ b/x-pack/plugins/beats_management/public/pages/main/beats.tsx @@ -5,8 +5,11 @@ */ import { + // @ts-ignore typings for EuiBadge not present in current version + EuiBadge, EuiButton, EuiButtonEmpty, + EuiFlexItem, EuiModal, EuiModalBody, EuiModalFooter, @@ -15,16 +18,20 @@ import { EuiOverlayMask, } from '@elastic/eui'; -import { CMBeat } from '../../../common/domain_types'; +import React from 'react'; +import { BeatTag, CMBeat, CMPopulatedBeat } from '../../../common/domain_types'; +import { BeatsTagAssignment } from '../../../server/lib/adapters/beats/adapter_types'; +import { BeatsTableType, Table } from '../../components/table'; import { FrontendLibs } from '../../lib/lib'; -import React from 'react'; interface BeatsPageProps { libs: FrontendLibs; } interface BeatsPageState { beats: CMBeat[]; + tags: any[] | null; + tableRef: any; } export class BeatsPage extends React.PureComponent { @@ -72,17 +79,109 @@ export class BeatsPage extends React.PureComponentbeats table and stuff - {this.state.beats.length}; + return ( + + ); } + + private handleBeatsActions = (action: string, payload: any) => { + switch (action) { + case 'edit': + // TODO: navigate to edit page + break; + case 'delete': + this.deleteSelected(); + break; + case 'search': + this.handleSearchQuery(payload); + break; + case 'loadAssignmentOptions': + this.loadTags(); + break; + } + + this.loadBeats(); + }; + + // TODO: call delete endpoint + private deleteSelected = async () => { + // const selected = this.getSelectedBeats(); + // await this.props.libs.beats.delete(selected); + }; + private async loadBeats() { const beats = await this.props.libs.beats.getAll(); this.setState({ beats, }); } + + // todo: add reference to ES filter endpoint + private handleSearchQuery = (query: any) => { + // await this.props.libs.beats.searach(query); + }; + + private loadTags = async () => { + const tags = await this.props.libs.tags.getAll(); + const selectedBeats = this.getSelectedBeats(); + + const renderedTags = tags.map((tag: BeatTag) => { + const hasMatches = selectedBeats.some((beat: any) => + beat.full_tags.some((t: any) => t.id === tag.id) + ); + + return ( + + this.removeTagsFromBeats(selectedBeats, tag) + : () => this.assignTagsToBeats(selectedBeats, tag) + } + onClickAriaLabel={tag.id} + > + {tag.id} + + + ); + }); + this.setState({ + tags: renderedTags, + }); + }; + + private createBeatTagAssignments = ( + beats: CMPopulatedBeat[], + tag: BeatTag + ): BeatsTagAssignment[] => beats.map(({ id }) => ({ beatId: id, tag: tag.id })); + + private removeTagsFromBeats = async (beats: CMPopulatedBeat[], tag: BeatTag) => { + await this.props.libs.beats.removeTagsFromBeats(this.createBeatTagAssignments(beats, tag)); + this.loadBeats(); + }; + + private assignTagsToBeats = async (beats: CMPopulatedBeat[], tag: BeatTag) => { + await this.props.libs.beats.assignTagsToBeats(this.createBeatTagAssignments(beats, tag)); + this.loadBeats(); + }; + + private getSelectedBeats = () => { + return this.state.tableRef.current.state.selection; + }; } From f4ed8bc336a80c53d70ebbbae7187df10ef86eba Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 2 Aug 2018 16:46:45 -0400 Subject: [PATCH 14/18] Update action control position. --- .../common/constants/index.ts | 2 +- .../constants/{beats_table.ts => table.ts} | 5 ----- .../public/components/table/controls.tsx | 20 ++++++------------- .../components/table/table_type_configs.tsx | 9 ++------- 4 files changed, 9 insertions(+), 27 deletions(-) rename x-pack/plugins/beats_management/common/constants/{beats_table.ts => table.ts} (74%) diff --git a/x-pack/plugins/beats_management/common/constants/index.ts b/x-pack/plugins/beats_management/common/constants/index.ts index 51ee00558f7ec..50851dcef947e 100644 --- a/x-pack/plugins/beats_management/common/constants/index.ts +++ b/x-pack/plugins/beats_management/common/constants/index.ts @@ -8,4 +8,4 @@ export { PLUGIN } from './plugin'; export { INDEX_NAMES } from './index_names'; export { UNIQUENESS_ENFORCING_TYPES, ConfigurationBlockTypes } from './configuration_blocks'; export const BASE_PATH = '/management/beats_management/'; -export { TABLE_CONFIG } from './beats_table'; +export { TABLE_CONFIG } from './table'; diff --git a/x-pack/plugins/beats_management/common/constants/beats_table.ts b/x-pack/plugins/beats_management/common/constants/table.ts similarity index 74% rename from x-pack/plugins/beats_management/common/constants/beats_table.ts rename to x-pack/plugins/beats_management/common/constants/table.ts index 1de2638df95c2..801a60082d1b8 100644 --- a/x-pack/plugins/beats_management/common/constants/beats_table.ts +++ b/x-pack/plugins/beats_management/common/constants/table.ts @@ -7,9 +7,4 @@ export const TABLE_CONFIG = { INITIAL_ROW_SIZE: 5, PAGE_SIZE_OPTIONS: [3, 5, 10, 20], - ACTIONS: { - BULK_ASSIGN_TAG: 'BULK_ASSIGN_TAG', - BULK_DELETE: 'BULK_DELETE', - BULK_EDIT: 'BULK_EDIT', - }, }; diff --git a/x-pack/plugins/beats_management/public/components/table/controls.tsx b/x-pack/plugins/beats_management/public/components/table/controls.tsx index db8528f27ef63..ab5fb655fe361 100644 --- a/x-pack/plugins/beats_management/public/components/table/controls.tsx +++ b/x-pack/plugins/beats_management/public/components/table/controls.tsx @@ -50,11 +50,8 @@ export class ControlBar extends React.Component ( - {this.renderActionButton()} {this.props.selectionCount} selected - - Disenroll Selected - + {this.renderActionButton()} ( - - {this.renderActionButton()} - - this.props.actionHandler('search', query)} - /> - - + this.props.actionHandler('search', query)} + /> ); private renderActionButton = () => { diff --git a/x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx b/x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx index 484f067b2cb76..fb4529da171cb 100644 --- a/x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx +++ b/x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx @@ -90,14 +90,9 @@ export const BeatsTableType: TableType = { controlDefinitions: (data: any) => ({ actions: [ { - action: 'edit', - name: 'Bulk Edit', - icon: , - }, - { + name: 'Disenroll Selected', action: 'delete', - name: 'Bulk Delete', - icon: , + danger: true, }, ], filters: [ From 05cbac243d9733cfcd961d08b1c4ddbe3a833749 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Fri, 3 Aug 2018 09:49:35 -0400 Subject: [PATCH 15/18] Refactor split render function into custom components. --- .../public/components/table/action_button.tsx | 63 +++++++ .../components/table/assignment_options.tsx | 103 +++++++++++ .../public/components/table/controls.tsx | 168 +++--------------- .../public/components/table/table.tsx | 2 +- 4 files changed, 189 insertions(+), 147 deletions(-) create mode 100644 x-pack/plugins/beats_management/public/components/table/action_button.tsx create mode 100644 x-pack/plugins/beats_management/public/components/table/assignment_options.tsx diff --git a/x-pack/plugins/beats_management/public/components/table/action_button.tsx b/x-pack/plugins/beats_management/public/components/table/action_button.tsx new file mode 100644 index 0000000000000..e91e620ed8d8c --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/action_button.tsx @@ -0,0 +1,63 @@ +/* + * 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 { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui'; +import React from 'react'; +import { ActionDefinition } from './table_type_configs'; + +interface ActionButtonProps { + actions: ActionDefinition[]; + isPopoverVisible: boolean; + actionHandler(action: string, payload?: any): void; + hidePopover(): void; + showPopover(): void; +} + +export function ActionButton(props: ActionButtonProps) { + const { actions, actionHandler, hidePopover, isPopoverVisible, showPopover } = props; + if (actions.length === 0) { + return null; + } else if (actions.length === 1) { + const action = actions[0]; + return ( + actionHandler(action.action)} + > + {action.name} + + ); + } + return ( + + Bulk Action + + } + closePopover={hidePopover} + id="contextMenu" + isOpen={isPopoverVisible} + panelPaddingSize="none" + withTitle + > + ({ + ...action, + onClick: () => actionHandler(action.action), + })), + }, + ]} + /> + + ); +} diff --git a/x-pack/plugins/beats_management/public/components/table/assignment_options.tsx b/x-pack/plugins/beats_management/public/components/table/assignment_options.tsx new file mode 100644 index 0000000000000..60a6bf2b46952 --- /dev/null +++ b/x-pack/plugins/beats_management/public/components/table/assignment_options.tsx @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiPopover } from '@elastic/eui'; +import React from 'react'; +import { ActionButton } from './action_button'; +import { ControlDefinitions } from './table_type_configs'; + +interface AssignmentOptionsProps { + assignmentOptions: any[] | null; + assignmentTitle: string | null; + controlDefinitions: ControlDefinitions; + selectionCount: number; + actionHandler(action: string, payload?: any): void; +} + +interface AssignmentOptionsState { + isAssignmentPopoverVisible: boolean; + isActionPopoverVisible: boolean; +} + +export class AssignmentOptions extends React.Component< + AssignmentOptionsProps, + AssignmentOptionsState +> { + constructor(props: AssignmentOptionsProps) { + super(props); + + this.state = { + isAssignmentPopoverVisible: false, + isActionPopoverVisible: false, + }; + } + + public render() { + const { + actionHandler, + assignmentOptions, + assignmentTitle, + controlDefinitions: { actions }, + selectionCount, + } = this.props; + const { isActionPopoverVisible, isAssignmentPopoverVisible } = this.state; + return ( + + {selectionCount} selected + + { + this.setState({ isActionPopoverVisible: false }); + }} + isPopoverVisible={isActionPopoverVisible} + showPopover={() => { + this.setState({ isActionPopoverVisible: true }); + }} + /> + + + { + this.setState({ + isAssignmentPopoverVisible: true, + }); + actionHandler('loadAssignmentOptions'); + }} + > + {assignmentTitle} + + } + closePopover={() => { + this.setState({ isAssignmentPopoverVisible: false }); + }} + id="assignmentList" + isOpen={isAssignmentPopoverVisible} + panelPaddingSize="s" + withTitle + > + {assignmentOptions ? ( + // @ts-ignore direction prop not available on current typing + + {assignmentOptions} + + ) : ( +
+ Loading +
+ )} +
+
+
+ ); + } +} diff --git a/x-pack/plugins/beats_management/public/components/table/controls.tsx b/x-pack/plugins/beats_management/public/components/table/controls.tsx index ab5fb655fe361..5182c10c71722 100644 --- a/x-pack/plugins/beats_management/public/components/table/controls.tsx +++ b/x-pack/plugins/beats_management/public/components/table/controls.tsx @@ -5,16 +5,11 @@ */ import { - EuiButton, - EuiContextMenu, - EuiFlexGroup, - EuiFlexItem, - EuiLoadingSpinner, - EuiPopover, - // @ts-ignore + // @ts-ignore typings for EuiSearchar not included in EUI EuiSearchBar, } from '@elastic/eui'; import React from 'react'; +import { AssignmentOptions } from './assignment_options'; import { ControlDefinitions } from './table_type_configs'; interface ControlBarProps { @@ -26,147 +21,28 @@ interface ControlBarProps { actionHandler(actionType: string, payload?: any): void; } -interface ControlBarState { - isPopoverVisible: boolean; - isAssignmentPopoverVisible: boolean; -} - -export class ControlBar extends React.Component { - constructor(props: ControlBarProps) { - super(props); - - this.state = { - isPopoverVisible: false, - isAssignmentPopoverVisible: false, - }; - } - - public render() { - const { selectionCount, showAssignmentOptions } = this.props; - return selectionCount !== 0 && showAssignmentOptions - ? this.renderAssignmentOptions() - : this.renderDefaultControls(); - } - - private renderAssignmentOptions = () => ( - - {this.props.selectionCount} selected - {this.renderActionButton()} - - { - this.showAssignmentPopover(); - this.props.actionHandler('loadAssignmentOptions'); - }} - > - {this.props.assignmentTitle} - - } - closePopover={this.hideAssignmentPopover} - id="assignmentList" - isOpen={this.state.isAssignmentPopoverVisible} - panelPaddingSize="s" - withTitle - > - {this.props.assignmentOptions ? ( - // @ts-ignore direction prop not available on current typing - - {this.props.assignmentOptions} - - ) : ( -
- Loading -
- )} -
-
-
- ); - - private renderDefaultControls = () => ( +export function ControlBar(props: ControlBarProps) { + const { + actionHandler, + assignmentOptions, + assignmentTitle, + controlDefinitions, + selectionCount, + showAssignmentOptions, + } = props; + return selectionCount !== 0 && showAssignmentOptions ? ( + + ) : ( this.props.actionHandler('search', query)} + filters={controlDefinitions.filters} + onChange={(query: any) => actionHandler('search', query)} /> ); - - private renderActionButton = () => { - const { - controlDefinitions: { actions }, - actionHandler, - } = this.props; - - if (actions.length === 0) { - return null; - } else if (actions.length === 1) { - const action = actions[0]; - return ( - actionHandler(action.action)} - > - {action.name} - - ); - } - return ( - - Bulk Action - - } - closePopover={this.hidePopover} - id="contextMenu" - isOpen={this.state.isPopoverVisible} - panelPaddingSize="none" - withTitle - > - ({ - ...action, - onClick: () => actionHandler(action.action), - })), - }, - ]} - /> - - ); - }; - - private hideAssignmentPopover = () => { - this.setState({ - isAssignmentPopoverVisible: false, - }); - }; - - private showAssignmentPopover = () => { - this.setState({ - isAssignmentPopoverVisible: true, - }); - }; - - private hidePopover = () => { - this.setState({ - isPopoverVisible: false, - }); - }; - - private showPopover = () => { - this.setState({ - isPopoverVisible: true, - }); - }; } diff --git a/x-pack/plugins/beats_management/public/components/table/table.tsx b/x-pack/plugins/beats_management/public/components/table/table.tsx index f38feb7ca2ec4..c66c290b2f5f9 100644 --- a/x-pack/plugins/beats_management/public/components/table/table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/table.tsx @@ -5,7 +5,7 @@ */ import { - // @ts-ignore + // @ts-ignore no typings for EuiInMemoryTable in EUI EuiInMemoryTable, EuiSpacer, } from '@elastic/eui'; From fe7441a213df6b4ccfa7d236829bf710422a29a5 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 6 Aug 2018 12:12:52 -0400 Subject: [PATCH 16/18] Add assignment options to Tags List. --- .../common/constants/table.ts | 1 + .../public/components/table/controls.tsx | 3 +- .../public/components/table/index.ts | 2 +- .../public/components/table/table.tsx | 12 +- .../components/table/table_type_configs.tsx | 43 +++++- .../adapters/beats/memory_beats_adapter.ts | 12 +- .../public/pages/main/beats.tsx | 3 +- .../public/pages/main/tags.tsx | 132 +++++++++++++++++- 8 files changed, 195 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/beats_management/common/constants/table.ts b/x-pack/plugins/beats_management/common/constants/table.ts index 801a60082d1b8..e5c2f6a029d67 100644 --- a/x-pack/plugins/beats_management/common/constants/table.ts +++ b/x-pack/plugins/beats_management/common/constants/table.ts @@ -7,4 +7,5 @@ export const TABLE_CONFIG = { INITIAL_ROW_SIZE: 5, PAGE_SIZE_OPTIONS: [3, 5, 10, 20], + TRUNCATE_TAG_LENGTH: 33, }; diff --git a/x-pack/plugins/beats_management/public/components/table/controls.tsx b/x-pack/plugins/beats_management/public/components/table/controls.tsx index 5182c10c71722..730c73a37d5e1 100644 --- a/x-pack/plugins/beats_management/public/components/table/controls.tsx +++ b/x-pack/plugins/beats_management/public/components/table/controls.tsx @@ -30,6 +30,7 @@ export function ControlBar(props: ControlBarProps) { selectionCount, showAssignmentOptions, } = props; + const filters = controlDefinitions.filters.length === 0 ? null : controlDefinitions.filters; return selectionCount !== 0 && showAssignmentOptions ? ( actionHandler('search', query)} /> ); diff --git a/x-pack/plugins/beats_management/public/components/table/index.ts b/x-pack/plugins/beats_management/public/components/table/index.ts index 0789cad5c3022..459002dd49b82 100644 --- a/x-pack/plugins/beats_management/public/components/table/index.ts +++ b/x-pack/plugins/beats_management/public/components/table/index.ts @@ -6,4 +6,4 @@ export { Table } from './table'; export { ControlBar } from './controls'; -export { BeatsTableType } from './table_type_configs'; +export { BeatsTableType, TagsTableType } from './table_type_configs'; diff --git a/x-pack/plugins/beats_management/public/components/table/table.tsx b/x-pack/plugins/beats_management/public/components/table/table.tsx index c66c290b2f5f9..dd84facd75704 100644 --- a/x-pack/plugins/beats_management/public/components/table/table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/table.tsx @@ -20,6 +20,7 @@ interface BeatsTableProps { assignmentOptions: any[] | null; assignmentTitle: string | null; items: any[]; + showAssignmentOptions: boolean; type: TableType; actionHandler(action: string, payload?: any): void; } @@ -42,7 +43,14 @@ export class Table extends React.Component { } public render() { - const { actionHandler, assignmentOptions, assignmentTitle, items, type } = this.props; + const { + actionHandler, + assignmentOptions, + assignmentTitle, + items, + showAssignmentOptions, + type, + } = this.props; const pagination = { initialPageSize: TABLE_CONFIG.INITIAL_ROW_SIZE, @@ -64,7 +72,7 @@ export class Table extends React.Component { assignmentTitle={assignmentTitle} controlDefinitions={type.controlDefinitions(items)} selectionCount={this.state.selection.length} - showAssignmentOptions={true} + showAssignmentOptions={showAssignmentOptions} /> ( + + {tag.id.length > TABLE_CONFIG.TRUNCATE_TAG_LENGTH + ? `${tag.id.substring(0, TABLE_CONFIG.TRUNCATE_TAG_LENGTH)}...` + : tag.id} + + ), + sortable: true, + width: '45%', + }, + { + align: 'right', + field: 'configuration_blocks', + name: 'Configurations', + render: (configurationBlocks: ConfigurationBlock[]) => ( +
{configurationBlocks.length}
+ ), + sortable: false, + }, + { + align: 'right', + field: 'last_updated', + name: 'Last update', + render: (lastUpdate: Date) =>
{moment(lastUpdate).fromNow()}
, + sortable: true, + }, + ], + controlDefinitions: (data: any) => ({ + actions: [], + filters: [], + }), +}; diff --git a/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts index 1940ec2ada4b0..eefdd77b7c619 100644 --- a/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts @@ -33,11 +33,13 @@ export class MemoryBeatsAdapter implements CMBeatsAdapter { const beatIds = removals.map(r => r.beatId); const response = this.beatsDB.filter(beat => beatIds.includes(beat.id)).map(beat => { - const tagData = removals.find(r => r.beatId === beat.id); - if (tagData) { - if (beat.tags) { - beat.tags = beat.tags.filter(tag => tag !== tagData.tag); - } + const tags = removals.filter(r => r.beatId === beat.id); + if (tags.length) { + tags.forEach((assignment: BeatsTagAssignment) => { + if (beat.tags) { + beat.tags = beat.tags.filter(tag => tag !== assignment.tag); + } + }); } return beat; }); diff --git a/x-pack/plugins/beats_management/public/pages/main/beats.tsx b/x-pack/plugins/beats_management/public/pages/main/beats.tsx index 1d8a9e6717605..1fa7784107fce 100644 --- a/x-pack/plugins/beats_management/public/pages/main/beats.tsx +++ b/x-pack/plugins/beats_management/public/pages/main/beats.tsx @@ -30,8 +30,8 @@ interface BeatsPageProps { interface BeatsPageState { beats: CMBeat[]; - tags: any[] | null; tableRef: any; + tags: any[] | null; } export class BeatsPage extends React.PureComponent { @@ -93,6 +93,7 @@ export class BeatsPage extends React.PureComponent ); diff --git a/x-pack/plugins/beats_management/public/pages/main/tags.tsx b/x-pack/plugins/beats_management/public/pages/main/tags.tsx index 66dab3c1b3550..329fbb22ade50 100644 --- a/x-pack/plugins/beats_management/public/pages/main/tags.tsx +++ b/x-pack/plugins/beats_management/public/pages/main/tags.tsx @@ -4,10 +4,138 @@ * you may not use this file except in compliance with the Elastic License. */ +// @ts-ignore EuiToolTip has no typings in current version +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiToolTip } from '@elastic/eui'; import React from 'react'; +import { BeatTag, CMBeat } from '../../../common/domain_types'; +import { BeatsTagAssignment } from '../../../server/lib/adapters/beats/adapter_types'; +import { Table, TagsTableType } from '../../components/table'; +import { FrontendLibs } from '../../lib/lib'; + +interface TagsPageProps { + libs: FrontendLibs; +} + +interface TagsPageState { + beats: any; + tableRef: any; + tags: BeatTag[]; +} + +export class TagsPage extends React.PureComponent { + constructor(props: TagsPageProps) { + super(props); + + this.state = { + beats: [], + tableRef: React.createRef(), + tags: [], + }; + + this.loadTags(); + } -export class TagsPage extends React.PureComponent { public render() { - return
tags table and stuff
; + return ( +
+ ); } + + private handleTagsAction = (action: string, payload: any) => { + switch (action) { + case 'loadAssignmentOptions': + this.loadBeats(); + break; + } + + this.loadTags(); + }; + + private async loadTags() { + const tags = await this.props.libs.tags.getAll(); + this.setState({ + tags, + }); + } + + private async loadBeats() { + const beats = await this.props.libs.beats.getAll(); + const selectedTags = this.getSelectedTags(); + const renderedBeats = beats.map((beat: CMBeat) => { + const tagsToRemove: BeatTag[] = []; + const tagsToAdd: BeatTag[] = []; + const tags = beat.tags || []; + selectedTags.forEach((tag: BeatTag) => { + tags.some((tagId: string) => tagId === tag.id) + ? tagsToRemove.push(tag) + : tagsToAdd.push(tag); + }); + + const tagIcons = tags.map((tagId: string) => { + const associatedTag = this.state.tags.find(tag => tag.id === tagId); + return ( + + + + ); + }); + + return ( + + + {tagIcons.map(icon => ( + + {icon} + + ))} + + { + this.assignTagsToBeats(beat, tagsToAdd); + this.removeTagsFromBeats(beat, tagsToRemove); + this.loadBeats(); + }} + > + {beat.id} + + + + + ); + }); + + this.setState({ + beats: renderedBeats, + }); + } + + private createBeatTagAssignments = (beat: CMBeat, tags: BeatTag[]): BeatsTagAssignment[] => + tags.map(({ id }) => ({ tag: id, beatId: beat.id })); + + private removeTagsFromBeats = async (beat: CMBeat, tags: BeatTag[]) => { + const assignments = this.createBeatTagAssignments(beat, tags); + await this.props.libs.beats.removeTagsFromBeats(assignments); + }; + + private assignTagsToBeats = async (beat: CMBeat, tags: BeatTag[]) => { + const assignments = this.createBeatTagAssignments(beat, tags); + await this.props.libs.beats.assignTagsToBeats(assignments); + }; + + private getSelectedTags = () => { + return this.state.tableRef.current.state.selection; + }; } From 2f343c5f31978e03dcb00212adb3c2bfec4d83bd Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 6 Aug 2018 12:24:52 -0400 Subject: [PATCH 17/18] Remove obsolete code. --- .../table/tag_action_control_bar.tsx | 44 ------ .../public/components/table/tags_table.tsx | 144 ------------------ .../adapters/beats/memory_beats_adapter.ts | 6 +- 3 files changed, 3 insertions(+), 191 deletions(-) delete mode 100644 x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx delete mode 100644 x-pack/plugins/beats_management/public/components/table/tags_table.tsx diff --git a/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx b/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx deleted file mode 100644 index 5c28974eca421..0000000000000 --- a/x-pack/plugins/beats_management/public/components/table/tag_action_control_bar.tsx +++ /dev/null @@ -1,44 +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 { - EuiButton, - EuiFlexGroup, - EuiFlexItem, - // @ts-ignore - EuiSearchBar, -} from '@elastic/eui'; -import React from 'react'; - -interface TagActionControlBarProps { - isDeleteDisabled: boolean; - onAddTag: any; - onDeleteSelected: any; - onSearchQueryChange: any; -} - -export const TagActionControlBar: React.SFC = ({ - isDeleteDisabled, - onAddTag, - onDeleteSelected, - onSearchQueryChange, -}) => { - return ( - - - Add Tag - - - - Delete Tag - - - - - - - ); -}; diff --git a/x-pack/plugins/beats_management/public/components/table/tags_table.tsx b/x-pack/plugins/beats_management/public/components/table/tags_table.tsx deleted file mode 100644 index f61a991ed359d..0000000000000 --- a/x-pack/plugins/beats_management/public/components/table/tags_table.tsx +++ /dev/null @@ -1,144 +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 { - // @ts-ignore - EuiBadge, - // @ts-ignore - EuiInMemoryTable, - EuiSpacer, -} from '@elastic/eui'; -import moment from 'moment'; -import React from 'react'; -import styled from 'styled-components'; -import { TABLE_CONFIG } from '../../../common/constants'; -import { BeatTag, ConfigurationBlock } from '../../../common/domain_types'; -import { TagActionControlBar } from './tag_action_control_bar'; - -interface TagsTableProps { - onAddTag: any; - onDeleteTags: any; - tags: BeatTag[]; -} - -interface TagsTableState { - selection: BeatTag[]; - search: any; -} - -const truncateText = (text: string) => - text.length > TABLE_CONFIG.TRUNCATE_TAG_LENGTH - ? `${text.substring(0, TABLE_CONFIG.TRUNCATE_TAG_LENGTH)}...` - : text; - -const columns = [ - { - field: 'id', - name: 'Tag name', - render: (id: string, tag: BeatTag) => ( - {truncateText(tag.id)} - ), - sortable: true, - width: '70%', - }, - { - align: 'right', - field: 'configuration_blocks', - name: 'Configurations', - render: (configurationBlocks: ConfigurationBlock[]) =>
{configurationBlocks.length}
, - sortable: false, - }, - { - align: 'right', - field: 'last_updated', - name: 'Last update', - render: (lastUpdate: Date) =>
{moment(lastUpdate).fromNow()}
, - sortable: true, - }, -]; - -const TableContainer = styled.div` - padding: 16px; -`; - -export class TagsTable extends React.Component { - constructor(props: TagsTableProps) { - super(props); - - this.state = { - selection: [], - search: null, - }; - } - - public render() { - const { onAddTag } = this.props; - const { selection } = this.state; - - const pagination = { - initialPageSize: TABLE_CONFIG.INITIAL_ROW_SIZE, - pageSizeOptions: TABLE_CONFIG.PAGE_SIZE_OPTIONS, - }; - - const selectionOptions = { - onSelectionChange: this.setSelection, - selectable: () => true, - selectableMessage: () => 'Select this tag', - }; - - return ( - - - - - - ); - } - - private getTagsToRender() { - const { search } = this.state; - let tagsToRender = this.props.tags; - if (search && !search.error && search.query.ast.getTermClauses().length) { - const { ast } = search.query; - const terms = ast.getTermClauses().map((clause: any) => clause.value); - tagsToRender = tagsToRender.filter(tag => - terms.some((term: string) => tag.id.toLowerCase().includes(term.toLowerCase())) - ); - } - return tagsToRender; - } - - private setSelection = (selection: any) => { - this.setState({ - selection, - }); - }; - - private onDeleteSelected = () => { - const { selection } = this.state; - const { onDeleteTags } = this.props; - onDeleteTags(selection); - }; - - private onSearchQueryChange = (search: any) => { - this.setState({ - search, - }); - }; -} diff --git a/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts index eefdd77b7c619..9606835337af3 100644 --- a/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts @@ -33,9 +33,9 @@ export class MemoryBeatsAdapter implements CMBeatsAdapter { const beatIds = removals.map(r => r.beatId); const response = this.beatsDB.filter(beat => beatIds.includes(beat.id)).map(beat => { - const tags = removals.filter(r => r.beatId === beat.id); - if (tags.length) { - tags.forEach((assignment: BeatsTagAssignment) => { + const removalsForBeat = removals.filter(r => r.beatId === beat.id); + if (removalsForBeat.length) { + removalsForBeat.forEach((assignment: BeatsTagAssignment) => { if (beat.tags) { beat.tags = beat.tags.filter(tag => tag !== assignment.tag); } From 01afe66702fc02bedd58d411e97ede465df2eb8b Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 6 Aug 2018 12:32:55 -0400 Subject: [PATCH 18/18] Move tooltips for tag icons to top position. --- x-pack/plugins/beats_management/public/pages/main/tags.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/beats_management/public/pages/main/tags.tsx b/x-pack/plugins/beats_management/public/pages/main/tags.tsx index 329fbb22ade50..d7ed848fd10c3 100644 --- a/x-pack/plugins/beats_management/public/pages/main/tags.tsx +++ b/x-pack/plugins/beats_management/public/pages/main/tags.tsx @@ -82,7 +82,7 @@ export class TagsPage extends React.PureComponent const tagIcons = tags.map((tagId: string) => { const associatedTag = this.state.tags.find(tag => tag.id === tagId); return ( - +