diff --git a/.ci/jobs.yml b/.ci/jobs.yml index 3f1b5302f87b01..fe28ae79268de6 100644 --- a/.ci/jobs.yml +++ b/.ci/jobs.yml @@ -1,34 +1,21 @@ JOB: - - kibana-intake - - x-pack-intake - - kibana-firefoxSmoke + - intake + - firefoxSmoke - kibana-ciGroup1 - kibana-ciGroup2 - kibana-ciGroup3 - kibana-ciGroup4 - kibana-ciGroup5 - kibana-ciGroup6 - - kibana-ciGroup7 - - kibana-ciGroup8 - - kibana-ciGroup9 - - kibana-ciGroup10 - - kibana-ciGroup11 - - kibana-ciGroup12 - - kibana-visualRegression + # - kibana-visualRegression # make sure all x-pack-ciGroups are listed in test/scripts/jenkins_xpack_ci_group.sh - - x-pack-firefoxSmoke - x-pack-ciGroup1 - x-pack-ciGroup2 - x-pack-ciGroup3 - x-pack-ciGroup4 - x-pack-ciGroup5 - - x-pack-ciGroup6 - - x-pack-ciGroup7 - - x-pack-ciGroup8 - - x-pack-ciGroup9 - - x-pack-ciGroup10 - - x-pack-visualRegression + # - x-pack-visualRegression # `~` is yaml for `null` exclude: ~ \ No newline at end of file diff --git a/.ci/run.sh b/.ci/run.sh index 88ce0bd9986a19..e5c26c48546f7f 100755 --- a/.ci/run.sh +++ b/.ci/run.sh @@ -11,7 +11,7 @@ source src/dev/ci_setup/setup.sh source src/dev/ci_setup/checkout_sibling_es.sh case "$JOB" in -kibana-intake) +intake) ./test/scripts/jenkins_unit.sh ;; kibana-ciGroup*) @@ -21,12 +21,9 @@ kibana-ciGroup*) kibana-visualRegression*) ./test/scripts/jenkins_visual_regression.sh ;; -kibana-firefoxSmoke*) +firefoxSmoke*) ./test/scripts/jenkins_firefox_smoke.sh ;; -x-pack-intake) - ./test/scripts/jenkins_xpack.sh - ;; x-pack-ciGroup*) export CI_GROUP="${JOB##x-pack-ciGroup}" ./test/scripts/jenkins_xpack_ci_group.sh @@ -34,9 +31,6 @@ x-pack-ciGroup*) x-pack-visualRegression*) ./test/scripts/jenkins_xpack_visual_regression.sh ;; -x-pack-firefoxSmoke*) - ./test/scripts/jenkins_xpack_firefox_smoke.sh - ;; *) echo "JOB '$JOB' is not implemented." exit 1 diff --git a/docs/development/add-data-guide.asciidoc b/docs/developer/add-data-guide.asciidoc similarity index 100% rename from docs/development/add-data-guide.asciidoc rename to docs/developer/add-data-guide.asciidoc diff --git a/docs/development/core-development.asciidoc b/docs/developer/core-development.asciidoc similarity index 100% rename from docs/development/core-development.asciidoc rename to docs/developer/core-development.asciidoc diff --git a/docs/development/core/development-basepath.asciidoc b/docs/developer/core/development-basepath.asciidoc similarity index 100% rename from docs/development/core/development-basepath.asciidoc rename to docs/developer/core/development-basepath.asciidoc diff --git a/docs/development/core/development-dependencies.asciidoc b/docs/developer/core/development-dependencies.asciidoc similarity index 100% rename from docs/development/core/development-dependencies.asciidoc rename to docs/developer/core/development-dependencies.asciidoc diff --git a/docs/development/core/development-elasticsearch.asciidoc b/docs/developer/core/development-elasticsearch.asciidoc similarity index 100% rename from docs/development/core/development-elasticsearch.asciidoc rename to docs/developer/core/development-elasticsearch.asciidoc diff --git a/docs/development/core/development-functional-tests.asciidoc b/docs/developer/core/development-functional-tests.asciidoc similarity index 100% rename from docs/development/core/development-functional-tests.asciidoc rename to docs/developer/core/development-functional-tests.asciidoc diff --git a/docs/development/core/development-modules.asciidoc b/docs/developer/core/development-modules.asciidoc similarity index 100% rename from docs/development/core/development-modules.asciidoc rename to docs/developer/core/development-modules.asciidoc diff --git a/docs/development/core/development-unit-tests.asciidoc b/docs/developer/core/development-unit-tests.asciidoc similarity index 100% rename from docs/development/core/development-unit-tests.asciidoc rename to docs/developer/core/development-unit-tests.asciidoc diff --git a/docs/development/index.asciidoc b/docs/developer/index.asciidoc similarity index 100% rename from docs/development/index.asciidoc rename to docs/developer/index.asciidoc diff --git a/docs/development/plugin-development.asciidoc b/docs/developer/plugin-development.asciidoc similarity index 100% rename from docs/development/plugin-development.asciidoc rename to docs/developer/plugin-development.asciidoc diff --git a/docs/development/plugin/development-plugin-feature-registration.asciidoc b/docs/developer/plugin/development-plugin-feature-registration.asciidoc similarity index 100% rename from docs/development/plugin/development-plugin-feature-registration.asciidoc rename to docs/developer/plugin/development-plugin-feature-registration.asciidoc diff --git a/docs/development/plugin/development-plugin-functional-tests.asciidoc b/docs/developer/plugin/development-plugin-functional-tests.asciidoc similarity index 100% rename from docs/development/plugin/development-plugin-functional-tests.asciidoc rename to docs/developer/plugin/development-plugin-functional-tests.asciidoc diff --git a/docs/development/plugin/development-plugin-localization.asciidoc b/docs/developer/plugin/development-plugin-localization.asciidoc similarity index 100% rename from docs/development/plugin/development-plugin-localization.asciidoc rename to docs/developer/plugin/development-plugin-localization.asciidoc diff --git a/docs/development/plugin/development-plugin-resources.asciidoc b/docs/developer/plugin/development-plugin-resources.asciidoc similarity index 100% rename from docs/development/plugin/development-plugin-resources.asciidoc rename to docs/developer/plugin/development-plugin-resources.asciidoc diff --git a/docs/development/plugin/development-uiexports.asciidoc b/docs/developer/plugin/development-uiexports.asciidoc similarity index 100% rename from docs/development/plugin/development-uiexports.asciidoc rename to docs/developer/plugin/development-uiexports.asciidoc diff --git a/docs/development/pr-review.asciidoc b/docs/developer/pr-review.asciidoc similarity index 100% rename from docs/development/pr-review.asciidoc rename to docs/developer/pr-review.asciidoc diff --git a/docs/development/security/index.asciidoc b/docs/developer/security/index.asciidoc similarity index 100% rename from docs/development/security/index.asciidoc rename to docs/developer/security/index.asciidoc diff --git a/docs/development/security/rbac.asciidoc b/docs/developer/security/rbac.asciidoc similarity index 100% rename from docs/development/security/rbac.asciidoc rename to docs/developer/security/rbac.asciidoc diff --git a/docs/development/visualize/development-create-visualization.asciidoc b/docs/developer/visualize/development-create-visualization.asciidoc similarity index 100% rename from docs/development/visualize/development-create-visualization.asciidoc rename to docs/developer/visualize/development-create-visualization.asciidoc diff --git a/docs/development/visualize/development-embedding-visualizations.asciidoc b/docs/developer/visualize/development-embedding-visualizations.asciidoc similarity index 100% rename from docs/development/visualize/development-embedding-visualizations.asciidoc rename to docs/developer/visualize/development-embedding-visualizations.asciidoc diff --git a/docs/development/visualize/development-visualize-index.asciidoc b/docs/developer/visualize/development-visualize-index.asciidoc similarity index 100% rename from docs/development/visualize/development-visualize-index.asciidoc rename to docs/developer/visualize/development-visualize-index.asciidoc diff --git a/docs/development.asciidoc b/docs/development.asciidoc deleted file mode 100644 index 55c2beff074d5c..00000000000000 --- a/docs/development.asciidoc +++ /dev/null @@ -1,25 +0,0 @@ -[[development]] -= Contributing to Kibana - -[partintro] --- -Contributing to Kibana can be daunting at first, but it doesn't have to be. If -you're planning a pull request to the Kibana repository, you may want to start -with <>. - -If you'd prefer to use Kibana's internal plugin API, then check out -<>. --- - -include::development/core-development.asciidoc[] - -include::development/plugin-development.asciidoc[] - -include::development/visualize/development-visualize-index.asciidoc[] - -include::development/add-data-guide.asciidoc[] - -include::development/security/index.asciidoc[] - -include::development/pr-review.asciidoc[] - diff --git a/docs/index.asciidoc b/docs/index.asciidoc index a4972c0aa21d2c..52dffb696554f2 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -84,6 +84,6 @@ include::migration.asciidoc[] include::CHANGELOG.asciidoc[] -include::development/index.asciidoc[] +include::developer/index.asciidoc[] include::redirects.asciidoc[] diff --git a/docs/maps/images/create_phrase_filter.png b/docs/maps/images/create_phrase_filter.png new file mode 100644 index 00000000000000..720aecf44d9faf Binary files /dev/null and b/docs/maps/images/create_phrase_filter.png differ diff --git a/docs/maps/images/create_spatial_filter.png b/docs/maps/images/create_spatial_filter.png new file mode 100644 index 00000000000000..abb52bd0b5b0a6 Binary files /dev/null and b/docs/maps/images/create_spatial_filter.png differ diff --git a/docs/maps/images/filter_icon.png b/docs/maps/images/filter_icon.png new file mode 100644 index 00000000000000..08fd9c6b10a32b Binary files /dev/null and b/docs/maps/images/filter_icon.png differ diff --git a/docs/maps/images/global_search_bar.png b/docs/maps/images/global_search_bar.png index 42445f82db65ad..c1bb1e7835e365 100644 Binary files a/docs/maps/images/global_search_bar.png and b/docs/maps/images/global_search_bar.png differ diff --git a/docs/maps/images/global_search_multiple_indices_query1.png b/docs/maps/images/global_search_multiple_indices_query1.png index 7007db3fa0a806..f019f2c4e39d64 100644 Binary files a/docs/maps/images/global_search_multiple_indices_query1.png and b/docs/maps/images/global_search_multiple_indices_query1.png differ diff --git a/docs/maps/images/global_search_multiple_indices_query2.png b/docs/maps/images/global_search_multiple_indices_query2.png index 2a97311114feb7..51017039178f19 100644 Binary files a/docs/maps/images/global_search_multiple_indices_query2.png and b/docs/maps/images/global_search_multiple_indices_query2.png differ diff --git a/docs/maps/images/tools_icon.png b/docs/maps/images/tools_icon.png new file mode 100644 index 00000000000000..677b16eb8e484d Binary files /dev/null and b/docs/maps/images/tools_icon.png differ diff --git a/docs/maps/search.asciidoc b/docs/maps/search.asciidoc index 3830ecdcbc1f00..98b8c6ae59c753 100644 --- a/docs/maps/search.asciidoc +++ b/docs/maps/search.asciidoc @@ -2,8 +2,9 @@ [[maps-search]] == Searching your data -**Elastic Maps** embeds the query bar for real-time ad hoc search. +**Elastic Maps** embeds the search bar for real-time search. Only layers requesting data from {es} are filtered when you submit a search request. +Layers narrowed by the search context contain the filter icon image:maps/images/filter_icon.png[] to the right of layer name in the legend. You can create a layer that requests data from {es} from the following: @@ -13,13 +14,51 @@ You can create a layer that requests data from {es} from the following: ** Grid aggregation source -** <> +** <>. The search context is applied to both the terms join and the vector source when the vector source is provided by Elasticsearch documents. * <> with Grid aggregation source [role="screenshot"] image::maps/images/global_search_bar.png[] +[role="xpack"] +[[maps-create-filter-from-map]] +=== Creating filters from your map + +You can create two types of filters by interacting with your map: + +* <> +* <> + +[float] +[[maps-spatial-filters]] +==== Spatial filters + +A spatial filter narrow searchs results to documents that either intersect with, are within, or do not intersect with the specified geometry. + +You can create spatial filters in two ways: + +* Click the tool icon image:maps/images/tools_icon.png[], and then draw a polygon or bounding box on the map to define the spatial filter. +* Click *Filter by geometry* in a tooltip, and then use the feature's geometry for the spatial filter. ++ +[role="screenshot"] +image::maps/images/create_spatial_filter.png[] + +Spatial filters have the following properties: + +* *Geometry label* enables you to provide a meaningful name for your spatial filter. +* *Spatial field* specifies the geo_point or geo_shape field used to determine if a document matches the spatial relation with the specified geometry. +* *Spatial relation* determines the {ref}/query-dsl-geo-shape-query.html#_spatial_relations[spatial relation operator] to use at search time. Only available when *Spatial field* is set to geo_shape. + +[float] +[[maps-phrase-filter]] +==== Phrase filters + +A phrase filter narrows search results to documents that contain the specified text. +You can create a phrase filter by clicking the plus icon image:maps/images/gs_plus_icon.png[] in a feature tooltip. + +[role="screenshot"] +image::maps/images/create_phrase_filter.png[] [role="xpack"] [[maps-layer-based-filtering]] @@ -43,6 +82,18 @@ This can also occur with a single layer with an {es} source and a <> Searching across multiple indices might sometimes result in empty layers. The most common cause for empty layers are searches for a field that exists in one index, but does not exist in other indices. + +[float] +[[maps-disable-search-for-layer]] +==== Disable search for layer + +To prevent the global search bar from applying search context to a layer, clear the *Apply global filter to layer* checkbox in Layer settings. +Disabling the search context applies to the layer source and all <> configured for the layer. + +[float] +[[maps-add-index-search]] +==== Use _index in your search + Add {ref}/mapping-index-field.html[_index] to your search to include documents from indices that do not contain a search field. For example, suppose you have a vector layer showing the `kibana_sample_data_logs` documents diff --git a/src/cli/cluster/cluster_manager.js b/src/cli/cluster/cluster_manager.js index 342f838e4ee7ad..bd32c5b9803a33 100644 --- a/src/cli/cluster/cluster_manager.js +++ b/src/cli/cluster/cluster_manager.js @@ -24,6 +24,7 @@ import opn from 'opn'; import { debounce, invoke, bindAll, once, uniq } from 'lodash'; import * as Rx from 'rxjs'; import { first, mapTo, filter, map, take } from 'rxjs/operators'; +import { REPO_ROOT } from '@kbn/dev-utils'; import Log from '../log'; import Worker from './worker'; @@ -102,8 +103,15 @@ export default class ClusterManager { if (opts.watch) { const pluginPaths = config.get('plugins.paths'); - const scanDirs = config.get('plugins.scanDirs'); - const extraPaths = [...pluginPaths, ...scanDirs]; + const scanDirs = [ + ...config.get('plugins.scanDirs'), + resolve(REPO_ROOT, 'src/plugins'), + resolve(REPO_ROOT, 'x-pack/plugins'), + ]; + const extraPaths = [ + ...pluginPaths, + ...scanDirs, + ]; const extraIgnores = scanDirs .map(scanDir => resolve(scanDir, '*')) diff --git a/src/legacy/core_plugins/data/public/index_patterns/components/index_pattern_select.tsx b/src/legacy/core_plugins/data/public/index_patterns/components/index_pattern_select.tsx index 62d4983bfbf033..c6f3a6e35bc436 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/components/index_pattern_select.tsx +++ b/src/legacy/core_plugins/data/public/index_patterns/components/index_pattern_select.tsx @@ -67,7 +67,9 @@ const getIndexPatternTitle = async ( // Takes in stateful runtime dependencies and pre-wires them to the component export function createIndexPatternSelect(savedObjectsClient: SavedObjectsClientContract) { - return (props: any) => ; + return (props: Omit) => ( + + ); } export class IndexPatternSelect extends Component { diff --git a/src/legacy/core_plugins/kibana/public/context/NOTES.md b/src/legacy/core_plugins/kibana/public/context/NOTES.md index 07f6533403c4ca..445080c2159982 100644 --- a/src/legacy/core_plugins/kibana/public/context/NOTES.md +++ b/src/legacy/core_plugins/kibana/public/context/NOTES.md @@ -81,11 +81,9 @@ query status and results. **query_parameters**: Exports the actions, reducers and selectors related to the parameters used to construct the query. -**components/loading_button**: Defines the `` +**components/action_bar**: Defines the `` directive including its respective styles. -**components/size_picker**: Defines the `` -directive including its respective styles. **api/anchor.js**: Exports `fetchAnchor()` that creates and executes the query for the anchor document. diff --git a/src/legacy/core_plugins/kibana/public/context/_index.scss b/src/legacy/core_plugins/kibana/public/context/_index.scss index b605bc3d6edeac..49293fa1b8ad88 100644 --- a/src/legacy/core_plugins/kibana/public/context/_index.scss +++ b/src/legacy/core_plugins/kibana/public/context/_index.scss @@ -5,4 +5,4 @@ // cxtChart__legend--small // cxtChart__legend-isLoading -@import 'components/size_picker/index'; +@import 'components/action_bar/index'; diff --git a/src/legacy/core_plugins/kibana/public/context/app.html b/src/legacy/core_plugins/kibana/public/context/app.html index 997c167920f716..68e1d536a91ce2 100644 --- a/src/legacy/core_plugins/kibana/public/context/app.html +++ b/src/legacy/core_plugins/kibana/public/context/app.html @@ -62,48 +62,19 @@ ng-if="contextApp.state.loadingStatus.anchor.status !== contextApp.constants.LOADING_STATUS.FAILED" > -
-
- - - - - - - - - -
+ type="'predecessors'" + > -
-
-
-
-
- - - - -
- - - - -
- -
-
-
+ diff --git a/src/legacy/core_plugins/kibana/public/context/app.js b/src/legacy/core_plugins/kibana/public/context/app.js index 346d51398e8ab5..6b10d80078f804 100644 --- a/src/legacy/core_plugins/kibana/public/context/app.js +++ b/src/legacy/core_plugins/kibana/public/context/app.js @@ -22,8 +22,7 @@ import _ from 'lodash'; import { callAfterBindingsWorkaround } from 'ui/compat'; import { uiModules } from 'ui/modules'; import contextAppTemplate from './app.html'; -import './components/loading_button'; -import './components/size_picker/size_picker'; +import './components/action_bar'; import { getFirstSortableField } from './api/utils/sorting'; import { createInitialQueryParametersState, diff --git a/src/legacy/core_plugins/kibana/public/context/components/action_bar/_action_bar.scss b/src/legacy/core_plugins/kibana/public/context/components/action_bar/_action_bar.scss new file mode 100644 index 00000000000000..da0911c3a452b9 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/context/components/action_bar/_action_bar.scss @@ -0,0 +1,10 @@ +.cxtSizePicker { + text-align: center; + width: $euiSize * 5; + + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + appearance: none; // Hide increment and decrement buttons for type="number" input. + margin: 0; + } +} diff --git a/src/legacy/core_plugins/kibana/public/context/components/action_bar/_index.scss b/src/legacy/core_plugins/kibana/public/context/components/action_bar/_index.scss new file mode 100644 index 00000000000000..1f54ecea5e1cb8 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/context/components/action_bar/_index.scss @@ -0,0 +1 @@ +@import './action_bar'; diff --git a/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar.test.tsx b/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar.test.tsx new file mode 100644 index 00000000000000..325cfb2c9f0bb0 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar.test.tsx @@ -0,0 +1,94 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { ActionBar, ActionBarProps } from './action_bar'; +// @ts-ignore +import { findTestSubject } from '@elastic/eui/lib/test'; +import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from '../../query_parameters/constants'; + +describe('Test Discover Context ActionBar for successor | predecessor records', () => { + ['successors', 'predecessors'].forEach(type => { + const onChangeCount = jest.fn(); + const props = { + defaultStepSize: 5, + docCount: 20, + docCountAvailable: 0, + isDisabled: false, + isLoading: false, + onChangeCount, + type, + } as ActionBarProps; + const wrapper = mountWithIntl(); + + const input = findTestSubject(wrapper, `${type}CountPicker`); + const btn = findTestSubject(wrapper, `${type}LoadMoreButton`); + + test(`${type}: Load button click`, () => { + btn.simulate('click'); + expect(onChangeCount).toHaveBeenCalledWith(25); + }); + + test(`${type}: Load button click doesnt submit when MAX_CONTEXT_SIZE was reached`, () => { + onChangeCount.mockClear(); + input.simulate('change', { target: { valueAsNumber: MAX_CONTEXT_SIZE } }); + btn.simulate('click'); + expect(onChangeCount).toHaveBeenCalledTimes(0); + }); + + test(`${type}: Count input change submits on blur`, () => { + input.simulate('change', { target: { valueAsNumber: 123 } }); + input.simulate('blur'); + expect(onChangeCount).toHaveBeenCalledWith(123); + }); + + test(`${type}: Count input change submits on return`, () => { + input.simulate('change', { target: { valueAsNumber: 124 } }); + input.simulate('submit'); + expect(onChangeCount).toHaveBeenCalledWith(124); + }); + + test(`${type}: Count input doesnt submits values higher than MAX_CONTEXT_SIZE `, () => { + onChangeCount.mockClear(); + input.simulate('change', { target: { valueAsNumber: MAX_CONTEXT_SIZE + 1 } }); + input.simulate('submit'); + expect(onChangeCount).toHaveBeenCalledTimes(0); + }); + + test(`${type}: Count input doesnt submits values lower than MIN_CONTEXT_SIZE `, () => { + onChangeCount.mockClear(); + input.simulate('change', { target: { valueAsNumber: MIN_CONTEXT_SIZE - 1 } }); + input.simulate('submit'); + expect(onChangeCount).toHaveBeenCalledTimes(0); + }); + + test(`${type}: Warning about limitation of additional records`, () => { + if (type === 'predecessors') { + expect(findTestSubject(wrapper, 'predecessorsWarningMsg').text()).toBe( + 'No documents newer than the anchor could be found.' + ); + } else { + expect(findTestSubject(wrapper, 'successorsWarningMsg').text()).toBe( + 'No documents older than the anchor could be found.' + ); + } + }); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar.tsx b/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar.tsx new file mode 100644 index 00000000000000..57ad8e0b1040f3 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar.tsx @@ -0,0 +1,164 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiButtonEmpty, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiSpacer, +} from '@elastic/eui'; +import { ActionBarWarning } from './action_bar_warning'; +import { SurrDocType } from '../../api/context'; +import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from '../../query_parameters/constants'; + +export interface ActionBarProps { + /** + * the number of documents fetched initially and added when the load button is clicked + */ + defaultStepSize: number; + /** + * the number of docs to be displayed + */ + docCount: number; + /** + * the number of documents that are available + * display warning when it's lower than docCount + */ + docCountAvailable: number; + /** + * is true while the anchor record is fetched + */ + isDisabled: boolean; + /** + * is true when list entries are fetched + */ + isLoading: boolean; + /** + * is triggered when the input containing count is changed + * @param count + */ + onChangeCount: (count: number) => void; + /** + * can be `predecessors` or `successors`, usage in context: + * predecessors action bar + records (these are newer records) + * anchor record + * successors records + action bar (these are older records) + */ + type: SurrDocType; +} + +export function ActionBar({ + defaultStepSize, + docCount, + docCountAvailable, + isDisabled, + isLoading, + onChangeCount, + type, +}: ActionBarProps) { + const showWarning = !isDisabled && !isLoading && docCountAvailable < docCount; + const isSuccessor = type === 'successors'; + const [newDocCount, setNewDocCount] = useState(docCount); + const isValid = (value: number) => value >= MIN_CONTEXT_SIZE && value <= MAX_CONTEXT_SIZE; + const onSubmit = (ev: React.FormEvent) => { + ev.preventDefault(); + if (newDocCount !== docCount && isValid(newDocCount)) { + onChangeCount(newDocCount); + } + }; + + return ( +
+ {isSuccessor && } + {isSuccessor && showWarning && } + {isSuccessor && showWarning && } + + + { + const value = newDocCount + defaultStepSize; + if (isValid(value)) { + setNewDocCount(value); + onChangeCount(value); + } + }} + flush="right" + > + + + + + + { + setNewDocCount(ev.target.valueAsNumber); + }} + onBlur={() => { + if (newDocCount !== docCount && isValid(newDocCount)) { + onChangeCount(newDocCount); + } + }} + type="number" + value={newDocCount >= 0 ? newDocCount : ''} + /> + + + + + {isSuccessor ? ( + + ) : ( + + )} + + + + {!isSuccessor && showWarning && } + {!isSuccessor && } + + ); +} diff --git a/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar_directive.ts b/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar_directive.ts new file mode 100644 index 00000000000000..0942539e63785a --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar_directive.ts @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +// @ts-ignore +import { uiModules } from 'ui/modules'; +import { wrapInI18nContext } from 'ui/i18n'; +import { ActionBar } from './action_bar'; + +uiModules.get('apps/context').directive('contextActionBar', function(reactDirective: any) { + return reactDirective(wrapInI18nContext(ActionBar)); +}); diff --git a/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar_warning.tsx b/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar_warning.tsx new file mode 100644 index 00000000000000..6b922bb05a2434 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/context/components/action_bar/action_bar_warning.tsx @@ -0,0 +1,72 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiCallOut } from '@elastic/eui'; +import { SurrDocType } from '../../api/context'; + +export function ActionBarWarning({ docCount, type }: { docCount: number; type: SurrDocType }) { + if (type === 'predecessors') { + return ( + + ) : ( + + ) + } + size="s" + /> + ); + } + + return ( + + ) : ( + + ) + } + size="s" + /> + ); +} diff --git a/src/legacy/core_plugins/kibana/public/context/components/loading_button/index.js b/src/legacy/core_plugins/kibana/public/context/components/action_bar/index.ts similarity index 96% rename from src/legacy/core_plugins/kibana/public/context/components/loading_button/index.js rename to src/legacy/core_plugins/kibana/public/context/components/action_bar/index.ts index b9cdff8e26a5db..de16caef9c773c 100644 --- a/src/legacy/core_plugins/kibana/public/context/components/loading_button/index.js +++ b/src/legacy/core_plugins/kibana/public/context/components/action_bar/index.ts @@ -17,4 +17,4 @@ * under the License. */ -import './loading_button'; +import './action_bar_directive'; diff --git a/src/legacy/core_plugins/kibana/public/context/components/loading_button/loading_button.html b/src/legacy/core_plugins/kibana/public/context/components/loading_button/loading_button.html deleted file mode 100644 index b994ede80dbed4..00000000000000 --- a/src/legacy/core_plugins/kibana/public/context/components/loading_button/loading_button.html +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/src/legacy/core_plugins/kibana/public/context/components/loading_button/loading_button.js b/src/legacy/core_plugins/kibana/public/context/components/loading_button/loading_button.js deleted file mode 100644 index 62effb3536a7a0..00000000000000 --- a/src/legacy/core_plugins/kibana/public/context/components/loading_button/loading_button.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uiModules } from 'ui/modules'; -import contextLoadingButtonTemplate from './loading_button.html'; - - -const module = uiModules.get('apps/context', [ - 'kibana', - 'ngRoute', -]); - -module.directive('contextLoadingButton', function ContextLoadingButton() { - return { - replace: true, - restrict: 'E', - scope: { - isDisabled: '=', - icon: '=', - }, - template: contextLoadingButtonTemplate, - transclude: true, - }; -}); diff --git a/src/legacy/core_plugins/kibana/public/context/components/size_picker/_index.scss b/src/legacy/core_plugins/kibana/public/context/components/size_picker/_index.scss deleted file mode 100644 index 6a7ffa72019196..00000000000000 --- a/src/legacy/core_plugins/kibana/public/context/components/size_picker/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './size_picker'; diff --git a/src/legacy/core_plugins/kibana/public/context/components/size_picker/_size_picker.scss b/src/legacy/core_plugins/kibana/public/context/components/size_picker/_size_picker.scss deleted file mode 100644 index 3e9b05a2872ca1..00000000000000 --- a/src/legacy/core_plugins/kibana/public/context/components/size_picker/_size_picker.scss +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 1. Hide increment and decrement buttons for type="number" input. - */ -.cxtSizePicker { - appearance: textfield; - text-align: center; - width: $euiSize * 5; - - &::-webkit-outer-spin-button, - &::-webkit-inner-spin-button { - appearance: none; /* 1 */ - margin: 0; /* 1 */ - } -} diff --git a/src/legacy/core_plugins/kibana/public/context/components/size_picker/size_picker.html b/src/legacy/core_plugins/kibana/public/context/components/size_picker/size_picker.html deleted file mode 100644 index d3bda3268310a8..00000000000000 --- a/src/legacy/core_plugins/kibana/public/context/components/size_picker/size_picker.html +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/src/legacy/core_plugins/kibana/public/context/components/size_picker/size_picker.js b/src/legacy/core_plugins/kibana/public/context/components/size_picker/size_picker.js deleted file mode 100644 index c6679b26f4ea39..00000000000000 --- a/src/legacy/core_plugins/kibana/public/context/components/size_picker/size_picker.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { uiModules } from 'ui/modules'; -import { callAfterBindingsWorkaround } from 'ui/compat'; -import contextSizePickerTemplate from './size_picker.html'; - -const module = uiModules.get('apps/context', [ - 'kibana', -]); - -module.directive('contextSizePicker', function ContextSizePicker() { - return { - bindToController: true, - controller: callAfterBindingsWorkaround(ContextSizePickerController), - controllerAs: 'contextSizePicker', - link: linkContextSizePicker, - replace: true, - restrict: 'E', - require: 'ngModel', - scope: { - count: '=', - isDisabled: '=', - onChangeCount: '=', // To avoid inconsistent ngModel states this action - // should make sure the new value is propagated back - // to the `count` property. If that propagation - // fails, the user input will be reset to the value - // of `count`. - }, - template: contextSizePickerTemplate, - }; -}); - -function linkContextSizePicker(scope, element, attrs, ngModel) { - scope.countModel = ngModel; -} - -function ContextSizePickerController($scope) { - $scope.$watch( - () => this.count, - () => $scope.countModel.$rollbackViewValue(), - ); - - this.getOrSetCount = (count) => ( - _.isUndefined(count) ? this.count : this.onChangeCount(count) - ); -} diff --git a/src/legacy/core_plugins/kibana/public/context/query/actions.js b/src/legacy/core_plugins/kibana/public/context/query/actions.js index 10a4812f85f4fb..72624210fcb497 100644 --- a/src/legacy/core_plugins/kibana/public/context/query/actions.js +++ b/src/legacy/core_plugins/kibana/public/context/query/actions.js @@ -32,8 +32,6 @@ export function QueryActionsProvider(Private, Promise) { const fetchAnchor = Private(fetchAnchorProvider); const { fetchSurroundingDocs } = Private(fetchContextProvider); const { - increasePredecessorCount, - increaseSuccessorCount, setPredecessorCount, setQueryParameters, setSuccessorCount, @@ -173,16 +171,6 @@ export function QueryActionsProvider(Private, Promise) { return fetchSurroundingRows('successors', state); }; - const fetchMorePredecessorRows = (state) => () => { - increasePredecessorCount(state)(); - return fetchSurroundingRows('predecessors', state); - }; - - const fetchMoreSuccessorRows = (state) => () => { - increaseSuccessorCount(state)(); - return fetchSurroundingRows('successors', state); - }; - const setAllRows = (state) => (predecessorRows, anchorRow, successorRows) => ( state.rows.all = [ ...(predecessorRows || []), @@ -199,8 +187,6 @@ export function QueryActionsProvider(Private, Promise) { fetchContextRowsWithNewQueryParameters, fetchGivenPredecessorRows, fetchGivenSuccessorRows, - fetchMorePredecessorRows, - fetchMoreSuccessorRows, setAllRows, }; } diff --git a/src/legacy/core_plugins/kibana/public/context/query_parameters/__tests__/action_increase_predecessor_count.js b/src/legacy/core_plugins/kibana/public/context/query_parameters/__tests__/action_increase_predecessor_count.js deleted file mode 100644 index cd19f7af8d8294..00000000000000 --- a/src/legacy/core_plugins/kibana/public/context/query_parameters/__tests__/action_increase_predecessor_count.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; - -import { createStateStub } from './_utils'; -import { QueryParameterActionsProvider } from '../actions'; - - -describe('context app', function () { - beforeEach(ngMock.module('kibana')); - - describe('action increasePredecessorCount', function () { - let increasePredecessorCount; - - beforeEach(ngMock.inject(function createPrivateStubs(Private) { - increasePredecessorCount = Private(QueryParameterActionsProvider).increasePredecessorCount; - })); - - it('should increase the predecessorCount by the given value', function () { - const state = createStateStub(); - - increasePredecessorCount(state)(20); - - expect(state.queryParameters.predecessorCount).to.equal(30); - }); - - it('should increase the predecessorCount by the default step size if not value is given', function () { - const state = createStateStub(); - - increasePredecessorCount(state)(); - - expect(state.queryParameters.predecessorCount).to.equal(13); - }); - - it('should limit the predecessorCount to 0 as a lower bound', function () { - const state = createStateStub(); - - increasePredecessorCount(state)(-20); - - expect(state.queryParameters.predecessorCount).to.equal(0); - }); - - it('should limit the predecessorCount to 10000 as an upper bound', function () { - const state = createStateStub(); - - increasePredecessorCount(state)(20000); - - expect(state.queryParameters.predecessorCount).to.equal(10000); - }); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/context/query_parameters/__tests__/action_increase_successor_count.js b/src/legacy/core_plugins/kibana/public/context/query_parameters/__tests__/action_increase_successor_count.js deleted file mode 100644 index 7036df1ea626fe..00000000000000 --- a/src/legacy/core_plugins/kibana/public/context/query_parameters/__tests__/action_increase_successor_count.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; - -import { createStateStub } from './_utils'; -import { QueryParameterActionsProvider } from '../actions'; - - -describe('context app', function () { - beforeEach(ngMock.module('kibana')); - - describe('action increaseSuccessorCount', function () { - let increaseSuccessorCount; - - beforeEach(ngMock.inject(function createPrivateStubs(Private) { - increaseSuccessorCount = Private(QueryParameterActionsProvider).increaseSuccessorCount; - })); - - it('should increase the successorCount by the given value', function () { - const state = createStateStub(); - - increaseSuccessorCount(state)(20); - - expect(state.queryParameters.successorCount).to.equal(30); - }); - - it('should increase the successorCount by the default step size if not value is given', function () { - const state = createStateStub(); - - increaseSuccessorCount(state)(); - - expect(state.queryParameters.successorCount).to.equal(13); - }); - - it('should limit the successorCount to 0 as a lower bound', function () { - const state = createStateStub(); - - increaseSuccessorCount(state)(-20); - - expect(state.queryParameters.successorCount).to.equal(0); - }); - - it('should limit the successorCount to 10000 as an upper bound', function () { - const state = createStateStub(); - - increaseSuccessorCount(state)(20000); - - expect(state.queryParameters.successorCount).to.equal(10000); - }); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/context/query_parameters/actions.js b/src/legacy/core_plugins/kibana/public/context/query_parameters/actions.js index cd5af188ad3213..1c895b8d9e1c5c 100644 --- a/src/legacy/core_plugins/kibana/public/context/query_parameters/actions.js +++ b/src/legacy/core_plugins/kibana/public/context/query_parameters/actions.js @@ -40,12 +40,6 @@ export function QueryParameterActionsProvider(indexPatterns, Private) { ) ); - const increasePredecessorCount = (state) => ( - value = state.queryParameters.defaultStepSize, - ) => ( - setPredecessorCount(state)(state.queryParameters.predecessorCount + value) - ); - const setSuccessorCount = (state) => (successorCount) => ( state.queryParameters.successorCount = clamp( MIN_CONTEXT_SIZE, @@ -54,12 +48,6 @@ export function QueryParameterActionsProvider(indexPatterns, Private) { ) ); - const increaseSuccessorCount = (state) => ( - value = state.queryParameters.defaultStepSize, - ) => ( - setSuccessorCount(state)(state.queryParameters.successorCount + value) - ); - const setQueryParameters = (state) => (queryParameters) => ( Object.assign( state.queryParameters, @@ -82,8 +70,6 @@ export function QueryParameterActionsProvider(indexPatterns, Private) { return { addFilter, updateFilters, - increasePredecessorCount, - increaseSuccessorCount, setPredecessorCount, setQueryParameters, setSuccessorCount, diff --git a/src/legacy/core_plugins/kibana/public/context/query_parameters/constants.js b/src/legacy/core_plugins/kibana/public/context/query_parameters/constants.ts similarity index 99% rename from src/legacy/core_plugins/kibana/public/context/query_parameters/constants.js rename to src/legacy/core_plugins/kibana/public/context/query_parameters/constants.ts index 9b9e93f7ba0001..9fdf79a714e786 100644 --- a/src/legacy/core_plugins/kibana/public/context/query_parameters/constants.js +++ b/src/legacy/core_plugins/kibana/public/context/query_parameters/constants.ts @@ -19,7 +19,6 @@ import { createInitialQueryParametersState } from './state'; - export const MAX_CONTEXT_SIZE = 10000; // Elasticsearch's default maximum size limit export const MIN_CONTEXT_SIZE = 0; export const QUERY_PARAMETER_KEYS = Object.keys(createInitialQueryParametersState()); diff --git a/src/legacy/core_plugins/kibana/public/context/query_parameters/state.js b/src/legacy/core_plugins/kibana/public/context/query_parameters/state.ts similarity index 89% rename from src/legacy/core_plugins/kibana/public/context/query_parameters/state.js rename to src/legacy/core_plugins/kibana/public/context/query_parameters/state.ts index 6fa5ad843de586..094dba6341d0c8 100644 --- a/src/legacy/core_plugins/kibana/public/context/query_parameters/state.js +++ b/src/legacy/core_plugins/kibana/public/context/query_parameters/state.ts @@ -17,7 +17,10 @@ * under the License. */ -export function createInitialQueryParametersState(defaultStepSize, tieBreakerField) { +export function createInitialQueryParametersState( + defaultStepSize: number = 5, + tieBreakerField: string = '_doc' +) { return { anchorId: null, columns: [], diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index e3f7701b02a605..9f572a6f8bc2eb 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -54,7 +54,6 @@ import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; import { getFilterGenerator } from 'ui/filter_manager'; import { SavedObjectsClientProvider } from 'ui/saved_objects'; -import { VisualizeLoaderProvider } from 'ui/visualize/loader/visualize_loader'; import { recentlyAccessed } from 'ui/persisted_log'; import { getDocLink } from 'ui/documentation_links'; import '../components/fetch_error'; @@ -196,8 +195,6 @@ function discoverController( localStorage, uiCapabilities ) { - const visualizeLoader = Private(VisualizeLoaderProvider); - let visualizeHandler; const Vis = Private(VisProvider); const responseHandler = vislibSeriesResponseHandlerProvider().handler; const getUnhashableStates = Private(getUnhashableStatesProvider); @@ -214,6 +211,13 @@ function discoverController( timefilter.disableTimeRangeSelector(); timefilter.disableAutoRefreshSelector(); + $scope.timefilterUpdateHandler = (ranges) => { + timefilter.setTime({ + from: moment(ranges.from).toISOString(), + to: moment(ranges.to).toISOString(), + mode: 'absolute', + }); + }; $scope.getDocLink = getDocLink; $scope.intervalOptions = intervalOptions; @@ -794,15 +798,7 @@ function discoverController( .resolve(buildVislibDimensions($scope.vis, { timeRange: $scope.timeRange, searchSource: $scope.searchSource })) .then(resp => responseHandler(tabifiedData, resp)) .then(resp => { - visualizeHandler.render({ - as: 'visualization', - value: { - visType: $scope.vis.type.name, - visData: resp, - visConfig: $scope.vis.params, - params: {}, - } - }); + $scope.histogramData = resp; }); } @@ -1048,13 +1044,6 @@ function discoverController( $scope.searchSource.setField('aggs', function () { return $scope.vis.getAggConfig().toDsl(); }); - - $timeout(async () => { - const visEl = $element.find('#discoverHistogram')[0]; - visualizeHandler = await visualizeLoader.embedVisualizationWithSavedObject(visEl, visSavedObject, { - autoFetch: false, - }); - }); } function resolveIndexPatternLoading() { diff --git a/src/legacy/core_plugins/kibana/public/discover/directives/_histogram.scss b/src/legacy/core_plugins/kibana/public/discover/directives/_histogram.scss new file mode 100644 index 00000000000000..948f438eea5423 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/directives/_histogram.scss @@ -0,0 +1,4 @@ +.dscHistogram__header--partial { + font-weight: $euiFontWeightRegular; + min-width: $euiSize * 12; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/directives/_index.scss b/src/legacy/core_plugins/kibana/public/discover/directives/_index.scss index 8d092c11a4e1ef..c65243d99c8f49 100644 --- a/src/legacy/core_plugins/kibana/public/discover/directives/_index.scss +++ b/src/legacy/core_plugins/kibana/public/discover/directives/_index.scss @@ -1 +1,2 @@ @import 'no_results'; +@import 'histogram'; \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/discover/directives/histogram.tsx b/src/legacy/core_plugins/kibana/public/discover/directives/histogram.tsx new file mode 100644 index 00000000000000..e387355af69556 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/directives/histogram.tsx @@ -0,0 +1,258 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer } from '@elastic/eui'; +import moment from 'moment-timezone'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import lightEuiTheme from '@elastic/eui/dist/eui_theme_light.json'; +import darkEuiTheme from '@elastic/eui/dist/eui_theme_dark.json'; + +import { + AnnotationDomainTypes, + Axis, + Chart, + HistogramBarSeries, + GeometryValue, + getAnnotationId, + getAxisId, + getSpecId, + LineAnnotation, + Position, + ScaleType, + Settings, + RectAnnotation, + TooltipValue, + TooltipType, +} from '@elastic/charts'; + +import { i18n } from '@kbn/i18n'; + +import { getChartTheme } from 'ui/elastic_charts'; +import chrome from 'ui/chrome'; +// @ts-ignore: path dynamic for kibana +import { timezoneProvider } from 'ui/vis/lib/timezone'; + +export interface DiscoverHistogramProps { + chartData: any; + timefilterUpdateHandler: (ranges: { from: number; to: number }) => void; +} + +export class DiscoverHistogram extends Component { + public static propTypes = { + chartData: PropTypes.object, + timefilterUpdateHandler: PropTypes.func, + }; + + public onBrushEnd = (min: number, max: number) => { + const range = { + from: min, + to: max, + }; + + this.props.timefilterUpdateHandler(range); + }; + + public onElementClick = (xInterval: number) => (elementData: GeometryValue[]) => { + const startRange = elementData[0].x; + + const range = { + from: startRange, + to: startRange + xInterval, + }; + + this.props.timefilterUpdateHandler(range); + }; + + public formatXValue = (val: string) => { + const xAxisFormat = this.props.chartData.xAxisFormat.params.pattern; + + return moment(val).format(xAxisFormat); + }; + + public renderBarTooltip = (xInterval: number, domainStart: number, domainEnd: number) => ( + headerData: TooltipValue + ): JSX.Element | string => { + const headerDataValue = headerData.value; + const formattedValue = this.formatXValue(headerDataValue); + + const partialDataText = i18n.translate('kbn.discover.histogram.partialData.bucketTooltipText', { + defaultMessage: + 'The selected time range does not include this entire bucket, it may contain partial data.', + }); + + if (headerDataValue < domainStart || headerDataValue + xInterval > domainEnd) { + return ( + + + + + + {partialDataText} + + +

{formattedValue}

+
+ ); + } + + return formattedValue; + }; + + public render() { + const uiSettings = chrome.getUiSettingsClient(); + const timeZone = timezoneProvider(uiSettings)(); + const { chartData } = this.props; + + if (!chartData || !chartData.series[0]) { + return null; + } + + const data = chartData.series[0].values; + + /** + * Deprecation: [interval] on [date_histogram] is deprecated, use [fixed_interval] or [calendar_interval]. + * see https://github.com/elastic/kibana/issues/27410 + * TODO: Once the Discover query has been update, we should change the below to use the new field + */ + const xInterval = chartData.ordered.interval; + + const xValues = chartData.xAxisOrderedValues; + const lastXValue = xValues[xValues.length - 1]; + + const domain = chartData.ordered; + const domainStart = domain.min.valueOf(); + const domainEnd = domain.max.valueOf(); + + const domainMin = data[0].x > domainStart ? domainStart : data[0].x; + const domainMax = domainEnd - xInterval > lastXValue ? domainEnd - xInterval : lastXValue; + + const xDomain = { + min: domainMin, + max: domainMax, + minInterval: xInterval, + }; + + // Domain end of 'now' will be milliseconds behind current time, so we extend time by 1 minute and check if + // the annotation is within this range; if so, the line annotation uses the domainEnd as its value + const now = moment(); + const isAnnotationAtEdge = + moment(domainEnd) + .add(60000) + .isAfter(now) && now.isAfter(domainEnd); + const lineAnnotationValue = isAnnotationAtEdge ? domainEnd : now; + + const lineAnnotationData = [ + { + dataValue: lineAnnotationValue, + }, + ]; + const isDarkMode = uiSettings.get('theme:darkMode'); + + const lineAnnotationStyle = { + line: { + strokeWidth: 2, + stroke: isDarkMode ? darkEuiTheme.euiColorDanger : lightEuiTheme.euiColorDanger, + opacity: 0.7, + }, + }; + + const rectAnnotations = []; + if (domainStart !== domainMin) { + rectAnnotations.push({ + coordinates: { + x1: domainStart, + }, + }); + } + if (domainEnd !== domainMax) { + rectAnnotations.push({ + coordinates: { + x0: domainEnd, + }, + }); + } + + const rectAnnotationStyle = { + stroke: isDarkMode ? darkEuiTheme.euiColorLightShade : lightEuiTheme.euiColorDarkShade, + strokeWidth: 0, + opacity: isDarkMode ? 0.6 : 0.2, + fill: isDarkMode ? darkEuiTheme.euiColorLightShade : lightEuiTheme.euiColorDarkShade, + }; + + const tooltipProps = { + headerFormatter: this.renderBarTooltip(xInterval, domainStart, domainEnd), + type: TooltipType.VerticalCursor, + }; + + return ( + + + + + + + + + ); + } +} diff --git a/src/legacy/core_plugins/kibana/public/discover/directives/index.js b/src/legacy/core_plugins/kibana/public/discover/directives/index.js index d13448bbf9c8a1..a6f0ead4f73657 100644 --- a/src/legacy/core_plugins/kibana/public/discover/directives/index.js +++ b/src/legacy/core_plugins/kibana/public/discover/directives/index.js @@ -20,14 +20,10 @@ import 'ngreact'; import { wrapInI18nContext } from 'ui/i18n'; import { uiModules } from 'ui/modules'; - import { DiscoverNoResults } from './no_results'; - import { DiscoverUninitialized } from './uninitialized'; - import { DiscoverUnsupportedIndexPattern } from './unsupported_index_pattern'; - -import './timechart'; +import { DiscoverHistogram } from './histogram'; const app = uiModules.get('apps/discover', ['react']); @@ -42,3 +38,5 @@ app.directive('discoverUninitialized', reactDirective => app.directive('discoverUnsupportedIndexPattern', reactDirective => reactDirective(wrapInI18nContext(DiscoverUnsupportedIndexPattern), ['unsupportedType']) ); + +app.directive('discoverHistogram', reactDirective => reactDirective(DiscoverHistogram)); diff --git a/src/legacy/core_plugins/kibana/public/discover/directives/timechart.js b/src/legacy/core_plugins/kibana/public/discover/directives/timechart.js deleted file mode 100644 index bb4f3a227d0cba..00000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/directives/timechart.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import VislibProvider from 'ui/vislib'; -import { uiModules } from 'ui/modules'; -uiModules - .get('apps/discover') - .directive('discoverTimechart', function (Private) { - const vislib = Private(VislibProvider); - - return { - restrict: 'E', - scope: { - data: '=' - }, - link: function ($scope, elem) { - - const init = function () { - // This elem should already have a height/width - const myChart = new vislib.Chart(elem[0], { - addLegend: false - }); - - $scope.$watch('data', function (data) { - if (data != null) { - myChart.render(data); - } - }); - }; - - // Start the directive - init(); - } - }; - }); diff --git a/src/legacy/core_plugins/kibana/public/discover/index.html b/src/legacy/core_plugins/kibana/public/discover/index.html index 980b3fefc6dd88..ae6bf340295fc7 100644 --- a/src/legacy/core_plugins/kibana/public/discover/index.html +++ b/src/legacy/core_plugins/kibana/public/discover/index.html @@ -162,11 +162,13 @@ -
-
+ ng-show="vis && rows.length !== 0" + chart-data="histogramData" + timefilter-update-handler="timefilterUpdateHandler" + watch-depth="reference" + >
createRegionMapTypeDefinition(dependencies)); + visualizationsSetup.types.registerVisualization(() => createRegionMapTypeDefinition(dependencies)); Vis = Private(visModule.VisProvider); RegionMapsVisualization = createRegionMapVisualization(dependencies); diff --git a/src/legacy/core_plugins/region_map/public/legacy.ts b/src/legacy/core_plugins/region_map/public/legacy.ts index 7adbc2117d7ee5..b669a45e3240a5 100644 --- a/src/legacy/core_plugins/region_map/public/legacy.ts +++ b/src/legacy/core_plugins/region_map/public/legacy.ts @@ -20,7 +20,7 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../visualizations/public/legacy'; import { RegionMapPluginSetupDependencies, RegionMapsConfig } from './plugin'; import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; @@ -30,7 +30,7 @@ const regionmapsConfig = npSetup.core.injectedMetadata.getInjectedVar( ) as RegionMapsConfig; const plugins: Readonly = { - visualizations: setupVisualizations, + visualizations: visualizationsSetup, data: npSetup.plugins.data, // Temporary solution diff --git a/src/legacy/core_plugins/region_map/public/plugin.ts b/src/legacy/core_plugins/region_map/public/plugin.ts index 4afee7269c5780..158f548b935dfe 100644 --- a/src/legacy/core_plugins/region_map/public/plugin.ts +++ b/src/legacy/core_plugins/region_map/public/plugin.ts @@ -24,7 +24,7 @@ import { UiSettingsClientContract, } from '../../../../core/public'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; -import { VisualizationsSetup } from '../../visualizations/public/np_ready/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'; diff --git a/src/legacy/core_plugins/region_map/public/region_map_type.js b/src/legacy/core_plugins/region_map/public/region_map_type.js index 9b28aa96eccbc7..71e949a0612b25 100644 --- a/src/legacy/core_plugins/region_map/public/region_map_type.js +++ b/src/legacy/core_plugins/region_map/public/region_map_type.js @@ -25,7 +25,7 @@ import { createRegionMapVisualization } from './region_map_visualization'; import { Status } from 'ui/vis/update_status'; import { RegionMapOptions } from './components/region_map_options'; -import { visFactory } from '../../visualizations/public/np_ready/public'; +import { visFactory } from '../../visualizations/public'; // TODO: reference to TILE_MAP plugin should be removed import { ORIGIN } from '../../tile_map/common/origin'; diff --git a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js index e32df04a583c2d..17162278b1e208 100644 --- a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js +++ b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js @@ -33,7 +33,7 @@ import EMS_TILES from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_ import EMS_STYLE_ROAD_MAP_BRIGHT from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_bright'; import EMS_STYLE_ROAD_MAP_DESATURATED from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_desaturated'; import EMS_STYLE_DARK_MAP from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_dark'; -import { setup } from '../../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../../visualizations/public/legacy'; import { createTileMapVisualization } from '../tile_map_visualization'; import { createTileMapTypeDefinition } from '../tile_map_type'; @@ -84,7 +84,7 @@ describe('CoordinateMapsVisualizationTest', function () { $injector, }; - setup.types.registerVisualization(() => createTileMapTypeDefinition(dependencies)); + visualizationsSetup.types.registerVisualization(() => createTileMapTypeDefinition(dependencies)); Vis = Private(visModule.VisProvider); CoordinateMapsVisualization = createTileMapVisualization(dependencies); diff --git a/src/legacy/core_plugins/tile_map/public/legacy.ts b/src/legacy/core_plugins/tile_map/public/legacy.ts index cf2e702cd09b64..185dac90bf8a04 100644 --- a/src/legacy/core_plugins/tile_map/public/legacy.ts +++ b/src/legacy/core_plugins/tile_map/public/legacy.ts @@ -20,13 +20,13 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../visualizations/public/legacy'; import { TileMapPluginSetupDependencies } from './plugin'; import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; const plugins: Readonly = { - visualizations: setupVisualizations, + visualizations: visualizationsSetup, data: npSetup.plugins.data, // Temporary solution diff --git a/src/legacy/core_plugins/tile_map/public/plugin.ts b/src/legacy/core_plugins/tile_map/public/plugin.ts index 1309c37a4c73a6..ba087d5ca5959b 100644 --- a/src/legacy/core_plugins/tile_map/public/plugin.ts +++ b/src/legacy/core_plugins/tile_map/public/plugin.ts @@ -24,7 +24,7 @@ import { UiSettingsClientContract, } from '../../../../core/public'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; -import { VisualizationsSetup } from '../../visualizations/public/np_ready/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'; diff --git a/src/legacy/core_plugins/tile_map/public/tile_map_type.js b/src/legacy/core_plugins/tile_map/public/tile_map_type.js index d96d8ad9be7865..4c339195173dfd 100644 --- a/src/legacy/core_plugins/tile_map/public/tile_map_type.js +++ b/src/legacy/core_plugins/tile_map/public/tile_map_type.js @@ -27,7 +27,7 @@ import { colorSchemas } from 'ui/vislib/components/color/truncated_colormaps'; import { convertToGeoJson } from 'ui/vis/map/convert_to_geojson'; import { createTileMapVisualization } from './tile_map_visualization'; -import { visFactory } from '../../visualizations/public/np_ready/public'; +import { visFactory } from '../../visualizations/public'; import { TileMapOptions } from './components/tile_map_options'; import { MapTypes } from './map_types'; diff --git a/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts b/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts index 539b4e1bdfb456..2552e04fbc9f2a 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts @@ -20,12 +20,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../visualizations/public/legacy'; import { MarkdownPluginSetupDependencies } from './plugin'; import { plugin } from '.'; const plugins: Readonly = { - visualizations: setupVisualizations, + visualizations: visualizationsSetup, data: npSetup.plugins.data, }; diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts b/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts index 2f1a46f1d8f3f9..7b2f8f6c236b29 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts +++ b/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { visFactory, DefaultEditorSize } from '../../visualizations/public/np_ready/public'; +import { visFactory, DefaultEditorSize } from '../../visualizations/public'; import { MarkdownVisWrapper } from './markdown_vis_controller'; import { MarkdownOptions } from './markdown_options'; diff --git a/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts b/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts index 5f9c82ca89290a..b013c755168d79 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts @@ -19,7 +19,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; -import { VisualizationsSetup } from '../../visualizations/public/np_ready/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { markdownVis } from './markdown_vis'; import { createMarkdownVisFn } from './markdown_fn'; diff --git a/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis.js b/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis.js index ce77c64d776513..f5fb6312d2b0ac 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis.js +++ b/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis.js @@ -24,7 +24,7 @@ import expect from '@kbn/expect'; import { VisProvider } from 'ui/vis'; import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { setup as setupVisualizations } from '../../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../../visualizations/public/legacy'; import { createMetricVisTypeDefinition } from '../metric_vis_type'; describe('metric_vis - createMetricVisTypeDefinition', () => { @@ -38,7 +38,7 @@ describe('metric_vis - createMetricVisTypeDefinition', () => { const Vis = Private(VisProvider); const metricVisType = createMetricVisTypeDefinition(); - setupVisualizations.types.registerVisualization(() => metricVisType); + visualizationsSetup.types.registerVisualization(() => metricVisType); const indexPattern = Private(LogstashIndexPatternStubProvider); diff --git a/src/legacy/core_plugins/vis_type_metric/public/legacy.ts b/src/legacy/core_plugins/vis_type_metric/public/legacy.ts index 2eea4c70309de1..2ded1a05a8ec49 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/legacy.ts @@ -20,13 +20,13 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../visualizations/public/legacy'; import { MetricVisPluginSetupDependencies } from './plugin'; import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; const plugins: Readonly = { - visualizations: setupVisualizations, + visualizations: visualizationsSetup, data: npSetup.plugins.data, // Temporary solution diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts index eba61edeeb84a3..76e88888ef7f2c 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts @@ -26,7 +26,7 @@ import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; // @ts-ignore import { MetricVisComponent } from './components/metric_vis_controller'; -import { visFactory } from '../../visualizations/public/np_ready/public'; +import { visFactory } from '../../visualizations/public'; export const createMetricVisTypeDefinition = () => { return visFactory.createReactVisualization({ diff --git a/src/legacy/core_plugins/vis_type_metric/public/plugin.ts b/src/legacy/core_plugins/vis_type_metric/public/plugin.ts index 66a3bb007e379b..b8c6001d1f02e5 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/plugin.ts @@ -20,7 +20,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; import { LegacyDependenciesPlugin } from './shim'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; -import { VisualizationsSetup } from '../../visualizations/public/np_ready/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { createMetricVisFn } from './metric_vis_fn'; // @ts-ignore diff --git a/src/legacy/core_plugins/vis_type_table/public/__tests__/table_vis_controller.js b/src/legacy/core_plugins/vis_type_table/public/__tests__/table_vis_controller.js index 9110c8dcb84864..f04c953f070ae4 100644 --- a/src/legacy/core_plugins/vis_type_table/public/__tests__/table_vis_controller.js +++ b/src/legacy/core_plugins/vis_type_table/public/__tests__/table_vis_controller.js @@ -28,7 +28,7 @@ import { AppStateProvider } from 'ui/state_management/app_state'; import { tabifyAggResponse } from 'ui/agg_response/tabify'; import { createTableVisTypeDefinition } from '../table_vis_type'; -import { setup as setupVisualizations } from '../../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../../visualizations/public/legacy'; describe('Table Vis - Controller', async function () { let $rootScope; @@ -52,7 +52,7 @@ describe('Table Vis - Controller', async function () { createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization, }; - setupVisualizations.types.registerVisualization(() => + visualizationsSetup.types.registerVisualization(() => createTableVisTypeDefinition(legacyDependencies) ); diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js index 8521ee729f313d..323201ded73a3d 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js +++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js @@ -31,7 +31,7 @@ import { round } from 'lodash'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { createTableVisTypeDefinition } from '../../table_vis_type'; -import { setup } from '../../../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../../../visualizations/public/legacy'; describe('Table Vis - AggTable Directive', function () { let $rootScope; @@ -107,7 +107,7 @@ describe('Table Vis - AggTable Directive', function () { createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization, }; - setup.types.registerVisualization(() => createTableVisTypeDefinition(legacyDependencies)); + visualizationsSetup.types.registerVisualization(() => createTableVisTypeDefinition(legacyDependencies)); tableAggResponse = legacyResponseHandlerProvider().handler; indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js index 7998a92a4759fa..77a0ee5221b956 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js +++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js @@ -28,7 +28,7 @@ import { tabifyAggResponse } from 'ui/agg_response/tabify'; import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { createTableVisTypeDefinition } from '../../table_vis_type'; -import { setup } from '../../../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../../../visualizations/public/legacy'; describe('Table Vis - AggTableGroup Directive', function () { let $rootScope; @@ -66,7 +66,7 @@ describe('Table Vis - AggTableGroup Directive', function () { createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization, }; - setup.types.registerVisualization(() => createTableVisTypeDefinition(legacyDependencies)); + visualizationsSetup.types.registerVisualization(() => createTableVisTypeDefinition(legacyDependencies)); tableAggResponse = legacyResponseHandlerProvider().handler; indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); diff --git a/src/legacy/core_plugins/vis_type_table/public/legacy.ts b/src/legacy/core_plugins/vis_type_table/public/legacy.ts index 8139a70552c48a..b6d2a4a152926d 100644 --- a/src/legacy/core_plugins/vis_type_table/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_table/public/legacy.ts @@ -22,11 +22,11 @@ import { npSetup, npStart } from 'ui/new_platform'; import { plugin } from '.'; import { TablePluginSetupDependencies } from './plugin'; -import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../visualizations/public/legacy'; import { LegacyDependenciesPlugin } from './shim'; const plugins: Readonly = { - visualizations: setupVisualizations, + visualizations: visualizationsSetup, data: npSetup.plugins.data, // Temporary solution diff --git a/src/legacy/core_plugins/vis_type_table/public/plugin.ts b/src/legacy/core_plugins/vis_type_table/public/plugin.ts index 6a39e8079a9fd2..23f6061ad8f93f 100644 --- a/src/legacy/core_plugins/vis_type_table/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_table/public/plugin.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { VisualizationsSetup } from '../../visualizations/public/np_ready/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; import { diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts index 6a5e06b6e6978d..dd2c21fe6477aa 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts @@ -20,12 +20,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../visualizations/public/legacy'; import { TagCloudPluginSetupDependencies } from './plugin'; import { plugin } from '.'; const plugins: Readonly = { - visualizations: setupVisualizations, + visualizations: visualizationsSetup, data: npSetup.plugins.data, }; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts index e13e9896e39403..3947521a9868ee 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts @@ -19,7 +19,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; -import { VisualizationsSetup } from '../../visualizations/public/np_ready/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { createTagCloudFn } from './tag_cloud_fn'; import { createTagCloudTypeDefinition } from './tag_cloud_type'; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts index 421821d93b045b..0b4d90522cc448 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts @@ -23,7 +23,7 @@ import { Status } from 'ui/vis/update_status'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { TagCloudOptions } from './components/tag_cloud_options'; -import { visFactory } from '../../visualizations/public/np_ready/public'; +import { visFactory } from '../../visualizations/public'; // @ts-ignore import { TagCloudVisualization } from './components/tag_cloud_visualization'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts b/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts index 8dc24503772dbb..79cfb1b164f0dd 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts @@ -20,12 +20,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../visualizations/public/legacy'; import { MetricsPluginSetupDependencies } from './plugin'; import { plugin } from '.'; const plugins: Readonly = { - visualizations: setupVisualizations, + visualizations: visualizationsSetup, data: npSetup.plugins.data, }; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts b/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts index fd00f95b1e00f0..b2f9269f852f69 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; // @ts-ignore import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; -import { visFactory } from '../../visualizations/public/np_ready/public'; +import { visFactory } from '../../visualizations/public'; // @ts-ignore import { createMetricsRequestHandler } from './request_handler'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts b/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts index 5c103a3ae4b084..673bd202b70f2d 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/plugin.ts @@ -18,7 +18,7 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; -import { VisualizationsSetup } from '../../visualizations/public/np_ready/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { createMetricsFn } from './metrics_fn'; import { createMetricsTypeDefinition } from './metrics_type'; diff --git a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js index 012f144983e989..9c46dfb9451fa3 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js +++ b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js @@ -41,7 +41,7 @@ import vegaMapImage256 from './vega_map_image_256.png'; import { VegaParser } from '../data_model/vega_parser'; import { SearchCache } from '../data_model/search_cache'; -import { setup } from '../../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../../visualizations/public/legacy'; import { createVegaTypeDefinition } from '../vega_type'; const THRESHOLD = 0.1; @@ -65,7 +65,7 @@ describe('VegaVisualizations', () => { uiSettings: $injector.get('config'), }; - setup.types.registerVisualization(() => + visualizationsSetup.types.registerVisualization(() => createVegaTypeDefinition(vegaVisualizationDependencies) ); diff --git a/src/legacy/core_plugins/vis_type_vega/public/legacy.ts b/src/legacy/core_plugins/vis_type_vega/public/legacy.ts index 15cf97beb57172..ded1e773ac07a0 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/legacy.ts @@ -20,13 +20,13 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; -import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../visualizations/public/legacy'; import { VegaPluginSetupDependencies } from './plugin'; import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; const plugins: Readonly = { - visualizations: setupVisualizations, + visualizations: visualizationsSetup, data: npSetup.plugins.data, // Temporary solution diff --git a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts index b2a6fade883ca0..4d90b360bdb69b 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts @@ -25,7 +25,7 @@ import { } from '../../../../core/public'; import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'; import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; -import { VisualizationsSetup } from '../../visualizations/public/np_ready/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { createVegaFn } from './vega_fn'; import { createVegaTypeDefinition } from './vega_type'; diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts index d1f04c794e3c62..6ffcd8867ffea9 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts @@ -25,7 +25,7 @@ import { DefaultEditorSize } from 'ui/vis/editor_size'; import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; import vegaEditorTemplate from './vega_editor_template.html'; -import { visFactory } from '../../visualizations/public/np_ready/public'; +import { visFactory } from '../../visualizations/public'; import { VegaVisualizationDependencies } from './plugin'; import { createVegaRequestHandler } from './vega_request_handler'; diff --git a/src/legacy/core_plugins/visualizations/index.ts b/src/legacy/core_plugins/visualizations/index.ts index 3642071667f487..bb9ef1588bdc2a 100644 --- a/src/legacy/core_plugins/visualizations/index.ts +++ b/src/legacy/core_plugins/visualizations/index.ts @@ -25,7 +25,7 @@ export default function VisualizationsPlugin(kibana: any) { const config: Legacy.PluginSpecOptions = { id: 'visualizations', require: ['data'], - publicDir: resolve(__dirname, 'public/np_ready/public'), + publicDir: resolve(__dirname, 'public'), config: (Joi: any) => { return Joi.object({ enabled: Joi.boolean().default(true), diff --git a/src/legacy/core_plugins/visualizations/public/index.ts b/src/legacy/core_plugins/visualizations/public/index.ts new file mode 100644 index 00000000000000..eb93bb9b419ebf --- /dev/null +++ b/src/legacy/core_plugins/visualizations/public/index.ts @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Static legacy code which hasn't been moved to this plugin yet, but + * should be eventually. + * + * @public + */ +// @ts-ignore Used only by tsvb, vega, input control vis +export { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; +// @ts-ignore +export { visFactory } from 'ui/vis/vis_factory'; +// @ts-ignore +export { DefaultEditorSize } from 'ui/vis/editor_size'; + +/** + * Legacy types which haven't been moved to this plugin yet, but + * should be eventually. + * + * @public + */ +import * as types from 'ui/vis/vis'; +export type Vis = types.Vis; +export type VisParams = types.VisParams; +export type VisProvider = types.VisProvider; +export type VisState = types.VisState; +export { VisualizationController, VisType } from 'ui/vis/vis_types/vis_type'; +export { VisTypesRegistry } from 'ui/registry/vis_types'; +export { Status } from 'ui/vis/update_status'; + +/** + * Static np-ready code, re-exported here so consumers can import from + * `src/legacy/core_plugins/visualizations/public` + * + * @public + */ +export * from './np_ready'; + +// for backwards compatibility with 7.3 +export { setup as visualizations } from './legacy'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts b/src/legacy/core_plugins/visualizations/public/legacy.ts similarity index 58% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts rename to src/legacy/core_plugins/visualizations/public/legacy.ts index 04a49294bd0c68..3dd31f624c4ad6 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts +++ b/src/legacy/core_plugins/visualizations/public/legacy.ts @@ -16,24 +16,37 @@ * specific language governing permissions and limitations * under the License. */ -/* eslint-disable @kbn/eslint/no-restricted-paths */ + +/** + * New Platform Shim + * + * In this file, we import any legacy dependencies we have, and shim them into + * our plugin by manually constructing the values that the new platform will + * eventually be passing to the `setup/start` method of our plugin definition. + * + * The idea is that our `plugin.ts` can stay "pure" and not contain any legacy + * world code. Then when it comes time to migrate to the new platform, we can + * simply delete this shim file. + * + * We are also calling `setup/start` here and exporting our public contract so that + * other legacy plugins are able to import from '../core_plugins/visualizations/legacy' + * and receive the response value of the `setup/start` contract, mimicking the + * data that will eventually be injected by the new platform. + */ + +import { PluginInitializerContext } from 'src/core/public'; import { npSetup, npStart } from 'ui/new_platform'; // @ts-ignore import { VisFiltersProvider, createFilter } from 'ui/vis/vis_filters'; // @ts-ignore -import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; -// @ts-ignore import { VisProvider as Vis } from 'ui/vis/index.js'; // @ts-ignore import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; -/* eslint-enable @kbn/eslint/no-restricted-paths */ - -import { visTypeAliasRegistry } from './types/vis_type_alias_registry'; import { plugin } from '.'; -const pluginInstance = plugin({} as any); +const pluginInstance = plugin({} as PluginInitializerContext); export const setup = pluginInstance.setup(npSetup.core, { __LEGACY: { @@ -43,8 +56,6 @@ export const setup = pluginInstance.setup(npSetup.core, { Vis, VisFactoryProvider, VisTypesRegistryProvider, - defaultFeedbackMessage, - visTypeAliasRegistry, }, }); -export const start = pluginInstance.start(npStart.core); +export const start = pluginInstance.start(npStart.core, {}); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/mocks.ts similarity index 72% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts rename to src/legacy/core_plugins/visualizations/public/mocks.ts index df5e4d25dedccb..5cfe78fc2fa349 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts +++ b/src/legacy/core_plugins/visualizations/public/mocks.ts @@ -17,7 +17,6 @@ * under the License. */ -/* eslint-disable @kbn/eslint/no-restricted-paths */ jest.mock('ui/vis/vis_filters'); jest.mock('ui/vis/default_feedback_message'); jest.mock('ui/vis/index.js'); @@ -26,23 +25,18 @@ jest.mock('ui/registry/vis_types'); // @ts-ignore import { VisFiltersProvider, createFilter } from 'ui/vis/vis_filters'; // @ts-ignore -import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; -// @ts-ignore import { VisProvider as Vis } from 'ui/vis/index.js'; // @ts-ignore import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; -/* eslint-enable @kbn/eslint/no-restricted-paths */ jest.mock('./types/vis_type_alias_registry'); -import { visTypeAliasRegistry } from './types/vis_type_alias_registry'; - -import { Plugin } from '.'; -import { coreMock } from '../../../../../../core/public/mocks'; +import { PluginInitializerContext } from 'src/core/public'; -export type Setup = jest.Mocked>; -export type Start = jest.Mocked>; +import { VisualizationsSetup, VisualizationsStart } from './'; +import { VisualizationsPlugin } from './np_ready/plugin'; +import { coreMock } from '../../../../core/public/mocks'; -const createSetupContract = (): Setup => ({ +const createSetupContract = (): VisualizationsSetup => ({ filters: { VisFiltersProvider: jest.fn(), createFilter: jest.fn(), @@ -51,7 +45,6 @@ const createSetupContract = (): Setup => ({ Vis, VisFactoryProvider: jest.fn(), registerVisualization: jest.fn(), - defaultFeedbackMessage, visTypeAliasRegistry: { add: jest.fn(), get: jest.fn(), @@ -59,10 +52,10 @@ const createSetupContract = (): Setup => ({ }, }); -const createStartContract = (): Start => {}; +const createStartContract = (): VisualizationsStart => ({}); const createInstance = () => { - const plugin = new Plugin({} as any); + const plugin = new VisualizationsPlugin({} as PluginInitializerContext); const setup = plugin.setup(coreMock.createSetup(), { __LEGACY: { @@ -72,11 +65,9 @@ const createInstance = () => { Vis, VisFactoryProvider, VisTypesRegistryProvider, - defaultFeedbackMessage, - visTypeAliasRegistry, }, }); - const doStart = () => plugin.start(coreMock.createStart()); + const doStart = () => plugin.start(coreMock.createStart(), {}); return { plugin, diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/filters/filters_service.ts b/src/legacy/core_plugins/visualizations/public/np_ready/filters/filters_service.ts similarity index 100% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/filters/filters_service.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/filters/filters_service.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/filters/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/filters/index.ts similarity index 92% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/filters/index.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/filters/index.ts index 591f7c9bc77151..480796c3771752 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/filters/index.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/filters/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { FiltersService, FiltersSetup } from './filters_service'; +export * from './filters_service'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/index.ts similarity index 53% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/index.ts index d38acaa3cf3f2c..38b6a321253589 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/index.ts @@ -17,29 +17,30 @@ * under the License. */ +/** + * Visualizations Plugin - public + * + * This is the entry point for the entire client-side public contract of the plugin. + * If something is not explicitly exported here, you can safely assume it is private + * to the plugin and not considered stable. + * + * All stateful contracts will be injected by the platform at runtime, and are defined + * in the setup/start interfaces in `plugin.ts`. The remaining items exported here are + * either types, or static code. + */ import { PluginInitializerContext } from 'src/core/public'; -import { VisualizationsPublicPlugin, Setup } from './plugin'; +import { VisualizationsPlugin, VisualizationsSetup, VisualizationsStart } from './plugin'; /** @public */ -export type VisualizationsSetup = Setup; +export type VisualizationsSetup = VisualizationsSetup; +export type VisualizationsStart = VisualizationsStart; /** @public types */ -export { - Vis, - visFactory, - DefaultEditorSize, - VisParams, - VisProvider, - VisState, - // VisualizationController, - // VisType, - VisTypeAlias, - VisTypesRegistry, - Status, -} from './types'; +export { VisTypeAlias } from './types'; export function plugin(initializerContext: PluginInitializerContext) { - return new VisualizationsPublicPlugin(initializerContext); + return new VisualizationsPlugin(initializerContext); } -export { VisualizationsPublicPlugin as Plugin }; +/** @public static code */ +// TODO once items are moved from ui/vis into this service diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json b/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json deleted file mode 100644 index 8ecf3dfce6e945..00000000000000 --- a/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "id": "visualizations", - "version": "kibana", - "requiredPlugins": [ - ], - "server": false, - "ui": true - } - \ No newline at end of file diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/plugin.ts similarity index 53% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/plugin.ts index abf5974b77532e..90ca5fc79175ca 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/plugin.ts @@ -20,9 +20,13 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core import { FiltersService, FiltersSetup } from './filters'; import { TypesService, TypesSetup } from './types'; -import { VisTypeAliasRegistry } from './types/vis_type_alias_registry'; -interface SetupDependencies { +/** + * Interface for any dependencies on other plugins' contracts. + * + * @internal + */ +interface VisualizationsPluginSetupDependencies { __LEGACY: { VisFiltersProvider: any; createFilter: any; @@ -30,36 +34,53 @@ interface SetupDependencies { Vis: any; VisFactoryProvider: any; VisTypesRegistryProvider: any; - defaultFeedbackMessage: any; - visTypeAliasRegistry: VisTypeAliasRegistry; }; } -export interface Setup { +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface VisualizationsPluginStartDependencies {} + +/** + * Interface for this plugin's returned setup/start contracts. + * + * @public + */ +export interface VisualizationsSetup { filters: FiltersSetup; types: TypesSetup; } -export type Start = void; +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisualizationsStart {} -export class VisualizationsPublicPlugin implements Plugin { - private readonly filters: FiltersService; - private readonly types: TypesService; +/** + * Visualizations Plugin - public + * + * This plugin's stateful contracts are returned from the `setup` and `start` methods + * below. The interfaces for these contracts are provided above. + * + * @internal + */ +export class VisualizationsPlugin + implements + Plugin< + VisualizationsSetup, + VisualizationsStart, + VisualizationsPluginSetupDependencies, + VisualizationsPluginStartDependencies + > { + private readonly filters: FiltersService = new FiltersService(); + private readonly types: TypesService = new TypesService(); - constructor(initializerContext: PluginInitializerContext) { - this.filters = new FiltersService(); - this.types = new TypesService(); - } + constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup, { __LEGACY }: SetupDependencies) { + public setup(core: CoreSetup, { __LEGACY }: VisualizationsPluginSetupDependencies) { const { VisFiltersProvider, createFilter, Vis, VisFactoryProvider, VisTypesRegistryProvider, - defaultFeedbackMessage, - visTypeAliasRegistry, } = __LEGACY; return { @@ -67,18 +88,12 @@ export class VisualizationsPublicPlugin implements Plugin any) => { VisTypesRegistryProvider.register(registerFn); }, - defaultFeedbackMessage, // make default in base vis type, or move? visTypeAliasRegistry, }; } @@ -64,17 +47,11 @@ export class TypesService { } } -/** @public */ +/** @internal */ export type TypesSetup = ReturnType; -export { visFactory, DefaultEditorSize }; - /** @public types */ export type VisTypeAlias = VisTypeAlias; -export type Vis = types.Vis; -export type VisParams = types.VisParams; -export type VisProvider = types.VisProvider; -export type VisState = types.VisState; -// todo: this breaks it // export { VisualizationController, VisType } from 'ui/vis/vis_types/vis_type'; -export { VisTypesRegistry } from 'ui/registry/vis_types'; -export { Status } from 'ui/vis/update_status'; + +/** @public static code */ +// TODO once items are moved from ui/vis into this service diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/vis_type_alias_registry.ts b/src/legacy/core_plugins/visualizations/public/np_ready/types/vis_type_alias_registry.ts similarity index 91% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/types/vis_type_alias_registry.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/types/vis_type_alias_registry.ts index eb84f93a0d3bae..f982d1ed3ca215 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/vis_type_alias_registry.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/types/vis_type_alias_registry.ts @@ -17,7 +17,7 @@ * under the License. */ -export interface VisualizationListItem { +interface VisualizationListItem { editUrl: string; icon: string; id: string; @@ -27,7 +27,7 @@ export interface VisualizationListItem { typeTitle: string; } -export interface VisualizationsAppExtension { +interface VisualizationsAppExtension { docTypes: string[]; searchFields?: string[]; toListItem: (savedObject: { @@ -52,14 +52,14 @@ export interface VisTypeAlias { const registry: VisTypeAlias[] = []; -export interface VisTypeAliasRegistry { +interface VisTypeAliasRegistry { get: () => VisTypeAlias[]; add: (newVisTypeAlias: VisTypeAlias) => void; } export const visTypeAliasRegistry: VisTypeAliasRegistry = { get: () => [...registry], - add: (newVisTypeAlias: VisTypeAlias) => { + add: newVisTypeAlias => { if (registry.find(visTypeAlias => visTypeAlias.name === newVisTypeAlias.name)) { throw new Error(`${newVisTypeAlias.name} already registered`); } diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/index.ts b/src/legacy/ui/public/elastic_charts/index.ts similarity index 75% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/types/index.ts rename to src/legacy/ui/public/elastic_charts/index.ts index 07b33c86fc4e21..661ea693102a73 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/types/index.ts +++ b/src/legacy/ui/public/elastic_charts/index.ts @@ -17,19 +17,10 @@ * under the License. */ -export { - TypesService, - visFactory, - DefaultEditorSize, - // types - TypesSetup, - Vis, - VisParams, - VisProvider, - VisState, - // VisualizationController, - // VisType, - VisTypeAlias, - VisTypesRegistry, - Status, -} from './types_service'; +import chrome from 'ui/chrome'; +import { Theme, LIGHT_THEME, DARK_THEME } from '@elastic/charts'; + +export function getChartTheme(): Theme { + const isDarkMode = chrome.getUiSettingsClient().get('theme:darkMode'); + return isDarkMode ? DARK_THEME : LIGHT_THEME; +} diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js index c29542e6a40354..9f37feef781cae 100644 --- a/test/functional/apps/discover/_discover.js +++ b/test/functional/apps/discover/_discover.js @@ -26,7 +26,7 @@ export default function ({ getService, getPageObjects }) { const browser = getService('browser'); const kibanaServer = getService('kibanaServer'); const filterBar = getService('filterBar'); - const PageObjects = getPageObjects(['common', 'discover', 'header', 'visualize', 'timePicker']); + const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); const defaultSettings = { defaultIndex: 'logstash-*', }; @@ -85,83 +85,30 @@ export default function ({ getService, getPageObjects }) { }); }); - it('should show the correct bar chart', async function () { - const expectedBarChartData = [ - 35, - 189, - 694, - 1347, - 1285, - 704, - 176, - 29, - 39, - 189, - 640, - 1276, - 1327, - 663, - 166, - 25, - 30, - 164, - 663, - 1320, - 1270, - 681, - 188, - 27, - ]; - await verifyChartData(expectedBarChartData); - }); - it('should show correct time range string in chart', async function () { const actualTimeString = await PageObjects.discover.getChartTimespan(); const expectedTimeString = `Sep 19, 2015 @ 06:31:44.000 - Sep 23, 2015 @ 18:31:44.000`; expect(actualTimeString).to.be(expectedTimeString); }); - it('should show bars in the correct time zone', async function () { - const maxTicks = [ - '2015-09-20 00:00', - '2015-09-20 12:00', - '2015-09-21 00:00', - '2015-09-21 12:00', - '2015-09-22 00:00', - '2015-09-22 12:00', - '2015-09-23 00:00', - '2015-09-23 12:00' - ]; - - for (const tick of await PageObjects.discover.getBarChartXTicks()) { - if (!maxTicks.includes(tick)) { - throw new Error(`unexpected x-axis tick "${tick}"`); - } - } - }); - it('should modify the time range when a bar is clicked', async function () { await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.visualize.waitForVisualization(); - await PageObjects.discover.clickHistogramBar(0); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.discover.clickHistogramBar(); const time = await PageObjects.timePicker.getTimeConfig(); - expect(time.start).to.be('Sep 20, 2015 @ 00:00:00.000'); - expect(time.end).to.be('Sep 20, 2015 @ 03:00:00.000'); - const rowData = await PageObjects.discover.getDocTableIndex(1); - expect(rowData).to.have.string('Sep 20, 2015 @ 02:57:03.761'); + expect(time.start).to.be('Sep 21, 2015 @ 09:00:00.000'); + expect(time.end).to.be('Sep 21, 2015 @ 12:00:00.000'); + const rowData = await PageObjects.discover.getDocTableField(1); + expect(rowData).to.have.string('Sep 21, 2015 @ 11:59:22.316'); }); it('should modify the time range when the histogram is brushed', async function () { await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.visualize.waitForVisualization(); - await PageObjects.discover.brushHistogram(0, 1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.discover.brushHistogram(); const newDurationHours = await PageObjects.timePicker.getTimeDurationInHours(); - expect(Math.round(newDurationHours)).to.be(3); + expect(Math.round(newDurationHours)).to.be(108); const rowData = await PageObjects.discover.getDocTableField(1); - expect(rowData).to.have.string('Sep 20, 2015 @ 02:56:02.323'); + expect(rowData).to.have.string('Sep 22, 2015 @ 23:50:13.253'); }); it('should show correct initial chart interval of Auto', async function () { @@ -173,167 +120,6 @@ export default function ({ getService, getPageObjects }) { expect(actualInterval).to.be(expectedInterval); }); - it('should show correct data for chart interval Hourly', async function () { - await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); - await PageObjects.discover.setChartInterval('Hourly'); - - const expectedBarChartData = [ - 4, - 7, - 16, - 23, - 38, - 87, - 132, - 159, - 248, - 320, - 349, - 376, - 380, - 324, - 293, - 233, - 188, - 125, - 69, - 40, - 28, - 17, - 2, - 3, - 8, - 10, - 12, - 28, - 36, - 84, - 111, - 157, - 229, - 292, - 324, - 373, - 378, - 345, - 306, - 223, - 167, - 124, - 72, - 35, - 22, - 11, - 7, - 1, - 6, - 5, - 12, - 25, - 27, - 76, - 111, - 175, - 228, - 294, - 358, - 372, - 366, - 344, - 276, - 213, - 201, - 113, - 72, - 39, - 36, - 12, - 7, - 3, - ]; - await verifyChartData(expectedBarChartData); - }); - - it('should show correct data for chart interval Daily', async function () { - const chartInterval = 'Daily'; - const expectedBarChartData = [4757, 4614, 4633]; - await PageObjects.discover.setChartInterval(chartInterval); - await retry.try(async () => { - await verifyChartData(expectedBarChartData); - }); - }); - - it('should show correct data for chart interval Weekly', async function () { - const chartInterval = 'Weekly'; - const expectedBarChartData = [4757, 9247]; - - await PageObjects.discover.setChartInterval(chartInterval); - await retry.try(async () => { - await verifyChartData(expectedBarChartData); - }); - }); - - it('browser back button should show previous interval Daily', async function () { - const expectedChartInterval = 'Daily'; - const expectedBarChartData = [4757, 4614, 4633]; - - await browser.goBack(); - await retry.try(async function tryingForTime() { - const actualInterval = await PageObjects.discover.getChartInterval(); - expect(actualInterval).to.be(expectedChartInterval); - }); - await verifyChartData(expectedBarChartData); - }); - - it('should show correct data for chart interval Monthly', async function () { - const chartInterval = 'Monthly'; - const expectedBarChartData = [13129]; - - await PageObjects.discover.setChartInterval(chartInterval); - await verifyChartData(expectedBarChartData); - }); - - it('should show correct data for chart interval Yearly', async function () { - const chartInterval = 'Yearly'; - const expectedBarChartData = [13129]; - - await PageObjects.discover.setChartInterval(chartInterval); - await verifyChartData(expectedBarChartData); - }); - - it('should show correct data for chart interval Auto', async function () { - const chartInterval = 'Auto'; - const expectedBarChartData = [ - 35, - 189, - 694, - 1347, - 1285, - 704, - 176, - 29, - 39, - 189, - 640, - 1276, - 1327, - 663, - 166, - 25, - 30, - 164, - 663, - 1320, - 1270, - 681, - 188, - 27, - ]; - - await PageObjects.discover.setChartInterval(chartInterval); - await verifyChartData(expectedBarChartData); - }); - it('should show Auto chart interval', async function () { const expectedChartInterval = 'Auto'; @@ -345,42 +131,6 @@ export default function ({ getService, getPageObjects }) { const isVisible = await PageObjects.discover.hasNoResults(); expect(isVisible).to.be(false); }); - - async function verifyChartData(expectedBarChartData) { - await retry.try(async function tryingForTime() { - const paths = await PageObjects.discover.getBarChartData(); - // the largest bars are over 100 pixels high so this is less than 1% tolerance - const barHeightTolerance = 1; - let stringResults = ''; - let hasFailure = false; - for (let y = 0; y < expectedBarChartData.length; y++) { - stringResults += - y + - ': expected = ' + - expectedBarChartData[y] + - ', actual = ' + - paths[y] + - ', Pass = ' + - (Math.abs(expectedBarChartData[y] - paths[y]) < - barHeightTolerance) + - '\n'; - if ( - Math.abs(expectedBarChartData[y] - paths[y]) > barHeightTolerance - ) { - hasFailure = true; - } - } - if (hasFailure) { - log.debug(stringResults); - log.debug(paths); - } - for (let x = 0; x < expectedBarChartData.length; x++) { - expect( - Math.abs(expectedBarChartData[x] - paths[x]) < barHeightTolerance - ).to.be.ok(); - } - }); - } }); describe('query #2, which has an empty time range', () => { @@ -439,35 +189,15 @@ export default function ({ getService, getPageObjects }) { describe('time zone switch', () => { it('should show bars in the correct time zone after switching', async function () { - await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'America/Phoenix' }); await browser.refresh(); await PageObjects.header.awaitKibanaChrome(); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - const maxTicks = [ - '2015-09-20 00:00', - '2015-09-20 12:00', - '2015-09-21 00:00', - '2015-09-21 12:00', - '2015-09-22 00:00', - '2015-09-22 12:00', - '2015-09-23 00:00', - '2015-09-23 12:00' - ]; - - await retry.try(async function () { - for (const tick of await PageObjects.discover.getBarChartXTicks()) { - if (!maxTicks.includes(tick)) { - throw new Error(`unexpected x-axis tick "${tick}"`); - } - } - }); log.debug('check that the newest doc timestamp is now -7 hours from the UTC time in the first test'); const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData.startsWith('Sep 22, 2015 @ 16:50:13.253')).to.be.ok(); }); - }); }); } diff --git a/test/functional/apps/management/_scripted_fields.js b/test/functional/apps/management/_scripted_fields.js index ec3941264450de..32d410eb240408 100644 --- a/test/functional/apps/management/_scripted_fields.js +++ b/test/functional/apps/management/_scripted_fields.js @@ -129,13 +129,13 @@ export default function ({ getService, getPageObjects }) { const toTime = '2015-09-18 18:31:44.000'; await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName); await retry.try(async function () { await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName); }); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); + await retry.try(async function () { const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be('Sep 18, 2015 @ 18:20:57.916\n18'); @@ -147,7 +147,7 @@ export default function ({ getService, getPageObjects }) { await log.debug('filter by the first value (14) in the expanded scripted field list'); await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName, '14'); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); + await retry.try(async function () { expect(await PageObjects.discover.getHitCount()).to.be('31'); }); @@ -161,7 +161,7 @@ export default function ({ getService, getPageObjects }) { await filterBar.removeAllFilters(); await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); + await inspector.open(); await inspector.setTablePageSize(50); await inspector.expectTableData(expectedChartValues); @@ -191,13 +191,13 @@ export default function ({ getService, getPageObjects }) { const toTime = '2015-09-18 18:31:44.000'; await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2); await retry.try(async function () { await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName2); }); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); + await retry.try(async function () { const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be('Sep 18, 2015 @ 18:20:57.916\ngood'); @@ -210,7 +210,7 @@ export default function ({ getService, getPageObjects }) { await log.debug('filter by "bad" in the expanded scripted field list'); await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName2, 'bad'); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); + await retry.try(async function () { expect(await PageObjects.discover.getHitCount()).to.be('27'); }); @@ -220,7 +220,6 @@ export default function ({ getService, getPageObjects }) { it('should visualize scripted field in vertical bar chart', async function () { await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName2); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); await inspector.open(); await inspector.expectTableData([ ['good', '359'], @@ -252,13 +251,13 @@ export default function ({ getService, getPageObjects }) { const toTime = '2015-09-18 18:31:44.000'; await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2); await retry.try(async function () { await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName2); }); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); + await retry.try(async function () { const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be('Sep 18, 2015 @ 18:20:57.916\ntrue'); @@ -271,7 +270,7 @@ export default function ({ getService, getPageObjects }) { await log.debug('filter by "true" in the expanded scripted field list'); await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName2, 'true'); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); + await retry.try(async function () { expect(await PageObjects.discover.getHitCount()).to.be('359'); }); @@ -281,7 +280,6 @@ export default function ({ getService, getPageObjects }) { it('should visualize scripted field in vertical bar chart', async function () { await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName2); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); await inspector.open(); await inspector.expectTableData([ ['true', '359'], @@ -314,13 +312,13 @@ export default function ({ getService, getPageObjects }) { const toTime = '2015-09-18 07:00:00.000'; await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2); await retry.try(async function () { await PageObjects.discover.clickFieldListItemAdd(scriptedPainlessFieldName2); }); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); + await retry.try(async function () { const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be('Sep 18, 2015 @ 06:52:55.953\n2015-09-18 07:00'); @@ -332,7 +330,7 @@ export default function ({ getService, getPageObjects }) { await log.debug('filter by "2015-09-17 23:00" in the expanded scripted field list'); await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName2, '2015-09-17 23:00'); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); + await retry.try(async function () { expect(await PageObjects.discover.getHitCount()).to.be('1'); }); @@ -342,7 +340,6 @@ export default function ({ getService, getPageObjects }) { it('should visualize scripted field in vertical bar chart', async function () { await PageObjects.discover.clickFieldListItemVisualize(scriptedPainlessFieldName2); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.waitForVisualization(); await inspector.open(); await inspector.setTablePageSize(50); await inspector.expectTableData([ diff --git a/test/functional/apps/management/index.js b/test/functional/apps/management/index.js index 4d4031b4e489b4..c9b444e5017891 100644 --- a/test/functional/apps/management/index.js +++ b/test/functional/apps/management/index.js @@ -33,7 +33,7 @@ export default function ({ getService, loadTestFile }) { }); describe('', function () { - this.tags('ciGroup7'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./_create_index_pattern_wizard')); loadTestFile(require.resolve('./_index_pattern_create_delete')); @@ -45,7 +45,7 @@ export default function ({ getService, loadTestFile }) { }); describe('', function () { - this.tags('ciGroup8'); + this.tags('ciGroup2'); loadTestFile(require.resolve('./_index_pattern_filter')); loadTestFile(require.resolve('./_scripted_fields_filter')); diff --git a/test/functional/apps/visualize/_gauge_chart.js b/test/functional/apps/visualize/_gauge_chart.js index d38d2b3cc2f0fe..60254b5c30f697 100644 --- a/test/functional/apps/visualize/_gauge_chart.js +++ b/test/functional/apps/visualize/_gauge_chart.js @@ -26,7 +26,8 @@ export default function ({ getService, getPageObjects }) { const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']); - describe('gauge chart', function indexPatternCreation() { + // FLAKY: https://github.com/elastic/kibana/issues/45089 + describe.skip('gauge chart', function indexPatternCreation() { this.tags('smoke'); const fromTime = '2015-09-19 06:31:44.000'; const toTime = '2015-09-23 18:31:44.000'; diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index cf7930603c20a8..3505ec3b1daf41 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -27,7 +27,8 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const inspector = getService('inspector'); const PageObjects = getPageObjects(['visualize', 'visualBuilder', 'timePicker']); - describe('visual builder', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/45315 + describe.skip('visual builder', function describeIndexTests() { this.tags('smoke'); beforeEach(async () => { await PageObjects.visualize.navigateToNewVisualization(); diff --git a/test/functional/apps/visualize/_tsvb_markdown.ts b/test/functional/apps/visualize/_tsvb_markdown.ts index 55fd297ee216e4..b433236492dc5e 100644 --- a/test/functional/apps/visualize/_tsvb_markdown.ts +++ b/test/functional/apps/visualize/_tsvb_markdown.ts @@ -37,7 +37,9 @@ export default function({ getPageObjects }: FtrProviderContext) { }); } - describe('visual builder', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/45323 + // FLAKY: https://github.com/elastic/kibana/issues/45330 + describe.skip('visual builder', function describeIndexTests() { describe('markdown', () => { before(async () => { await visualBuilder.resetPage(); diff --git a/test/functional/apps/visualize/_vertical_bar_chart.js b/test/functional/apps/visualize/_vertical_bar_chart.js index 1479d2bf034cc4..221ce68cf5ed83 100644 --- a/test/functional/apps/visualize/_vertical_bar_chart.js +++ b/test/functional/apps/visualize/_vertical_bar_chart.js @@ -278,7 +278,8 @@ export default function ({ getService, getPageObjects }) { }); }); - describe('vertical bar with multiple splits', function () { + // FLAKY: https://github.com/elastic/kibana/issues/45105 + describe.skip('vertical bar with multiple splits', function () { before(initBarChart); it('should show correct series', async function () { diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index 2a13b6fea91581..68a00b29d107eb 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -40,7 +40,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { }); describe('', function() { - this.tags('ciGroup9'); + this.tags('ciGroup3'); loadTestFile(require.resolve('./_embedding_chart')); loadTestFile(require.resolve('./_chart_types')); @@ -50,7 +50,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { }); describe('', function() { - this.tags('ciGroup10'); + this.tags('ciGroup4'); loadTestFile(require.resolve('./_inspector')); loadTestFile(require.resolve('./_experimental_vis')); @@ -62,7 +62,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { }); describe('', function() { - this.tags('ciGroup11'); + this.tags('ciGroup5'); loadTestFile(require.resolve('./_line_chart')); loadTestFile(require.resolve('./_pie_chart')); @@ -76,7 +76,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { }); describe('', function() { - this.tags('ciGroup12'); + this.tags('ciGroup6'); loadTestFile(require.resolve('./_tag_cloud')); loadTestFile(require.resolve('./_tile_map')); diff --git a/test/functional/page_objects/context_page.js b/test/functional/page_objects/context_page.js index a189376f06454f..437aa411c48d39 100644 --- a/test/functional/page_objects/context_page.js +++ b/test/functional/page_objects/context_page.js @@ -54,19 +54,19 @@ export function ContextPageProvider({ getService, getPageObjects }) { } async getPredecessorCountPicker() { - return await testSubjects.find('predecessorCountPicker'); + return await testSubjects.find('predecessorsCountPicker'); } async getSuccessorCountPicker() { - return await testSubjects.find('successorCountPicker'); + return await testSubjects.find('successorsCountPicker'); } async getPredecessorLoadMoreButton() { - return await testSubjects.find('predecessorLoadMoreButton'); + return await testSubjects.find('predecessorsLoadMoreButton'); } async getSuccessorLoadMoreButton() { - return await testSubjects.find('successorLoadMoreButton'); + return await testSubjects.find('successorsLoadMoreButton'); } async clickPredecessorLoadMoreButton() { diff --git a/test/functional/page_objects/discover_page.js b/test/functional/page_objects/discover_page.js index dfe8dc1071d46c..52b4c8d931ae21 100644 --- a/test/functional/page_objects/discover_page.js +++ b/test/functional/page_objects/discover_page.js @@ -116,16 +116,20 @@ export function DiscoverPageProvider({ getService, getPageObjects }) { await testSubjects.click('discoverOpenButton'); } - async clickHistogramBar(i) { - const bars = await find.allByCssSelector(`.series.histogram rect`); - await bars[i].click(); + async clickHistogramBar() { + const el = await find.byCssSelector('.echChart canvas:last-of-type'); + + await browser.getActions() + .move({ x: 200, y: 20, origin: el._webElement }) + .click() + .perform(); } - async brushHistogram(from, to) { - const bars = await find.allByCssSelector('.series.histogram rect'); + async brushHistogram() { + const el = await find.byCssSelector('.echChart canvas:last-of-type'); await browser.dragAndDrop( - { location: bars[from], offset: { x: 0, y: -5 } }, - { location: bars[to], offset: { x: 0, y: -5 } } + { location: el, offset: { x: 200, y: 20 } }, + { location: el, offset: { x: 400, y: 30 } } ); } @@ -133,12 +137,6 @@ export function DiscoverPageProvider({ getService, getPageObjects }) { return await globalNav.getLastBreadcrumb(); } - async getBarChartXTicks() { - const xAxis = await find.byCssSelector('.x.axis.CategoryAxis-1'); - const $ = await xAxis.parseDomContent(); - return $('.tick > text').toArray().map(tick => $(tick).text().trim()); - } - async getBarChartData() { let yAxisLabel = 0; diff --git a/test/mocha_decorations.d.ts b/test/mocha_decorations.d.ts index 4645faf3d5fe84..f6fca538a21598 100644 --- a/test/mocha_decorations.d.ts +++ b/test/mocha_decorations.d.ts @@ -26,12 +26,12 @@ type Tags = | 'ciGroup4' | 'ciGroup5' | 'ciGroup6' - | 'ciGroup7' - | 'ciGroup8' - | 'ciGroup9' - | 'ciGroup10' - | 'ciGroup11' - | 'ciGroup12'; + | 'ciGroup1' + | 'ciGroup2' + | 'ciGroup3' + | 'ciGroup4' + | 'ciGroup5' + | 'ciGroup6'; // We need to use the namespace here to match the Mocha definition // eslint-disable-next-line @typescript-eslint/no-namespace diff --git a/test/scripts/jenkins_firefox_smoke.sh b/test/scripts/jenkins_firefox_smoke.sh index bf3fe0691aa115..50e5843eb46f5b 100755 --- a/test/scripts/jenkins_firefox_smoke.sh +++ b/test/scripts/jenkins_firefox_smoke.sh @@ -17,3 +17,5 @@ checks-reporter-with-killswitch "Firefox smoke test" \ --kibana-install-dir "$installDir" \ --include-tag "smoke" \ --config test/functional/config.firefox.js; + +source "$KIBANA_DIR/test/scripts/jenkins_xpack_firefox_smoke.sh" diff --git a/test/scripts/jenkins_unit.sh b/test/scripts/jenkins_unit.sh index b5ae3724ce37c8..b304c555b79cac 100755 --- a/test/scripts/jenkins_unit.sh +++ b/test/scripts/jenkins_unit.sh @@ -6,3 +6,5 @@ trap 'node "$KIBANA_DIR/src/dev/failed_tests/cli"' EXIT export TEST_BROWSER_HEADLESS=1 "$(FORCE_COLOR=0 yarn bin)/grunt" jenkins:unit --dev; + +source "$KIBANA_DIR/test/scripts/jenkins_xpack.sh" diff --git a/test/scripts/jenkins_xpack.sh b/test/scripts/jenkins_xpack.sh index 61525b7a50398d..bdfc9dcebb308e 100755 --- a/test/scripts/jenkins_xpack.sh +++ b/test/scripts/jenkins_xpack.sh @@ -23,11 +23,13 @@ checks-reporter-with-killswitch "X-Pack SIEM cyclic dependency test" node legacy echo "" echo "" -echo " -> Running jest contracts tests" -cd "$XPACK_DIR" -SLAPSHOT_ONLINE=true CONTRACT_ONLINE=true node scripts/jest_contract.js --ci --verbose -echo "" -echo "" +# FAILING: https://github.com/elastic/kibana/issues/44250 +# echo " -> Running jest contracts tests" +# cd "$XPACK_DIR" +# SLAPSHOT_ONLINE=true CONTRACT_ONLINE=true node scripts/jest_contract.js --ci --verbose +# echo "" +# echo "" + # echo " -> Running jest integration tests" # cd "$XPACK_DIR" # node scripts/jest_integration --ci --verbose diff --git a/test/scripts/jenkins_xpack_ci_group.sh b/test/scripts/jenkins_xpack_ci_group.sh index 3527aa5eedfa9c..5fc376a8d72e3b 100755 --- a/test/scripts/jenkins_xpack_ci_group.sh +++ b/test/scripts/jenkins_xpack_ci_group.sh @@ -14,10 +14,10 @@ node scripts/functional_tests --assert-none-excluded \ --include-tag ciGroup4 \ --include-tag ciGroup5 \ --include-tag ciGroup6 \ - --include-tag ciGroup7 \ - --include-tag ciGroup8 \ - --include-tag ciGroup9 \ - --include-tag ciGroup10 + --include-tag ciGroup1 \ + --include-tag ciGroup2 \ + --include-tag ciGroup3 \ + --include-tag ciGroup4 echo " -> building and extracting default Kibana distributable for use in functional tests" cd "$KIBANA_DIR" diff --git a/test/visual_regression/config.ts b/test/visual_regression/config.ts index 74f283f74683a8..77450b517dcc6d 100644 --- a/test/visual_regression/config.ts +++ b/test/visual_regression/config.ts @@ -26,7 +26,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { return { ...functionalConfig.getAll(), - testFiles: [require.resolve('./tests/console_app')], + testFiles: [require.resolve('./tests/console_app'), require.resolve('./tests/discover')], services, diff --git a/test/visual_regression/services/visual_testing/visual_testing.ts b/test/visual_regression/services/visual_testing/visual_testing.ts index 2b05f666a3500b..210de4c714d5cb 100644 --- a/test/visual_regression/services/visual_testing/visual_testing.ts +++ b/test/visual_regression/services/visual_testing/visual_testing.ts @@ -27,15 +27,15 @@ import { FtrProviderContext } from '../../ftr_provider_context'; // @ts-ignore internal js that is passed to the browser as is import { takePercySnapshot, takePercySnapshotWithAgent } from './take_percy_snapshot'; +export const DEFAULT_OPTIONS = { + widths: [1200], +}; + export async function VisualTestingProvider({ getService }: FtrProviderContext) { const browser = getService('browser'); const log = getService('log'); const lifecycle = getService('lifecycle'); - const DEFAULT_OPTIONS = { - widths: [1200], - }; - let currentTest: Test | undefined; lifecycle.on('beforeEachTest', (test: Test) => { currentTest = test; diff --git a/test/visual_regression/tests/discover/chart_visualization.js b/test/visual_regression/tests/discover/chart_visualization.js new file mode 100644 index 00000000000000..ca3def70627d9a --- /dev/null +++ b/test/visual_regression/tests/discover/chart_visualization.js @@ -0,0 +1,123 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; + +export default function ({ getService, getPageObjects }) { + const log = getService('log'); + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); + const visualTesting = getService('visualTesting'); + const defaultSettings = { + defaultIndex: 'logstash-*', + }; + + describe('discover', function describeIndexTests() { + const fromTime = '2015-09-19 06:31:44.000'; + const toTime = '2015-09-23 18:31:44.000'; + + before(async function () { + log.debug('load kibana index with default index pattern'); + await esArchiver.load('discover'); + + // and load a set of makelogs data + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace(defaultSettings); + log.debug('discover'); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + }); + + describe('query', function () { + this.tags(['skipFirefox']); + + it('should show bars in the correct time zone', async function () { + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await visualTesting.snapshot(); + }); + + it('should show correct data for chart interval Hourly', async function () { + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await PageObjects.discover.setChartInterval('Hourly'); + await visualTesting.snapshot(); + }); + + it('should show correct data for chart interval Daily', async function () { + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await PageObjects.discover.setChartInterval('Daily'); + await retry.try(async () => { + await visualTesting.snapshot(); + }); + }); + + it('should show correct data for chart interval Weekly', async function () { + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await PageObjects.discover.setChartInterval('Weekly'); + await retry.try(async () => { + await visualTesting.snapshot(); + }); + }); + + it('browser back button should show previous interval Daily', async function () { + await browser.goBack(); + await retry.try(async function tryingForTime() { + const actualInterval = await PageObjects.discover.getChartInterval(); + expect(actualInterval).to.be('Daily'); + }); + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await visualTesting.snapshot(); + }); + + it('should show correct data for chart interval Monthly', async function () { + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await PageObjects.discover.setChartInterval('Monthly'); + await visualTesting.snapshot(); + }); + + it('should show correct data for chart interval Yearly', async function () { + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await PageObjects.discover.setChartInterval('Yearly'); + await visualTesting.snapshot(); + }); + + it('should show correct data for chart interval Auto', async function () { + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await PageObjects.discover.setChartInterval('Auto'); + await visualTesting.snapshot(); + }); + }); + + describe('time zone switch', () => { + it('should show bars in the correct time zone after switching', async function () { + await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'America/Phoenix' }); + await browser.refresh(); + await PageObjects.header.awaitKibanaChrome(); + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + await retry.try(async function () { + await visualTesting.snapshot(); + }); + }); + + }); + }); +} diff --git a/test/visual_regression/tests/discover/index.js b/test/visual_regression/tests/discover/index.js new file mode 100644 index 00000000000000..ba44fb12eef703 --- /dev/null +++ b/test/visual_regression/tests/discover/index.js @@ -0,0 +1,42 @@ +/* +* Licensed to Elasticsearch B.V. under one or more contributor +* license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright +* ownership. Elasticsearch B.V. licenses this file to you under +* the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import { DEFAULT_OPTIONS } from '../../services/visual_testing/visual_testing'; + +// Width must be the same as visual_testing or canvas image widths will get skewed +const [SCREEN_WIDTH] = DEFAULT_OPTIONS.widths || []; + +export default function ({ getService, loadTestFile }) { + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + + describe('discover app', function () { + this.tags('ciGroup6'); + + before(function () { + return browser.setWindowSize(SCREEN_WIDTH, 1000); + }); + + after(function unloadMakelogs() { + return esArchiver.unload('logstash_functional'); + }); + + loadTestFile(require.resolve('./chart_visualization')); + }); +} diff --git a/x-pack/legacy/plugins/canvas/i18n/components.ts b/x-pack/legacy/plugins/canvas/i18n/components.ts index a076c878d6cc55..1781d46e567825 100644 --- a/x-pack/legacy/plugins/canvas/i18n/components.ts +++ b/x-pack/legacy/plugins/canvas/i18n/components.ts @@ -12,5 +12,113 @@ export const ComponentStrings = { i18n.translate('xpack.canvas.embedObject.noMatchingObjectsMessage', { defaultMessage: 'No matching objects found.', }), + getTitleText: () => + i18n.translate('xpack.canvas.embedObject.titleText', { + defaultMessage: 'Embed Object', + }), + }, + Asset: { + getCopyAssetTooltipText: () => + i18n.translate('xpack.canvas.asset.copyAssetTooltipText', { + defaultMessage: 'Copy id to clipboard', + }), + getCreateImageTooltipText: () => + i18n.translate('xpack.canvas.asset.createImageTooltipText', { + defaultMessage: 'Create image element', + }), + getDeleteAssetTooltipText: () => + i18n.translate('xpack.canvas.asset.deleteAssetTooltipText', { + defaultMessage: 'Delete', + }), + getDownloadAssetTooltipText: () => + i18n.translate('xpack.canvas.asset.downloadAssetTooltipText', { + defaultMessage: 'Download', + }), + getThumbnailAltText: () => + i18n.translate('xpack.canvas.asset.thumbnailAltText', { + defaultMessage: 'Asset thumbnail', + }), + }, + AssetManager: { + getBtnText: () => + i18n.translate('xpack.canvas.assetManager.buttonText', { + defaultMessage: 'Manage assets', + }), + getConfirmModalBtnText: () => + i18n.translate('xpack.canvas.assetManager.confirmModalButtonText', { + defaultMessage: 'Remove', + }), + getConfirmModalMessageText: () => + i18n.translate('xpack.canvas.assetManager.confirmModalMessage', { + defaultMessage: 'Are you sure you want to remove this asset?', + }), + getConfirmModalTitleText: () => + i18n.translate('xpack.canvas.assetManager.confirmModalTitleText', { + defaultMessage: 'Remove Asset', + }), + }, + AssetModal: { + getDescriptionText: () => + i18n.translate('xpack.canvas.assetModal.descriptionText', { + defaultMessage: + 'Below are the image assets in this workpad. Any assets that are currently in use cannot be determined at this time. To reclaim space, delete assets.', + }), + getEmptyAssetsMessageText: () => + i18n.translate('xpack.canvas.assetModal.emptyAssetsMessage', { + defaultMessage: 'Import your assets to get started', + }), + getFilePickerPromptText: () => + i18n.translate('xpack.canvas.assetModal.filePickerPromptText', { + defaultMessage: 'Select or drag and drop images', + }), + getLoadingText: () => + i18n.translate('xpack.canvas.assetModal.loadingText', { + defaultMessage: 'Uploading images', + }), + getModalCloseBtnText: () => + i18n.translate('xpack.canvas.assetModal.modalCloseButtonText', { + defaultMessage: 'Close', + }), + getModalTitleText: () => + i18n.translate('xpack.canvas.assetModal.modalTitleText', { + defaultMessage: 'Manage workpad assets', + }), + getSpaceUsedText: (percentageUsed: number) => + i18n.translate('xpack.canvas.assetModal.spacedUsedText', { + defaultMessage: '{percentageUsed}% space used', + values: { + percentageUsed, + }, + }), + }, + WorkpadHeader: { + getAddElementBtnText: () => + i18n.translate('xpack.canvas.workpadHeader.addElementButtonText', { + defaultMessage: 'Add element', + }), + getAddElementModalCloseBtnText: () => + i18n.translate('xpack.canvas.workpadHeader.addElementModalCloseButtonText', { + defaultMessage: 'Close', + }), + getEmbedObjectBtnText: () => + i18n.translate('xpack.canvas.workpadHeader.embedObjectButtonText', { + defaultMessage: 'Embed object', + }), + getFullScreenTooltipText: () => + i18n.translate('xpack.canvas.workpadHeader.fullscreenTooltipText', { + defaultMessage: 'Enter fullscreen mode', + }), + getHideEditControlText: () => + i18n.translate('xpack.canvas.workpadHeader.hideEditControlText', { + defaultMessage: 'Hide editing controls', + }), + getNoWritePermText: () => + i18n.translate('xpack.canvas.workpadHeader.noWritePermissionText', { + defaultMessage: "You don't have permission to edit this workpad", + }), + getShowEditControlText: () => + i18n.translate('xpack.canvas.workpadHeader.showEditControlText', { + defaultMessage: 'Show editing controls', + }), }, }; diff --git a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset.tsx b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset.tsx index 29022bc5771b94..1739c466f18134 100644 --- a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset.tsx @@ -16,10 +16,15 @@ import { EuiToolTip, } from '@elastic/eui'; import React, { FunctionComponent } from 'react'; + +import { ComponentStrings } from '../../../i18n'; + import { Clipboard } from '../clipboard'; import { Download } from '../download'; import { AssetType } from '../../../types'; +const { Asset: strings } = ComponentStrings; + interface Props { /** The asset to be rendered */ asset: AssetType; @@ -36,10 +41,10 @@ export const Asset: FunctionComponent = props => { const createImage = ( - + onCreate(asset)} /> @@ -48,9 +53,9 @@ export const Asset: FunctionComponent = props => { const downloadAsset = ( - + - + @@ -58,9 +63,9 @@ export const Asset: FunctionComponent = props => { const copyAsset = ( - + result && onCopy(asset)}> - + @@ -68,11 +73,11 @@ export const Asset: FunctionComponent = props => { const deleteAsset = ( - + onDelete(asset)} /> @@ -86,7 +91,7 @@ export const Asset: FunctionComponent = props => { size="original" url={props.asset.value} fullScreenIconColor="dark" - alt="Asset thumbnail" + alt={strings.getThumbnailAltText()} style={{ backgroundImage: `url(${props.asset.value})` }} />
diff --git a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_manager.tsx b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_manager.tsx index d603f36da40777..e203400bb68a65 100644 --- a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_manager.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_manager.tsx @@ -13,10 +13,15 @@ import { } from '@elastic/eui'; import PropTypes from 'prop-types'; import React, { Fragment, PureComponent } from 'react'; + +import { ComponentStrings } from '../../../i18n'; + import { ConfirmModal } from '../confirm_modal'; import { AssetType } from '../../../types'; import { AssetModal } from './asset_modal'; +const { AssetManager: strings } = ComponentStrings; + interface Props { /** A list of assets, if available */ assetValues: AssetType[]; @@ -80,9 +85,9 @@ export class AssetManager extends PureComponent { const confirmModal = ( @@ -90,7 +95,7 @@ export class AssetManager extends PureComponent { return ( - Manage assets + {strings.getBtnText()} {isModalVisible ? assetModal : null} {confirmModal} diff --git a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_modal.tsx b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_modal.tsx index 0134b5bee76ae1..adc0f348093461 100644 --- a/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_modal.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/asset_manager/asset_modal.tsx @@ -24,12 +24,17 @@ import { } from '@elastic/eui'; import PropTypes from 'prop-types'; import React, { FunctionComponent } from 'react'; + +import { ComponentStrings } from '../../../i18n'; + // @ts-ignore import { ASSET_MAX_SIZE } from '../../../common/lib/constants'; import { Loading } from '../loading'; import { Asset } from './asset'; import { AssetType } from '../../../types'; +const { AssetModal: strings } = ComponentStrings; + interface Props { /** The assets to display within the modal */ assetValues: AssetType[]; @@ -68,7 +73,7 @@ export const AssetModal: FunctionComponent = props => { Import your assets to get started} + title={

{strings.getEmptyAssetsMessageText()}

} titleSize="xs" />
@@ -83,15 +88,15 @@ export const AssetModal: FunctionComponent = props => { > - Manage workpad assets + {strings.getModalTitleText()} {isLoading ? ( - + ) : ( = props => { -

- Below are the image assets in this workpad. Any assets that are currently in use - cannot be determined at this time. To reclaim space, delete assets. -

+

{strings.getDescriptionText()}

{assetValues.length ? ( @@ -137,11 +139,13 @@ export const AssetModal: FunctionComponent = props => { /> - {percentageUsed}% space used + + {strings.getSpaceUsedText(percentageUsed)} + - Close + {strings.getModalCloseBtnText()} diff --git a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx index 52a91a3a2fae6b..0f03657e970bbf 100644 --- a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/flyout.tsx @@ -6,7 +6,6 @@ import React from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; import { SavedObjectFinder, SavedObjectMetaData, @@ -58,9 +57,7 @@ export class AddEmbeddableFlyout extends React.Component { -

- -

+

{strings.getTitleText()}

diff --git a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx index 3bf65ceac7525d..8a040c0c90c83e 100644 --- a/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/embeddable_flyout/index.tsx @@ -104,7 +104,7 @@ export class EmbeddableFlyoutPortal extends React.Component { } } -export const AddEmbeddablePanel = compose( +export const AddEmbeddablePanel = compose void }>( connect( mapStateToProps, mapDispatchToProps, diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/index.js b/x-pack/legacy/plugins/canvas/public/components/workpad_header/index.tsx similarity index 53% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/index.js rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/index.tsx index 79ec7dbbbe2469..6d3c9a0640ba2c 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/index.tsx @@ -5,22 +5,41 @@ */ import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +// @ts-ignore untyped local import { canUserWrite } from '../../state/selectors/app'; +// @ts-ignore untyped local import { getSelectedPage, isWriteable } from '../../state/selectors/workpad'; +// @ts-ignore untyped local import { setWriteable } from '../../state/actions/workpad'; -import { WorkpadHeader as Component } from './workpad_header'; +import { State } from '../../../types'; +import { WorkpadHeader as Component, Props as ComponentProps } from './workpad_header'; -const mapStateToProps = state => ({ +interface StateProps { + isWriteable: boolean; + canUserWrite: boolean; + selectedPage: string; +} + +interface DispatchProps { + setWriteable: (isWorkpadWriteable: boolean) => void; +} + +const mapStateToProps = (state: State): StateProps => ({ isWriteable: isWriteable(state) && canUserWrite(state), canUserWrite: canUserWrite(state), selectedPage: getSelectedPage(state), }); -const mapDispatchToProps = dispatch => ({ - setWriteable: isWriteable => dispatch(setWriteable(isWriteable)), +const mapDispatchToProps = (dispatch: Dispatch) => ({ + setWriteable: (isWorkpadWriteable: boolean) => dispatch(setWriteable(isWorkpadWriteable)), }); -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ +const mergeProps = ( + stateProps: StateProps, + dispatchProps: DispatchProps, + ownProps: ComponentProps +): ComponentProps => ({ ...stateProps, ...dispatchProps, ...ownProps, diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts index e30adaf6f00e1e..c430ef6dd54d24 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts @@ -43,7 +43,7 @@ interface Props { enabled: boolean; } -export const WorkpadExport = compose( +export const WorkpadExport = compose( connect(mapStateToProps), withProps( ({ workpad, pageCount, enabled }: Props): ComponentProps => ({ diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.js b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx similarity index 79% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.js rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx index a3d2811c6c1be6..8a19984234b446 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.js +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx @@ -6,6 +6,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +// @ts-ignore no @types definition import { Shortcuts } from 'react-shortcuts'; import { EuiFlexItem, @@ -18,17 +19,39 @@ import { EuiModalFooter, EuiToolTip, } from '@elastic/eui'; + +import { ComponentStrings } from '../../../i18n'; + +// @ts-ignore untyped local import { AssetManager } from '../asset_manager'; +// @ts-ignore untyped local import { ElementTypes } from '../element_types'; import { ToolTipShortcut } from '../tool_tip_shortcut/'; import { AddEmbeddablePanel } from '../embeddable_flyout'; +// @ts-ignore untyped local import { ControlSettings } from './control_settings'; +// @ts-ignore untyped local import { RefreshControl } from './refresh_control'; +// @ts-ignore untyped local import { FullscreenControl } from './fullscreen_control'; import { WorkpadExport } from './workpad_export'; import { WorkpadZoom } from './workpad_zoom'; -export class WorkpadHeader extends React.PureComponent { +const { WorkpadHeader: strings } = ComponentStrings; + +export interface Props { + isWriteable: boolean; + toggleWriteable: () => void; + canUserWrite: boolean; + selectedPage: string; +} + +interface State { + isModalVisible: boolean; + isPanelVisible: boolean; +} + +export class WorkpadHeader extends React.PureComponent { static propTypes = { isWriteable: PropTypes.bool, toggleWriteable: PropTypes.func, @@ -36,12 +59,13 @@ export class WorkpadHeader extends React.PureComponent { state = { isModalVisible: false, isPanelVisible: false }; - _fullscreenButton = ({ toggleFullscreen }) => ( + _fullscreenButton = ({ toggleFullscreen }: { toggleFullscreen: () => void }) => ( - Enter fullscreen mode + {strings.getFullScreenTooltipText()}{' '} + } > @@ -53,7 +77,7 @@ export class WorkpadHeader extends React.PureComponent { ); - _keyHandler = action => { + _keyHandler = (action: string) => { if (action === 'EDITING') { this.props.toggleWriteable(); } @@ -76,7 +100,7 @@ export class WorkpadHeader extends React.PureComponent { - Close + {strings.getAddElementModalCloseBtnText()} @@ -85,12 +109,20 @@ export class WorkpadHeader extends React.PureComponent { _embeddableAdd = () => ; - _getEditToggleToolTip = ({ textOnly } = { textOnly: false }) => { + _getEditToggleToolTipText = () => { if (!this.props.canUserWrite) { - return "You don't have permission to edit this workpad"; + return strings.getNoWritePermText(); } - const content = this.props.isWriteable ? `Hide editing controls` : `Show editing controls`; + const content = this.props.isWriteable + ? strings.getHideEditControlText() + : strings.getShowEditControlText(); + + return content; + }; + + _getEditToggleToolTip = ({ textOnly } = { textOnly: false }) => { + const content = this._getEditToggleToolTipText(); if (textOnly) { return content; @@ -149,7 +181,7 @@ export class WorkpadHeader extends React.PureComponent { iconType={isWriteable ? 'lockOpen' : 'lock'} onClick={toggleWriteable} size="s" - aria-label={this._getEditToggleToolTip({ textOnly: true })} + aria-label={this._getEditToggleToolTipText()} isDisabled={!canUserWrite} /> @@ -163,7 +195,9 @@ export class WorkpadHeader extends React.PureComponent { - Embed object + + {strings.getEmbedObjectBtnText()} + - Add element + {strings.getAddElementBtnText()} diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx index bb5055c5429146..406d6b54729b90 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_zoom/index.tsx @@ -32,7 +32,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ setZoomScale: (scale: number) => dispatch(setZoomScale(scale)), }); -export const WorkpadZoom = compose( +export const WorkpadZoom = compose( connect( mapStateToProps, mapDispatchToProps diff --git a/x-pack/legacy/plugins/code/model/repository.ts b/x-pack/legacy/plugins/code/model/repository.ts index ec2777caf8584e..c4469a7d593d72 100644 --- a/x-pack/legacy/plugins/code/model/repository.ts +++ b/x-pack/legacy/plugins/code/model/repository.ts @@ -171,6 +171,7 @@ export interface IndexWorkerProgress extends WorkerProgress { export enum RepoState { CLONING, + UPDATING, DELETING, INDEXING, READY, diff --git a/x-pack/legacy/plugins/code/public/components/admin_page/project_item.tsx b/x-pack/legacy/plugins/code/public/components/admin_page/project_item.tsx index 48a6407b911c04..b3a2c49c08f516 100644 --- a/x-pack/legacy/plugins/code/public/components/admin_page/project_item.tsx +++ b/x-pack/legacy/plugins/code/public/components/admin_page/project_item.tsx @@ -30,6 +30,7 @@ import { RepoStatus } from '../../actions/status'; const stateColor = { [RepoState.CLONING]: 'secondary', + [RepoState.UPDATING]: 'secondary', [RepoState.DELETING]: 'accent', [RepoState.INDEXING]: 'primary', }; @@ -126,6 +127,12 @@ class CodeProjectItem extends React.PureComponent< ); + } else if (status.state === RepoState.UPDATING) { + footer = ( +
+ +
+ ); } else if (status.state === RepoState.DELETE_ERROR) { footer = (
diff --git a/x-pack/legacy/plugins/code/public/reducers/search.ts b/x-pack/legacy/plugins/code/public/reducers/search.ts index 182f1905d2345c..5092e611215b8d 100644 --- a/x-pack/legacy/plugins/code/public/reducers/search.ts +++ b/x-pack/legacy/plugins/code/public/reducers/search.ts @@ -156,7 +156,7 @@ export const search = handleActions( took, stats: { total, - from: from! + 1, + from: from as number, to: from! + results!.length, page: page!, totalPage: totalPage!, diff --git a/x-pack/legacy/plugins/code/public/reducers/status.ts b/x-pack/legacy/plugins/code/public/reducers/status.ts index fe9e58ffcc488c..4e57d5e7e7565c 100644 --- a/x-pack/legacy/plugins/code/public/reducers/status.ts +++ b/x-pack/legacy/plugins/code/public/reducers/status.ts @@ -71,6 +71,9 @@ const getGitState = (gitStatus: CloneWorkerProgress) => { ) { return RepoState.CLONE_ERROR; } else if (progress < WorkerReservedProgress.COMPLETED) { + if (gitStatus.cloneProgress && gitStatus.cloneProgress.isCloned) { + return RepoState.UPDATING; + } return RepoState.CLONING; } else if (progress === WorkerReservedProgress.COMPLETED) { return RepoState.READY; diff --git a/x-pack/legacy/plugins/index_management/public/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js b/x-pack/legacy/plugins/index_management/public/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js index 551c205c27d702..9be40a905d8422 100644 --- a/x-pack/legacy/plugins/index_management/public/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js +++ b/x-pack/legacy/plugins/index_management/public/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js @@ -9,6 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { all } from 'lodash'; import { + EuiBadge, EuiButton, EuiCallOut, EuiContextMenu, @@ -431,10 +432,12 @@ export class IndexActionsContextMenu extends Component { {isSystemIndexByName[indexName] ? ( {' '} - + + + ) : ''} @@ -457,7 +460,7 @@ export class IndexActionsContextMenu extends Component {

{' '} - + + + ) : ''} diff --git a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js b/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js index 0799e59bbb0113..8a103a554f7f2c 100644 --- a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js +++ b/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { setup } from '../../../../../src/legacy/core_plugins/visualizations/public/np_ready/public/legacy'; +import { setup as visualizationsSetup } from '../../../../../src/legacy/core_plugins/visualizations/public/legacy'; import { i18n } from '@kbn/i18n'; import { APP_ID, APP_ICON, MAP_BASE_URL } from '../common/constants'; -setup.types.visTypeAliasRegistry.add({ +visualizationsSetup.types.visTypeAliasRegistry.add({ aliasUrl: MAP_BASE_URL, name: APP_ID, title: i18n.translate('xpack.maps.visTypeAlias.title', { diff --git a/x-pack/legacy/plugins/ml/public/data_frame/common/index.ts b/x-pack/legacy/plugins/ml/public/data_frame/common/index.ts index 8b33eb666c7386..7af8c7ee73cff9 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame/common/index.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame/common/index.ts @@ -37,7 +37,7 @@ export { DATA_FRAME_MODE, DATA_FRAME_TRANSFORM_STATE, } from './transform_stats'; -export { moveToDataFrameTransformList, moveToDataFrameWizard, moveToDiscover } from './navigation'; +export { moveToDataFrameWizard, getDiscoverUrl } from './navigation'; export { getEsAggFromAggConfig, isPivotAggsConfigWithUiSupport, diff --git a/x-pack/legacy/plugins/ml/public/data_frame/common/navigation.test.ts b/x-pack/legacy/plugins/ml/public/data_frame/common/navigation.test.ts new file mode 100644 index 00000000000000..97cb5b4d5d2367 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/data_frame/common/navigation.test.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. + */ + +import { getDiscoverUrl } from './navigation'; + +describe('navigation', () => { + test('getDiscoverUrl should provide encoded url to Discover page', () => { + expect(getDiscoverUrl('farequote-airline', 'http://example.com')).toBe( + 'http://example.com#/discover?_g=()&_a=(index:farequote-airline)' + ); + }); +}); diff --git a/x-pack/legacy/plugins/ml/public/data_frame/common/navigation.ts b/x-pack/legacy/plugins/ml/public/data_frame/common/navigation.ts index 746193f1e1262a..e8726e8c2eedd7 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame/common/navigation.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame/common/navigation.ts @@ -10,11 +10,12 @@ export function moveToDataFrameWizard() { window.location.href = '#/data_frames/new_transform'; } -export function moveToDataFrameTransformList() { - window.location.href = '#/data_frames'; -} - -export function moveToDiscover(indexPatternId: string, baseUrl: string) { +/** + * Gets a url for navigating to Discover page. + * @param indexPatternId Index pattern id. + * @param baseUrl Base url. + */ +export function getDiscoverUrl(indexPatternId: string, baseUrl: string): string { const _g = rison.encode({}); // Add the index pattern ID to the appState part of the URL. @@ -24,5 +25,5 @@ export function moveToDiscover(indexPatternId: string, baseUrl: string) { const hash = `#/discover?_g=${_g}&_a=${_a}`; - window.location.href = `${baseUrl}${hash}`; + return `${baseUrl}${hash}`; } diff --git a/x-pack/legacy/plugins/ml/public/data_frame/pages/data_frame_new_pivot/components/step_create/step_create_form.tsx b/x-pack/legacy/plugins/ml/public/data_frame/pages/data_frame_new_pivot/components/step_create/step_create_form.tsx index 1ce9f5749fc2f3..7c13d0addece9f 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame/pages/data_frame_new_pivot/components/step_create/step_create_form.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame/pages/data_frame_new_pivot/components/step_create/step_create_form.tsx @@ -34,11 +34,7 @@ import { useKibanaContext } from '../../../../../contexts/kibana/use_kibana_cont import { useUiChromeContext } from '../../../../../contexts/ui/use_ui_chrome_context'; import { PROGRESS_JOBS_REFRESH_INTERVAL_MS } from '../../../../../../common/constants/jobs_list'; -import { - getTransformProgress, - moveToDataFrameTransformList, - moveToDiscover, -} from '../../../../common'; +import { getTransformProgress, getDiscoverUrl } from '../../../../common'; export interface StepDetailsExposedState { created: boolean; @@ -367,7 +363,7 @@ export const StepCreateForm: SFC = React.memo( defaultMessage: 'Return to the data frame transform management page.', } )} - onClick={moveToDataFrameTransformList} + href="#/data_frames" /> {started === true && createIndexPattern === true && indexPatternId === undefined && ( @@ -400,7 +396,7 @@ export const StepCreateForm: SFC = React.memo( defaultMessage: 'Use Discover to explore the data frame pivot.', } )} - onClick={() => moveToDiscover(indexPatternId, baseUrl)} + href={getDiscoverUrl(indexPatternId, baseUrl)} /> )} diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js b/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js index 3d2c7c3b0ae3c6..b4c46f3539640a 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js @@ -474,7 +474,8 @@ describe('CSV Execute Job', function () { }); }); - describe('cancellation', function () { + // FLAKY: https://github.com/elastic/kibana/issues/43069 + describe.skip('cancellation', function () { const scrollId = getRandomScrollId(); beforeEach(function () { diff --git a/x-pack/legacy/plugins/siem/cypress/README.md b/x-pack/legacy/plugins/siem/cypress/README.md index 996d23ab5fab28..d3313ef64e4490 100644 --- a/x-pack/legacy/plugins/siem/cypress/README.md +++ b/x-pack/legacy/plugins/siem/cypress/README.md @@ -201,7 +201,7 @@ CYPRESS_baseUrl=http://localhost:5601 CYPRESS_ELASTICSEARCH_USERNAME=elastic CYP When Cypress tests are run on the command line via `yarn cypress:run`, reporting artifacts are generated under the `target` directory in the root -of the Kibana, as detailed for each artifact type in the sections bleow. +of the Kibana, as detailed for each artifact type in the sections below. ### HTML Reports diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts new file mode 100644 index 00000000000000..d261832a92f2a9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts @@ -0,0 +1,76 @@ +/* + * 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. + */ + +/* + * These links are for different test scenarios that try and capture different drill downs into + * ml-network and ml-hosts and are of the flavor of testing: + * A filter being null: (filterQuery:!n) + * A filter being set with single values: filterQuery:(expression:%27process.name%20:%20%22conhost.exe%22%27,kind:kuery) + * A filter being set with multiple values: filterQuery:(expression:%27process.name%20:%20%22conhost.exe,sc.exe%22%27,kind:kuery) + * A filter containing variables not replaced: filterQuery:(expression:%27process.name%20:%20%$process.name$%22%27,kind:kuery) + * + * In different combination with: + * network not being set: $ip$ + * host not being set: $host.name$ + * ...or... + * network being set normally: 127.0.0.1 + * host being set normally: suricata-iowa + * ...or... + * network having multiple values: 127.0.0.1,127.0.0.2 + * host having multiple values: suricata-iowa,siem-windows + */ + +// Single IP with a null for the filterQuery: +export const mlNetworkSingleIpNullFilterQuery = + "/app/siem#/ml-network/ip/127.0.0.1?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// Single IP with a value for the filterQuery: +export const mlNetworkSingleIpFilterQuery = + "/app/siem#/ml-network/ip/127.0.0.1?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// Multiple IPs with a null for the filterQuery: +export const mlNetworkMultipleIpNullFilterQuery = + "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// Multiple IPs with a value for the filterQuery: +export const mlNetworkMultipleIpFilterQuery = + "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// $ip$ with a null filterQuery: +export const mlNetworkNullFilterQuery = + "/app/siem#/ml-network/ip/$ip$?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// $ip$ with a value for the filterQuery: +export const mlNetworkFilterQuery = + "/app/siem#/ml-network/ip/$ip$?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// Single host name with a null for the filterQuery: +export const mlHostSingleHostNullFilterQuery = + "/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Single host name with a variable in the filterQuery +export const mlHostSingleHostFilterQueryVariable = + "/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Single host name with a value for filterQuery: +export const mlHostSingleHostFilterQuery = + "/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Multiple host names with null for filterQuery +export const mlHostMultiHostNullFilterQuery = + "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Multiple host names with a value for filterQuery +export const mlHostMultiHostFilterQuery = + "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Undefined/null host name with a null for the KQL: +export const mlHostVariableHostNullFilterQuery = + "/app/siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Undefined/null host name but with a value for filterQuery +export const mlHostVariableHostFilterQuery = + "/app/siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts new file mode 100644 index 00000000000000..a178f6364d54cf --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts @@ -0,0 +1,206 @@ +/* + * 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 { logout } from '../../lib/logout'; +import { + mlNetworkSingleIpNullFilterQuery, + mlNetworkSingleIpFilterQuery, + mlNetworkMultipleIpNullFilterQuery, + mlNetworkMultipleIpFilterQuery, + mlNetworkNullFilterQuery, + mlNetworkFilterQuery, + mlHostSingleHostNullFilterQuery, + mlHostSingleHostFilterQueryVariable, + mlHostSingleHostFilterQuery, + mlHostMultiHostNullFilterQuery, + mlHostMultiHostFilterQuery, + mlHostVariableHostNullFilterQuery, + mlHostVariableHostFilterQuery, +} from '../../lib/ml_conditional_links'; +import { loginAndWaitForPage } from '../../lib/util/helpers'; +import { KQL_INPUT } from '../../lib/url_state'; + +describe('ml conditional links', () => { + afterEach(() => { + return logout(); + }); + + it('sets the KQL from a single IP with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkSingleIpFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(process.name: "conhost.exe" or process.name: "sc.exe")' + ); + }); + + it('sets the KQL from a multiple IPs with a null for the filterQuery', () => { + loginAndWaitForPage(mlNetworkMultipleIpNullFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2"))' + ); + }); + + it('sets the KQL from a multiple IPs with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkMultipleIpFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2")) and ((process.name: "conhost.exe" or process.name: "sc.exe"))' + ); + }); + + it('sets the KQL from a $ip$ with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(process.name: "conhost.exe" or process.name: "sc.exe")' + ); + }); + + it('sets the KQL from a single host name with a value for filterQuery', () => { + loginAndWaitForPage(mlHostSingleHostFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(process.name: "conhost.exe" or process.name: "sc.exe")' + ); + }); + + it('sets the KQL from a multiple host names with null for filterQuery', () => { + loginAndWaitForPage(mlHostMultiHostNullFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(host.name: "siem-windows" or host.name: "siem-suricata")' + ); + }); + + it('sets the KQL from a multiple host names with a value for filterQuery', () => { + loginAndWaitForPage(mlHostMultiHostFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(host.name: "siem-windows" or host.name: "siem-suricata") and ((process.name: "conhost.exe" or process.name: "sc.exe"))' + ); + }); + + it('sets the KQL from a undefined/null host name but with a value for filterQuery', () => { + loginAndWaitForPage(mlHostVariableHostFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(process.name: "conhost.exe" or process.name: "sc.exe")' + ); + }); + + it('redirects from a single IP with a null for the filterQuery', () => { + loginAndWaitForPage(mlNetworkSingleIpNullFilterQuery); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/network/ip/127.0.0.1?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' + ); + }); + + it('redirects from a single IP with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkSingleIpFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/network/ip/127.0.0.1?kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:network.details)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + ); + }); + + it('redirects from a multiple IPs with a null for the filterQuery', () => { + loginAndWaitForPage(mlNetworkMultipleIpNullFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/network?kqlQuery=(filterQuery:(expression:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))'),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + ); + }); + + it('redirects from a multiple IPs with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkMultipleIpFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/network?kqlQuery=(filterQuery:(expression:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + ); + }); + + it('redirects from a $ip$ with a null filterQuery', () => { + loginAndWaitForPage(mlNetworkNullFilterQuery); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' + ); + }); + + it('redirects from a $ip$ with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/network?kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + ); + }); + + it('redirects from a single host name with a null for the filterQuery', () => { + loginAndWaitForPage(mlHostSingleHostNullFilterQuery); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/hosts/siem-windows/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + ); + }); + + it('redirects from a host name with a variable in the filterQuery', () => { + loginAndWaitForPage(mlHostSingleHostFilterQueryVariable); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/hosts/siem-windows/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + ); + }); + + it('redirects from a single host name with a value for filterQuery', () => { + loginAndWaitForPage(mlHostSingleHostFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/hosts/siem-windows/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:hosts.details)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + ); + }); + + it('redirects from a multiple host names with null for filterQuery', () => { + loginAndWaitForPage(mlHostMultiHostNullFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/hosts/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)'),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + ); + }); + + it('redirects from a multiple host names with a value for filterQuery', () => { + loginAndWaitForPage(mlHostMultiHostFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/hosts/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + ); + }); + + it('redirects from a undefined/null host name with a null for the KQL', () => { + loginAndWaitForPage(mlHostVariableHostNullFilterQuery); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/hosts/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + ); + }); + + it('redirects from a undefined/null host name but with a value for filterQuery', () => { + loginAndWaitForPage(mlHostVariableHostFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/hosts/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + ); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts index c039cc2f88646e..bab39a437b0df7 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts @@ -17,6 +17,7 @@ describe('add_entities_to_kql', () => { afterAll(() => { console.log = originalError; }); + describe('#entityToKql', () => { test('returns empty string with no entity names defined and an empty entity string', () => { const entity = entityToKql([], ''); @@ -165,5 +166,10 @@ describe('add_entities_to_kql', () => { '(filterQuery:(expression:\'(host.name: "host-name-1" or host.name: "host-name-2") and (process.name : "")\',kind:kuery))' ); }); + + test('returns kql expression with a null filterQuery', () => { + const entity = addEntitiesToKql(['host.name'], ['host-1'], '(filterQuery:!n)'); + expect(entity).toEqual('(filterQuery:(expression:\'(host.name: "host-1")\'))'); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts index c6c7704aa09994..490bb8fd324c30 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts @@ -52,6 +52,10 @@ export const addEntitiesToKql = ( } return encode(value); } + } else if (value.filterQuery == null) { + const entitiesKql = entitiesToKql(entityNames, entities); + value.filterQuery = { expression: `(${entitiesKql})` }; + return encode(value); } } return kqlQuery; diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx index eec33a657c6ee2..f2e60a5fb86e9c 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -6,31 +6,27 @@ import React from 'react'; -import { match as RouteMatch, Redirect, Route, Switch } from 'react-router-dom'; +import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { QueryString } from 'ui/utils/query_string'; -import { pure } from 'recompose'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, multipleEntities, getMultipleEntities } from './entity_helpers'; import { replaceKqlQueryLocationForHostPage } from './replace_kql_query_location_for_host_page'; -interface MlHostConditionalProps { - match: RouteMatch<{}>; - location: Location; -} - interface QueryStringType { '?_g': string; kqlQuery: string | null; timerange: string | null; } -export const MlHostConditionalContainer = pure(({ match }) => ( +type MlHostConditionalProps = Partial> & { url: string }; + +export const MlHostConditionalContainer = React.memo(({ url }) => ( { const queryStringDecoded: QueryStringType = QueryString.decode( location.search.substring(1) @@ -43,7 +39,7 @@ export const MlHostConditionalContainer = pure(({ match }} /> (({ match ); } const reEncoded = QueryString.encode(queryStringDecoded); - return ; + return ; } else if (multipleEntities(hostName)) { const hosts: string[] = getMultipleEntities(hostName); if (queryStringDecoded.kqlQuery != null) { @@ -77,10 +73,10 @@ export const MlHostConditionalContainer = pure(({ match ); } const reEncoded = QueryString.encode(queryStringDecoded); - return ; + return ; } else { const reEncoded = QueryString.encode(queryStringDecoded); - return ; + return ; } }} /> diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx index 4089689fe9b689..200a00b6dd5626 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx @@ -6,31 +6,27 @@ import React from 'react'; -import { match as RouteMatch, Redirect, Route, Switch } from 'react-router-dom'; +import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { QueryString } from 'ui/utils/query_string'; -import { pure } from 'recompose'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, getMultipleEntities, multipleEntities } from './entity_helpers'; import { replaceKqlQueryLocationForNetworkPage } from './replace_kql_query_location_for_network_page'; -interface MlNetworkConditionalProps { - match: RouteMatch<{}>; - location: Location; -} - interface QueryStringType { '?_g': string; kqlQuery: string | null; timerange: string | null; } -export const MlNetworkConditionalContainer = pure(({ match }) => ( +type MlNetworkConditionalProps = Partial> & { url: string }; + +export const MlNetworkConditionalContainer = React.memo(({ url }) => ( { const queryStringDecoded: QueryStringType = QueryString.decode( location.search.substring(1) @@ -43,7 +39,7 @@ export const MlNetworkConditionalContainer = pure(({ }} /> ( /> } /> - - + ( + + )} + /> + ( + + )} + /> diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8d6c837d872835..0d890c29f2897e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1369,7 +1369,6 @@ "kbn.context.failedToLoadAnchorDocumentDescription": "別ののドキュメントの読み込みに失敗しました", "kbn.context.failedToLoadAnchorDocumentErrorDescription": "別のドキュメントの読み込みに失敗しました。", "kbn.context.loadingDescription": "読み込み中…", - "kbn.context.loadMoreDescription": "他 {defaultStepSize} を読み込む", "kbn.context.newerDocumentsDescription": "新しいドキュメント", "kbn.context.noSearchableTiebreakerFieldDescription": "インデックスパターン {indexPatternId} で検索可能なタイブレーカーフィールドが見つかりませんでした。高度な設定 {tieBreakerFields} tを変更してこのインデックスパターンの有効なフィールドを含めてください。", "kbn.context.olderDocumentsDescription": "古いドキュメント", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 868c6606cd1c2b..44a1009707d89a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1369,7 +1369,6 @@ "kbn.context.failedToLoadAnchorDocumentDescription": "无法加载该定位点文档", "kbn.context.failedToLoadAnchorDocumentErrorDescription": "无法加载定位点文档。", "kbn.context.loadingDescription": "正在加载……", - "kbn.context.loadMoreDescription": "再加载 {defaultStepSize} 个", "kbn.context.newerDocumentsDescription": "较新文档", "kbn.context.noSearchableTiebreakerFieldDescription": "索引模式 {indexPatternId} 中找不到任何可搜索的平分决胜字段。请更改高级设置“{tieBreakerFields}”以包括此索引模式的有效字段。", "kbn.context.olderDocumentsDescription": "较旧文档", @@ -4307,6 +4306,7 @@ "xpack.code.repoItem.cancelButtonText": "否,取消", "xpack.code.repoItem.cloneErrorText": "克隆错误", "xpack.code.repoItem.cloningText": "克隆中...", + "xpack.code.repoItem.updatingText": "更新中...", "xpack.code.repoItem.confirmButtonText": "是,继续", "xpack.code.repoItem.deleteButtonLabel": "删除", "xpack.code.repoItem.deleteConfirmTitle": "确认删除仓库?", diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts index 4c2dc3cbdf11ff..d3d5ca592ce639 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts @@ -19,7 +19,7 @@ export default function alertingApiIntegrationTests({ const esArchiver = getService('esArchiver'); describe('alerting api integration security and spaces enabled', function() { - this.tags('ciGroup8'); + this.tags('ciGroup3'); before(async () => { for (const space of Spaces) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts index dfbb2cca81a495..8c59e398186193 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts @@ -17,7 +17,7 @@ export default function alertingApiIntegrationTests({ const esArchiver = getService('esArchiver'); describe('alerting api integration spaces only', function() { - this.tags('ciGroup8'); + this.tags('ciGroup3'); before(async () => { for (const space of Object.values(Spaces)) { diff --git a/x-pack/test/api_integration/apis/index.js b/x-pack/test/api_integration/apis/index.js index 09186f4a605021..ffb3e1c64774f9 100644 --- a/x-pack/test/api_integration/apis/index.js +++ b/x-pack/test/api_integration/apis/index.js @@ -6,7 +6,7 @@ export default function ({ loadTestFile }) { describe('apis', function () { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./es')); loadTestFile(require.resolve('./security')); diff --git a/x-pack/test/api_integration/apis/security/index.js b/x-pack/test/api_integration/apis/security/index.js index 4d034622427fce..2174b578abff55 100644 --- a/x-pack/test/api_integration/apis/security/index.js +++ b/x-pack/test/api_integration/apis/security/index.js @@ -6,7 +6,7 @@ export default function ({ loadTestFile }) { describe('security', function () { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./basic_login')); loadTestFile(require.resolve('./builtin_es_privileges')); diff --git a/x-pack/test/api_integration/apis/spaces/index.ts b/x-pack/test/api_integration/apis/spaces/index.ts index adcf70d032e0f1..f3f96b891db07b 100644 --- a/x-pack/test/api_integration/apis/spaces/index.ts +++ b/x-pack/test/api_integration/apis/spaces/index.ts @@ -7,7 +7,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ loadTestFile }: FtrProviderContext) { describe('spaces', function() { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./saved_objects')); loadTestFile(require.resolve('./space_attributes')); diff --git a/x-pack/test/functional/apps/apm/index.ts b/x-pack/test/functional/apps/apm/index.ts index 945af09183f03d..977b6fca549c34 100644 --- a/x-pack/test/functional/apps/apm/index.ts +++ b/x-pack/test/functional/apps/apm/index.ts @@ -7,7 +7,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ loadTestFile }: FtrProviderContext) { describe('APM', function() { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./feature_controls')); }); } diff --git a/x-pack/test/functional/apps/dashboard_mode/index.js b/x-pack/test/functional/apps/dashboard_mode/index.js index 2d263834fc3112..5612fced7a25db 100644 --- a/x-pack/test/functional/apps/dashboard_mode/index.js +++ b/x-pack/test/functional/apps/dashboard_mode/index.js @@ -6,7 +6,7 @@ export default function ({ loadTestFile }) { describe('dashboard mode', function () { - this.tags('ciGroup7'); + this.tags('ciGroup2'); loadTestFile(require.resolve('./dashboard_view_mode')); }); diff --git a/x-pack/test/functional/apps/graph/graph.ts b/x-pack/test/functional/apps/graph/graph.ts index 6db62ea11d995e..97bb7c1b9918b3 100644 --- a/x-pack/test/functional/apps/graph/graph.ts +++ b/x-pack/test/functional/apps/graph/graph.ts @@ -13,7 +13,8 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const browser = getService('browser'); - describe('graph', function() { + // FLAKY: https://github.com/elastic/kibana/issues/45317 + describe.skip('graph', function() { before(async () => { await browser.setWindowSize(1600, 1000); log.debug('load graph/secrepo data'); diff --git a/x-pack/test/functional/apps/index_lifecycle_management/index.ts b/x-pack/test/functional/apps/index_lifecycle_management/index.ts index 9078a9d681e7e9..d85b1af2b26124 100644 --- a/x-pack/test/functional/apps/index_lifecycle_management/index.ts +++ b/x-pack/test/functional/apps/index_lifecycle_management/index.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Index Lifecycle Management app', function() { - this.tags('ciGroup7'); + this.tags('ciGroup2'); loadTestFile(require.resolve('./home_page')); }); }; diff --git a/x-pack/test/functional/apps/infra/index.ts b/x-pack/test/functional/apps/infra/index.ts index b706dc8cce5469..b534f6b69fe5b7 100644 --- a/x-pack/test/functional/apps/infra/index.ts +++ b/x-pack/test/functional/apps/infra/index.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('InfraOps app', function() { - this.tags('ciGroup7'); + this.tags('ciGroup2'); loadTestFile(require.resolve('./home_page')); loadTestFile(require.resolve('./feature_controls')); diff --git a/x-pack/test/functional/apps/license_management/index.ts b/x-pack/test/functional/apps/license_management/index.ts index 7524d00a4b8dd9..a41e4f5f4abd10 100644 --- a/x-pack/test/functional/apps/license_management/index.ts +++ b/x-pack/test/functional/apps/license_management/index.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('License app', function() { - this.tags('ciGroup7'); + this.tags('ciGroup2'); loadTestFile(require.resolve('./home_page')); }); }; diff --git a/x-pack/test/functional/apps/maps/index.js b/x-pack/test/functional/apps/maps/index.js index 9880fe41076d0a..76cccc9f5d21af 100644 --- a/x-pack/test/functional/apps/maps/index.js +++ b/x-pack/test/functional/apps/maps/index.js @@ -28,7 +28,7 @@ export default function ({ loadTestFile, getService }) { }); describe('', function () { - this.tags('ciGroup7'); + this.tags('ciGroup2'); loadTestFile(require.resolve('./documents_source')); loadTestFile(require.resolve('./saved_object_management')); loadTestFile(require.resolve('./sample_data')); @@ -38,7 +38,7 @@ export default function ({ loadTestFile, getService }) { }); describe('', function () { - this.tags('ciGroup10'); + this.tags('ciGroup5'); loadTestFile(require.resolve('./es_geo_grid_source')); loadTestFile(require.resolve('./joins')); loadTestFile(require.resolve('./add_layer_panel')); diff --git a/x-pack/test/functional/apps/uptime/index.ts b/x-pack/test/functional/apps/uptime/index.ts index c1bc8f856c467d..b5d3c73c96855b 100644 --- a/x-pack/test/functional/apps/uptime/index.ts +++ b/x-pack/test/functional/apps/uptime/index.ts @@ -18,7 +18,7 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'UTC' }); }); after(async () => await esArchiver.unload(ARCHIVE)); - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./overview')); diff --git a/x-pack/test/kerberos_api_integration/apis/index.ts b/x-pack/test/kerberos_api_integration/apis/index.ts index 00818c2b59eee7..6bb924818a6728 100644 --- a/x-pack/test/kerberos_api_integration/apis/index.ts +++ b/x-pack/test/kerberos_api_integration/apis/index.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function({ loadTestFile }: FtrProviderContext) { describe('apis Kerberos', function() { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./security')); }); } diff --git a/x-pack/test/oidc_api_integration/apis/authorization_code_flow/index.js b/x-pack/test/oidc_api_integration/apis/authorization_code_flow/index.js index 0ef60bb9298267..85f2a82cc96411 100644 --- a/x-pack/test/oidc_api_integration/apis/authorization_code_flow/index.js +++ b/x-pack/test/oidc_api_integration/apis/authorization_code_flow/index.js @@ -6,7 +6,7 @@ export default function ({ loadTestFile }) { describe('apis', function () { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./oidc_auth')); }); } diff --git a/x-pack/test/oidc_api_integration/apis/implicit_flow/index.ts b/x-pack/test/oidc_api_integration/apis/implicit_flow/index.ts index 22ce3b17a5949e..0503efea77eab1 100644 --- a/x-pack/test/oidc_api_integration/apis/implicit_flow/index.ts +++ b/x-pack/test/oidc_api_integration/apis/implicit_flow/index.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function({ loadTestFile }: FtrProviderContext) { describe('apis', function() { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./oidc_auth')); }); } diff --git a/x-pack/test/pki_api_integration/apis/index.ts b/x-pack/test/pki_api_integration/apis/index.ts index d859ed172ac691..47ffb25835d439 100644 --- a/x-pack/test/pki_api_integration/apis/index.ts +++ b/x-pack/test/pki_api_integration/apis/index.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function({ loadTestFile }: FtrProviderContext) { describe('apis PKI', function() { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./security')); }); } diff --git a/x-pack/test/reporting/functional/index.js b/x-pack/test/reporting/functional/index.js index fa473f454a9258..17aeb03eeadb5b 100644 --- a/x-pack/test/reporting/functional/index.js +++ b/x-pack/test/reporting/functional/index.js @@ -6,7 +6,7 @@ export default function ({ loadTestFile }) { describe('reporting app', function () { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./reporting')); }); } diff --git a/x-pack/test/saml_api_integration/apis/index.js b/x-pack/test/saml_api_integration/apis/index.js index ac08d2e078abfd..b4e6503f201e5d 100644 --- a/x-pack/test/saml_api_integration/apis/index.js +++ b/x-pack/test/saml_api_integration/apis/index.js @@ -6,7 +6,7 @@ export default function ({ loadTestFile }) { describe('apis SAML', function () { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./security')); }); } diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/index.ts b/x-pack/test/saved_object_api_integration/security_only/apis/index.ts index fadefd2743b30a..e24de7c7ae77f5 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/index.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/index.ts @@ -12,7 +12,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { const supertest = getService('supertest'); describe('saved objects security only enabled', function() { - this.tags('ciGroup9'); + this.tags('ciGroup4'); before(async () => { await createUsersAndRoles(es, supertest); diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts index 4493a5332b62c1..b54345d78456f9 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts @@ -13,7 +13,7 @@ export default function({ loadTestFile, getService }: TestInvoker) { const supertest = getService('supertest'); describe('spaces api with security', function() { - this.tags('ciGroup8'); + this.tags('ciGroup3'); before(async () => { await createUsersAndRoles(es, supertest); diff --git a/x-pack/test/token_api_integration/auth/index.js b/x-pack/test/token_api_integration/auth/index.js index e7b5a5b46a503c..528a5c4bf8cf5b 100644 --- a/x-pack/test/token_api_integration/auth/index.js +++ b/x-pack/test/token_api_integration/auth/index.js @@ -6,7 +6,7 @@ export default function ({ loadTestFile }) { describe('token-based auth', function () { - this.tags('ciGroup6'); + this.tags('ciGroup1'); loadTestFile(require.resolve('./login')); loadTestFile(require.resolve('./logout')); loadTestFile(require.resolve('./header')); diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/index.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/index.ts index e28ea819bfdfce..f87bb10aaf0838 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/index.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/index.ts @@ -17,7 +17,7 @@ export default function uiCapabilitiesTests({ loadTestFile, getService }: FtrPro const featuresService: FeaturesService = getService('features'); describe('ui capabilities', function() { - this.tags('ciGroup9'); + this.tags('ciGroup4'); before(async () => { const features = await featuresService.get(); diff --git a/x-pack/test/ui_capabilities/security_only/tests/index.ts b/x-pack/test/ui_capabilities/security_only/tests/index.ts index b84c02f9d65c03..a941e64839726a 100644 --- a/x-pack/test/ui_capabilities/security_only/tests/index.ts +++ b/x-pack/test/ui_capabilities/security_only/tests/index.ts @@ -13,7 +13,7 @@ export default function uiCapabilitesTests({ loadTestFile, getService }: FtrProv const securityService: SecurityService = getService('security'); describe('ui capabilities', function() { - this.tags('ciGroup9'); + this.tags('ciGroup4'); before(async () => { for (const user of UserScenarios) { diff --git a/x-pack/test/ui_capabilities/spaces_only/tests/index.ts b/x-pack/test/ui_capabilities/spaces_only/tests/index.ts index 294f545c7d90f2..177ca0064a6bf9 100644 --- a/x-pack/test/ui_capabilities/spaces_only/tests/index.ts +++ b/x-pack/test/ui_capabilities/spaces_only/tests/index.ts @@ -14,7 +14,7 @@ export default function uiCapabilitesTests({ loadTestFile, getService }: FtrProv const featuresService: FeaturesService = getService('features'); describe('ui capabilities', function() { - this.tags('ciGroup9'); + this.tags('ciGroup4'); before(async () => { const features = await featuresService.get(); diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js index 1b7406b37022a9..82e9214f553986 100644 --- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js @@ -6,7 +6,7 @@ export default function ({ loadTestFile }) { describe('upgrade assistant', function () { - this.tags('ciGroup7'); + this.tags('ciGroup2'); loadTestFile(require.resolve('./reindexing')); }); diff --git a/x-pack/test/visual_regression/tests/maps/index.js b/x-pack/test/visual_regression/tests/maps/index.js index de5c50e900ca8b..c080e5727b2431 100644 --- a/x-pack/test/visual_regression/tests/maps/index.js +++ b/x-pack/test/visual_regression/tests/maps/index.js @@ -26,7 +26,7 @@ export default function ({ loadTestFile, getService }) { await esArchiver.unload('maps/kibana'); }); - this.tags('ciGroup10'); + this.tags('ciGroup5'); loadTestFile(require.resolve('./vector_styling')); }); }