[discover] De-angularize index pattern selection (#46347)
* Implementation of DiscoverIndexPattern React component * Implement usage of the DiscoverIndexPattern * Adapt tests
This commit is contained in:
parent
bd1ac0dad4
commit
f9cc456de0
|
@ -127,13 +127,6 @@ describe('discover field chooser directives', function () {
|
|||
};
|
||||
};
|
||||
|
||||
describe('Index list', function () {
|
||||
it('should be in alphabetical order', function () {
|
||||
$elem.find('.ui-select-toggle').click();
|
||||
expect($elem.find('[role=option]').text().replace(/\W+/g, '')).to.be('abc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Field listing', function () {
|
||||
it('should have Selected Fields, Fields and Popular Fields sections', function () {
|
||||
const headers = $elem.find('.sidebar-list-header');
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`DiscoverIndexPattern A single index pattern is just displayed 1`] = `
|
||||
<DiscoverIndexPatternTitle
|
||||
isChangeable={false}
|
||||
onChange={[Function]}
|
||||
title="test1"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`DiscoverIndexPattern Invalid props dont cause an exception: "" 1`] = `""`;
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
// @ts-ignore
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
import { SavedObject } from 'kibana/server';
|
||||
import { DiscoverIndexPattern, DiscoverIndexPatternProps } from './discover_index_pattern';
|
||||
import { comboBoxKeyCodes } from '@elastic/eui';
|
||||
|
||||
const indexPattern1 = {
|
||||
id: 'test1',
|
||||
attributes: {
|
||||
title: 'test1',
|
||||
},
|
||||
} as SavedObject;
|
||||
|
||||
const indexPattern2 = {
|
||||
id: 'test2',
|
||||
attributes: {
|
||||
title: 'test2',
|
||||
},
|
||||
} as SavedObject;
|
||||
|
||||
describe('DiscoverIndexPattern', () => {
|
||||
test('Invalid props dont cause an exception', () => {
|
||||
const props = {
|
||||
indexPatternList: null,
|
||||
selectedIndexPattern: null,
|
||||
setIndexPattern: jest.fn(),
|
||||
} as any;
|
||||
|
||||
expect(shallowWithIntl(<DiscoverIndexPattern {...props} />)).toMatchSnapshot(`""`);
|
||||
});
|
||||
test('A single index pattern is just displayed', () => {
|
||||
const props = {
|
||||
indexPatternList: [indexPattern1],
|
||||
selectedIndexPattern: indexPattern1,
|
||||
setIndexPattern: jest.fn(),
|
||||
} as DiscoverIndexPatternProps;
|
||||
|
||||
expect(shallowWithIntl(<DiscoverIndexPattern {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Multiple index patterns are selectable', () => {
|
||||
const props = {
|
||||
indexPatternList: [indexPattern1, indexPattern2],
|
||||
selectedIndexPattern: indexPattern2,
|
||||
setIndexPattern: jest.fn(),
|
||||
} as DiscoverIndexPatternProps;
|
||||
const component = mountWithIntl(<DiscoverIndexPattern {...props} />);
|
||||
findTestSubject(component, 'indexPattern-switch-link').simulate('click');
|
||||
|
||||
const searchInput = findTestSubject(component, 'comboBoxSearchInput');
|
||||
searchInput.simulate('change', { target: { value: 'test1' } });
|
||||
searchInput.simulate('keyDown', { keyCode: comboBoxKeyCodes.ENTER });
|
||||
expect(props.setIndexPattern).toBeCalledWith('test1');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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 { EuiComboBox } from '@elastic/eui';
|
||||
import { SavedObject } from 'kibana/server';
|
||||
import { DiscoverIndexPatternTitle } from './discover_index_pattern_title';
|
||||
|
||||
export interface DiscoverIndexPatternProps {
|
||||
/**
|
||||
* list of available index patterns, if length > 1, component offers a "change" link
|
||||
*/
|
||||
indexPatternList: SavedObject[];
|
||||
/**
|
||||
* currently selected index pattern, due to angular issues it's undefined at first rendering
|
||||
*/
|
||||
selectedIndexPattern: SavedObject;
|
||||
/**
|
||||
* triggered when user selects a new index pattern
|
||||
*/
|
||||
setIndexPattern: (id: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component allows you to select an index pattern in discovers side bar
|
||||
*/
|
||||
export function DiscoverIndexPattern({
|
||||
indexPatternList,
|
||||
selectedIndexPattern,
|
||||
setIndexPattern,
|
||||
}: DiscoverIndexPatternProps) {
|
||||
if (!indexPatternList || indexPatternList.length === 0 || !selectedIndexPattern) {
|
||||
// just in case, shouldn't happen
|
||||
return null;
|
||||
}
|
||||
const [selected, setSelected] = useState(selectedIndexPattern);
|
||||
const [showCombo, setShowCombo] = useState(false);
|
||||
const options = indexPatternList.map(entity => ({
|
||||
value: entity.id,
|
||||
label: entity.attributes!.title,
|
||||
}));
|
||||
const selectedOptions = selected
|
||||
? [{ value: selected.id, label: selected.attributes.title }]
|
||||
: [];
|
||||
|
||||
const findIndexPattern = (id?: string) => indexPatternList.find(entity => entity.id === id);
|
||||
|
||||
if (!showCombo) {
|
||||
return (
|
||||
<DiscoverIndexPatternTitle
|
||||
isChangeable={indexPatternList.length > 1}
|
||||
onChange={() => setShowCombo(true)}
|
||||
title={selected.attributes ? selected.attributes.title : ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* catches a EuiComboBox related 'Can't perform a React state update on an unmounted component'
|
||||
* warning in console by delaying the hiding/removal of the EuiComboBox a bit
|
||||
*/
|
||||
function hideCombo() {
|
||||
setTimeout(() => setShowCombo(false), 50);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiComboBox
|
||||
className="index-pattern-selection"
|
||||
data-test-subj="index-pattern-selection"
|
||||
fullWidth={true}
|
||||
isClearable={false}
|
||||
onBlur={() => hideCombo()}
|
||||
onChange={choices => {
|
||||
const newSelected = choices[0] && findIndexPattern(choices[0].value);
|
||||
if (newSelected) {
|
||||
setSelected(newSelected);
|
||||
setIndexPattern(newSelected.id);
|
||||
}
|
||||
hideCombo();
|
||||
}}
|
||||
inputRef={el => {
|
||||
// auto focus input element when combo box is displayed
|
||||
if (el) {
|
||||
el.focus();
|
||||
}
|
||||
}}
|
||||
options={options}
|
||||
selectedOptions={selectedOptions}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 { DiscoverIndexPattern } from './discover_index_pattern';
|
||||
|
||||
const app = uiModules.get('apps/discover');
|
||||
|
||||
app.directive('discoverIndexPatternSelect', function(reactDirective: any) {
|
||||
return reactDirective(wrapInI18nContext(DiscoverIndexPattern), [
|
||||
['indexPatternList', { watchDepth: 'reference' }],
|
||||
['selectedIndexPattern', { watchDepth: 'reference' }],
|
||||
['setIndexPattern', { watchDepth: 'reference' }],
|
||||
]);
|
||||
});
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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 { EuiToolTip, EuiFlexItem, EuiFlexGroup, EuiTitle, EuiButtonEmpty } from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
export interface DiscoverIndexPatternTitleProps {
|
||||
/**
|
||||
* determines whether the change link is displayed
|
||||
*/
|
||||
isChangeable: boolean;
|
||||
/**
|
||||
* function triggered when the change link is clicked
|
||||
*/
|
||||
onChange: () => void;
|
||||
/**
|
||||
* title of the current index pattern
|
||||
*/
|
||||
title: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component displaying the title of the current selected index pattern
|
||||
* and if changeable is true, a link is provided to change the index pattern
|
||||
*/
|
||||
export function DiscoverIndexPatternTitle({
|
||||
isChangeable,
|
||||
onChange,
|
||||
title,
|
||||
}: DiscoverIndexPatternTitleProps) {
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="none" responsive={false} className="index-pattern-selection">
|
||||
<EuiFlexItem className="eui-textTruncate">
|
||||
<EuiToolTip content={title}>
|
||||
<EuiTitle size="xxs" className="eui-textTruncate">
|
||||
<h2 title={title}>{title}</h2>
|
||||
</EuiTitle>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
{isChangeable && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="kbn.discover.fieldChooser.indexPattern.changeLinkTooltip"
|
||||
defaultMessage="Change current index pattern"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="indexPattern-switch-link"
|
||||
size="xs"
|
||||
onClick={() => onChange()}
|
||||
>
|
||||
(
|
||||
<FormattedMessage
|
||||
id="kbn.discover.fieldChooser.indexPattern.changeLinkLabel"
|
||||
defaultMessage="change"
|
||||
description="should be a short word since lack of space"
|
||||
/>
|
||||
)
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
|
@ -1,39 +1,14 @@
|
|||
<section class="sidebar-list" aria-label="{{::'kbn.discover.fieldChooser.filter.indexAndFieldsSectionAriaLabel' | i18n: {defaultMessage: 'Index and fields'} }}">
|
||||
<div ng-show="indexPatternList.length > 1">
|
||||
<ui-select
|
||||
class="index-pattern-selection"
|
||||
ng-model="selectedIndexPattern"
|
||||
on-select="setIndexPattern($item)"
|
||||
>
|
||||
<ui-select-match>
|
||||
<span
|
||||
class="uiSelectMatch--ellipsis"
|
||||
tooltip="{{$select.selected.get('title')}}"
|
||||
tooltip-append-to-body="true"
|
||||
>
|
||||
{{$select.selected.get('title')}}
|
||||
</span>
|
||||
</ui-select-match>
|
||||
<ui-select-choices repeat="pattern in indexPatternList | filter:$select.search">
|
||||
<div ng-bind-html="pattern.get('title') | highlight: $select.search"></div>
|
||||
</ui-select-choices>
|
||||
</ui-select>
|
||||
</div>
|
||||
<div ng-hide="indexPatternList.length > 1">
|
||||
<div class="index-pattern">
|
||||
<h2
|
||||
class="index-pattern-label"
|
||||
id="index_pattern_id"
|
||||
tabindex="0"
|
||||
css-truncate>
|
||||
|
||||
{{ indexPattern.title }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<discover-index-pattern-select
|
||||
selected-index-pattern="selectedIndexPattern"
|
||||
set-index-pattern="setIndexPattern"
|
||||
index-pattern-list="indexPatternList"
|
||||
>
|
||||
</discover-index-pattern-select>
|
||||
|
||||
<div class="dscSidebar__listHeader sidebar-list-header" ng-if="fields.length">
|
||||
<h3
|
||||
class="euiFlexItem euiTitle euiTitle--xxsmall"
|
||||
class="euiFlexItem euiTitle euiTitle--xxxsmall sidebar-list-header-heading"
|
||||
id="selected_fields"
|
||||
tabindex="0"
|
||||
i18n-id="kbn.discover.fieldChooser.filter.selectedFieldsTitle"
|
||||
|
@ -54,7 +29,7 @@
|
|||
|
||||
<div class="sidebar-list-header sidebar-item euiFlexGroup euiFlexGroup--gutterMedium" ng-if="fields.length">
|
||||
<h3
|
||||
class="euiFlexItem euiTitle euiTitle--xxsmall"
|
||||
class="euiFlexItem euiTitle euiTitle--xxxsmall sidebar-list-header-heading"
|
||||
id="available_fields"
|
||||
tabindex="0"
|
||||
i18n-id="kbn.discover.fieldChooser.filter.availableFieldsTitle"
|
||||
|
|
|
@ -21,7 +21,7 @@ import 'ui/directives/css_truncate';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import 'ui/directives/field_name';
|
||||
import './discover_field';
|
||||
import 'ui/angular_ui_select';
|
||||
import './discover_index_pattern_directive';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import rison from 'rison-node';
|
||||
|
@ -61,8 +61,8 @@ app.directive('discFieldChooser', function ($location, config, $route) {
|
|||
(pattern) => pattern.id === $scope.indexPattern.id
|
||||
);
|
||||
$scope.indexPatternList = _.sortBy($scope.indexPatternList, o => o.get('title'));
|
||||
$scope.setIndexPattern = function (pattern) {
|
||||
$scope.state.index = pattern.id;
|
||||
$scope.setIndexPattern = function (id) {
|
||||
$scope.state.index = id;
|
||||
$scope.state.save();
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// ONLY USED IN DISCOVER
|
||||
|
||||
.sidebar-container {
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
padding-left: $euiSizeS !important;
|
||||
padding-right: $euiSizeS !important;
|
||||
background-color: $euiColorLightestShade;
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
|
@ -52,40 +52,43 @@
|
|||
}
|
||||
}
|
||||
|
||||
.sidebar-item-title,
|
||||
.sidebar-item-text {
|
||||
margin: 0;
|
||||
padding: $euiSizeXS $euiSizeS;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
.sidebar-item-title,
|
||||
.sidebar-item-text {
|
||||
margin: 0;
|
||||
padding: $euiSizeXS 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.sidebar-item-title {
|
||||
@include euiTextTruncate;
|
||||
text-align: left;
|
||||
.sidebar-item-title {
|
||||
@include euiTextTruncate;
|
||||
text-align: left;
|
||||
|
||||
&.full-title {
|
||||
white-space: normal;
|
||||
}
|
||||
&.full-title {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-item-text {
|
||||
background: $euiColorEmptyShade;
|
||||
}
|
||||
.sidebar-item-text {
|
||||
background: $euiColorEmptyShade;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-list-header {
|
||||
padding-left: $euiSizeS;
|
||||
padding-right: $euiSizeS;
|
||||
color: $euiColorFullShade;
|
||||
border: 1px solid transparent;
|
||||
.sidebar-list-header-heading {
|
||||
color: $euiColorDarkestShade;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar-list-header-label {
|
||||
font-size: $euiFontSizeS;
|
||||
padding-left: $euiSizeS;
|
||||
font-size: $euiFontSizeXS;
|
||||
line-height: $euiLineHeight;
|
||||
font-weight: $euiFontWeightBold;
|
||||
color: $euiColorDarkShade;
|
||||
border-bottom: 1px solid $euiColorLightShade;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,12 +113,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.index-pattern-selection {
|
||||
padding: 0 $euiSizeS;
|
||||
}
|
||||
|
||||
.index-pattern-selection .ui-select-choices {
|
||||
width: auto;
|
||||
max-height: 300px;
|
||||
.index-pattern-selection:not(.euiComboBox) {
|
||||
padding: $euiSizeS 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ export function DiscoverPageProvider({ getService, getPageObjects }) {
|
|||
const globalNav = getService('globalNav');
|
||||
const config = getService('config');
|
||||
const defaultFindTimeout = config.get('timeouts.find');
|
||||
const comboBox = getService('comboBox');
|
||||
|
||||
class DiscoverPage {
|
||||
async getQueryField() {
|
||||
|
@ -271,8 +272,8 @@ export function DiscoverPageProvider({ getService, getPageObjects }) {
|
|||
}
|
||||
|
||||
async selectIndexPattern(indexPattern) {
|
||||
await find.clickByCssSelector('.index-pattern-selection');
|
||||
await find.setValue('.ui-select-search', indexPattern + '\n');
|
||||
await testSubjects.click('indexPattern-switch-link');
|
||||
await comboBox.set('index-pattern-selection', indexPattern);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue