Skip to content

Commit

Permalink
[Lens] Filters aggregation (#75635)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbondyra authored Sep 10, 2020
1 parent 65abdff commit 0c678eb
Show file tree
Hide file tree
Showing 25 changed files with 1,050 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
<b>Signature:</b>

```typescript
QueryStringInput: React.FC<Pick<Props, "query" | "prepend" | "size" | "className" | "placeholder" | "onChange" | "onBlur" | "onSubmit" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "disableAutoFocus" | "persistedLog" | "bubbleSubmitEvent" | "languageSwitcherPopoverAnchorPosition" | "onChangeQueryInputFocus">>
QueryStringInput: React.FC<Pick<Props, "query" | "prepend" | "size" | "className" | "placeholder" | "onChange" | "onBlur" | "onSubmit" | "isInvalid" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "disableAutoFocus" | "persistedLog" | "bubbleSubmitEvent" | "languageSwitcherPopoverAnchorPosition" | "onChangeQueryInputFocus">>
```
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@
border-radius: 0;
border-left-width: 0;
}

.kuiLocalSearchAssistedInput {
display: flex;
flex: 1 1 100%;
position: relative;
}

/**
* 1. em used for right padding so documentation link and query string
* won't overlap if the user increases their default browser font size
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1479,7 +1479,7 @@ export interface QueryStateChange extends QueryStateChangePartial {
// Warning: (ae-missing-release-tag) "QueryStringInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const QueryStringInput: React.FC<Pick<Props_3, "query" | "prepend" | "size" | "className" | "placeholder" | "onChange" | "onBlur" | "onSubmit" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "disableAutoFocus" | "persistedLog" | "bubbleSubmitEvent" | "languageSwitcherPopoverAnchorPosition" | "onChangeQueryInputFocus">>;
export const QueryStringInput: React.FC<Pick<Props_3, "query" | "prepend" | "size" | "className" | "placeholder" | "onChange" | "onBlur" | "onSubmit" | "isInvalid" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "disableAutoFocus" | "persistedLog" | "bubbleSubmitEvent" | "languageSwitcherPopoverAnchorPosition" | "onChangeQueryInputFocus">>;

// @public (undocumented)
export type QuerySuggestion = QuerySuggestionBasic | QuerySuggestionField;
Expand Down
23 changes: 15 additions & 8 deletions src/plugins/data/public/ui/query_string_input/_query_bar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,37 @@
border-right: none !important;
}

.kbnQueryBar__textareaWrap {
overflow: visible !important; // Override EUI form control
display: flex;
flex: 1 1 100%;
position: relative;
}

.kbnQueryBar__textarea {
z-index: $euiZContentMenu;
resize: none !important; // When in the group, it will autosize
height: $euiSizeXXL;
height: $euiFormControlHeight;
// Unlike most inputs within layout control groups, the text area still needs a border.
// These adjusts help it sit above the control groups shadow to line up correctly.
padding-top: $euiSizeS + 3px !important;
transform: translateY(-2px);
padding: $euiSizeS - 1px;
padding: $euiSizeS;
padding-top: $euiSizeS + 3px;
transform: translateY(-1px) translateX(-1px);

&:not(:focus) {
&:not(:focus):not(:invalid) {
@include euiYScrollWithShadows;
}

&:not(:focus) {
white-space: nowrap;
overflow-y: hidden;
overflow-x: hidden;
border: none;
box-shadow: none;
}

// When focused, let it scroll
&:focus {
overflow-x: auto;
overflow-y: auto;
width: calc(100% + 1px); // To overtake the group's fake border
white-space: normal;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import React, { Component, RefObject, createRef } from 'react';
import { i18n } from '@kbn/i18n';

import classNames from 'classnames';
import {
EuiTextArea,
Expand Down Expand Up @@ -63,6 +64,7 @@ interface Props {
dataTestSubj?: string;
size?: SuggestionsListSize;
className?: string;
isInvalid?: boolean;
}

interface State {
Expand Down Expand Up @@ -591,6 +593,7 @@ export class QueryStringInputUI extends Component<Props, State> {
'euiFormControlLayout euiFormControlLayout--group kbnQueryBar__wrap',
this.props.className
);

return (
<div className={className}>
{this.props.prepend}
Expand All @@ -607,7 +610,7 @@ export class QueryStringInputUI extends Component<Props, State> {
>
<div
role="search"
className="euiFormControlLayout__childrenWrapper kuiLocalSearchAssistedInput"
className="euiFormControlLayout__childrenWrapper kbnQueryBar__textareaWrap"
ref={this.queryBarInputDivRefInstance}
>
<EuiTextArea
Expand Down Expand Up @@ -651,6 +654,7 @@ export class QueryStringInputUI extends Component<Props, State> {
}
role="textbox"
data-test-subj={this.props.dataTestSubj || 'queryInput'}
isInvalid={this.props.isInvalid}
>
{this.getQueryString()}
</EuiTextArea>
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/ui/typeahead/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ export const SUGGESTIONS_LIST_REQUIRED_BOTTOM_SPACE = 250;
* A distance in px to display suggestions list right under the query input without a gap
* @public
*/
export const SUGGESTIONS_LIST_REQUIRED_TOP_OFFSET = 2;
export const SUGGESTIONS_LIST_REQUIRED_TOP_OFFSET = 1;
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export class SuggestionsComponent extends Component<Props> {
const StyledSuggestionsListDiv = styled.div`
${(props: { queryBarRect: DOMRect; verticalListPosition: string }) => `
position: absolute;
z-index: 4001;
left: ${props.queryBarRect.left}px;
width: ${props.queryBarRect.width}px;
${props.verticalListPosition}`}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
@import 'config_panel';
@import 'dimension_popover';
@import 'layer_panel';
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@
min-height: $euiSizeXXL;
}

.lnsLayerPanel__anchor {
width: 100%;
}

.lnsLayerPanel__dndGrab {
padding: $euiSizeS;
}

.lnsLayerPanel__styleEditor {
width: $euiSize * 30;
padding: $euiSizeS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@
display: block;
word-break: break-word;
}

// todo: remove after closing https://github.com/elastic/eui/issues/3548
.lnsDimensionPopover__fixTranslateDnd {
// sass-lint:disable-block no-important
transform: none !important;
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './dimension_popover.scss';

import React from 'react';
import { EuiPopover } from '@elastic/eui';
Expand Down Expand Up @@ -31,6 +32,7 @@ export function DimensionPopover({
<EuiPopover
className="lnsDimensionPopover"
anchorClassName="lnsDimensionPopover__trigger"
panelClassName="lnsDimensionPopover__fixTranslateDnd"
isOpen={
popoverState.isOpen &&
(popoverState.openId === accessor || (noMatch && popoverState.addingToGroupId === groupId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function BucketNestingEditor({
value,
text: c.label,
fieldName: hasField(c) ? fieldMap[c.sourceField].displayName : '',
operationType: c.operationType,
}));

if (!column || !column.isBucketed || !aggColumns.length) {
Expand All @@ -61,6 +62,36 @@ export function BucketNestingEditor({
}
}

// todo: move the copy to operations
const topLevelCopy: Record<string, string> = {
terms: i18n.translate('xpack.lens.indexPattern.groupingOverallTerms', {
defaultMessage: 'Overall top {field}',
values: { field: fieldName },
}),
filters: i18n.translate('xpack.lens.indexPattern.groupingOverallFilters', {
defaultMessage: 'Top values for each custom query',
}),
date_histogram: i18n.translate('xpack.lens.indexPattern.groupingOverallDateHistogram', {
defaultMessage: 'Top values for each {field}',
values: { field: fieldName },
}),
};

const bottomLevelCopy: Record<string, string> = {
terms: i18n.translate('xpack.lens.indexPattern.groupingSecondTerms', {
defaultMessage: 'Top values for each {target}',
values: { target: target.fieldName },
}),
filters: i18n.translate('xpack.lens.indexPattern.groupingSecondFilters', {
defaultMessage: 'Overall top {target}',
values: { target: target.fieldName },
}),
date_histogram: i18n.translate('xpack.lens.indexPattern.groupingSecondDateHistogram', {
defaultMessage: 'Overall top {target}',
values: { target: target.fieldName },
}),
};

return (
<>
<EuiHorizontalRule margin="m" />
Expand All @@ -73,34 +104,14 @@ export function BucketNestingEditor({
<EuiRadio
id={generator('topLevel')}
data-test-subj="indexPattern-nesting-topLevel"
label={
column.operationType === 'terms'
? i18n.translate('xpack.lens.indexPattern.groupingOverallTerms', {
defaultMessage: 'Overall top {field}',
values: { field: fieldName },
})
: i18n.translate('xpack.lens.indexPattern.groupingOverallDateHistogram', {
defaultMessage: 'Top values for each {field}',
values: { field: fieldName },
})
}
label={topLevelCopy[column.operationType]}
checked={!prevColumn}
onChange={toggleNesting}
/>
<EuiRadio
id={generator('bottomLevel')}
data-test-subj="indexPattern-nesting-bottomLevel"
label={
column.operationType === 'terms'
? i18n.translate('xpack.lens.indexPattern.groupingSecondTerms', {
defaultMessage: 'Top values for each {target}',
values: { target: target.fieldName },
})
: i18n.translate('xpack.lens.indexPattern.groupingSecondDateHistogram', {
defaultMessage: 'Overall top {target}',
values: { target: target.fieldName },
})
}
label={bottomLevelCopy[column.operationType]}
checked={!!prevColumn}
onChange={toggleNesting}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ export function PopoverEditor(props: PopoverEditorProps) {
compatibleWithCurrentField ? '' : ' incompatible'
}`,
onClick() {
// todo: when moving from terms agg to filters, we want to create a filter `$field.name : *`
// it probably has to be re-thought when removing the field name.
const isTermsToFilters =
selectedColumn?.operationType === 'terms' && operationType === 'filters';

if (!selectedColumn || !compatibleWithCurrentField) {
const possibleFields = fieldByOperation[operationType] || [];

Expand All @@ -186,7 +191,7 @@ export function PopoverEditor(props: PopoverEditorProps) {
trackUiEvent(`indexpattern_dimension_operation_${operationType}`);
return;
}
if (incompatibleSelectedOperationType) {
if (incompatibleSelectedOperationType && !isTermsToFilters) {
setInvalidOperationType(null);
}
if (selectedColumn.operationType === operationType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export function getIndexPatternDatasource({
data,
savedObjects: core.savedObjects,
docLinks: core.docLinks,
http: core.http,
}}
>
<IndexPatternDimensionEditor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ export const cardinalityOperation: OperationDefinition<CardinalityIndexPatternCo
sourceField: field.name,
isBucketed: IS_BUCKETED,
params:
previousColumn && previousColumn.dataType === 'number' ? previousColumn.params : undefined,
previousColumn?.dataType === 'number' &&
previousColumn.params &&
'format' in previousColumn.params
? previousColumn.params
: undefined,
};
},
toEsAggsConfig: (column, columnId) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ export const countOperation: OperationDefinition<CountIndexPatternColumn> = {
scale: 'ratio',
sourceField: field.name,
params:
previousColumn && previousColumn.dataType === 'number' ? previousColumn.params : undefined,
previousColumn?.dataType === 'number' &&
previousColumn.params &&
'format' in previousColumn.params
? previousColumn.params
: undefined,
};
},
toEsAggsConfig: (column, columnId) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.lnsIndexPatternDimensionEditor__filtersEditor {
width: $euiSize * 60;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { MouseEventHandler } from 'react';
import { shallow } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { EuiPopover, EuiLink } from '@elastic/eui';
import { createMockedIndexPattern } from '../../../mocks';
import { FilterPopover, QueryInput, LabelInput } from './filter_popover';

jest.mock('.', () => ({
isQueryValid: () => true,
defaultLabel: 'label',
}));

const defaultProps = {
filter: {
input: { query: 'bytes >= 1', language: 'kuery' },
label: 'More than one',
id: '1',
},
setFilter: jest.fn(),
indexPattern: createMockedIndexPattern(),
Button: ({ onClick }: { onClick: MouseEventHandler }) => (
<EuiLink onClick={onClick}>trigger</EuiLink>
),
isOpenByCreation: true,
setIsOpenByCreation: jest.fn(),
};

describe('filter popover', () => {
jest.mock('../../../../../../../../src/plugins/data/public', () => ({
QueryStringInput: () => {
return 'QueryStringInput';
},
}));
it('should be open if is open by creation', () => {
const setIsOpenByCreation = jest.fn();
const instance = shallow(
<FilterPopover {...defaultProps} setIsOpenByCreation={setIsOpenByCreation} />
);
expect(instance.find(EuiPopover).prop('isOpen')).toEqual(true);
act(() => {
instance.find(EuiPopover).prop('closePopover')!();
});
instance.update();
expect(setIsOpenByCreation).toHaveBeenCalledWith(false);
});
it('should call setFilter when modifying QueryInput', () => {
const setFilter = jest.fn();
const instance = shallow(<FilterPopover {...defaultProps} setFilter={setFilter} />);
instance.find(QueryInput).prop('onChange')!({
query: 'modified : query',
language: 'lucene',
});
expect(setFilter).toHaveBeenCalledWith({
input: {
language: 'lucene',
query: 'modified : query',
},
label: 'More than one',
id: '1',
});
});
it('should call setFilter when modifying LabelInput', () => {
const setFilter = jest.fn();
const instance = shallow(<FilterPopover {...defaultProps} setFilter={setFilter} />);
instance.find(LabelInput).prop('onChange')!('Modified label');
expect(setFilter).toHaveBeenCalledWith({
input: {
language: 'kuery',
query: 'bytes >= 1',
},
label: 'Modified label',
id: '1',
});
});
});
Loading

0 comments on commit 0c678eb

Please sign in to comment.