Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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');
});
});
Original file line number Diff line number Diff line change
@@ -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 : ''}
/>
);
}

/**
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

* 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 }}
/>
);
}
Original file line number Diff line number Diff line change
@@ -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' }],
]);
});
Original file line number Diff line number Diff line change
@@ -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>
);
}
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"
Expand Down
Loading