Fix KQL typeahead missing description (#128480)

* fix display description in typeahead
* Design fixes

- Fiddled with the flex properties to ensure the min-width for text is 250px but will expand in full until the edges are reached then both the text and description will start truncating
- Remove now unused/unnecssary `—withDescription` class

* remove description for fields suggestions

Co-authored-by: cchaos <caroline.horn@elastic.co>
This commit is contained in:
Anton Dosov 2022-03-28 22:06:59 +02:00 committed by GitHub
parent 610fe94a00
commit d8323c2246
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 54 additions and 56 deletions

View file

@ -116,7 +116,7 @@ describe('Kuery field suggestions', () => {
expect(keywordIndex).toBeLessThan(analyzedIndex);
});
test('should have descriptions', async () => {
test('should not have descriptions', async () => {
const prefix = '';
const suffix = '';
const suggestions = await getSuggestions(
@ -125,7 +125,7 @@ describe('Kuery field suggestions', () => {
);
expect(suggestions.length).toBeGreaterThan(0);
suggestions.forEach((suggestion) => {
expect(suggestion).toHaveProperty('description');
expect(suggestion).not.toHaveProperty('description');
});
});

View file

@ -6,9 +6,7 @@
* Side Public License, v 1.
*/
import React from 'react';
import { flatten } from 'lodash';
import { FormattedMessage } from '@kbn/i18n-react';
import { escapeKuery } from './lib/escape_kuery';
import { sortPrefixFirst } from './sort_prefix_first';
import {
@ -19,18 +17,6 @@ import {
} from '../../../../../../../src/plugins/data/public';
import { KqlQuerySuggestionProvider } from './types';
const getDescription = (field: IFieldType) => {
return (
<p>
<FormattedMessage
id="data.kueryAutocomplete.filterResultsDescription"
defaultMessage="Filter results that contain {fieldName}"
values={{ fieldName: <span className="kbnSuggestionItem__callout">{field.name}</span> }}
/>
</p>
);
};
const keywordComparator = (first: IFieldType, second: IFieldType) => {
const extensions = ['raw', 'keyword'];
if (extensions.map((ext) => `${first.name}.${ext}`).includes(second.name)) {
@ -72,7 +58,6 @@ export const setupGetFieldSuggestions: KqlQuerySuggestionProvider<QuerySuggestio
field.name.slice(field.subType.nested.path.length + 1)
)} }`
: `${escapeKuery(field.name.slice(nestedPath ? nestedPath.length + 1 : 0))} `;
const description = getDescription(field);
const cursorIndex =
field.subType && field.subType.nested && remainingPath.length > 0
? text.length - 2
@ -81,7 +66,6 @@ export const setupGetFieldSuggestions: KqlQuerySuggestionProvider<QuerySuggestio
return {
type: QuerySuggestionTypes.Field,
text,
description,
start,
end,
cursorIndex,

View file

@ -28,6 +28,7 @@ exports[`SuggestionComponent Should display the suggestion and use the provided
</div>
<div
className="kbnSuggestionItem__description"
data-test-subj="autoCompleteSuggestionDescription"
>
This is not a helpful suggestion
</div>
@ -63,6 +64,7 @@ exports[`SuggestionComponent Should make the element active if the selected prop
</div>
<div
className="kbnSuggestionItem__description"
data-test-subj="autoCompleteSuggestionDescription"
>
This is not a helpful suggestion
</div>

View file

@ -4,6 +4,7 @@ $kbnTypeaheadTypes: (
value: $euiColorSuccess,
operator: $euiColorPrimary,
conjunction: $euiColorVis3,
recentSearch: $euiColorMediumShade,
);
.kbnTypeahead.kbnTypeahead--small {
@ -88,11 +89,11 @@ $kbnTypeaheadTypes: (
}
.kbnSuggestionItem {
display: flex;
flex-grow: 1;
display: inline-flex;
align-items: center;
font-size: $euiFontSizeXS;
white-space: nowrap;
width: 100%;
@each $name, $color in $kbnTypeaheadTypes {
&.kbnSuggestionItem--#{$name} {
@ -102,29 +103,17 @@ $kbnTypeaheadTypes: (
}
}
}
&.kbnSuggestionItem--recentSearch {
.kbnSuggestionItem__type {
background-color: $euiColorLightShade;
color: $euiColorMediumShade;
}
.kbnSuggestionItem__text {
width: auto;
}
}
}
.kbnSuggestionItem__text,
.kbnSuggestionItem__type,
.kbnSuggestionItem__description {
flex-grow: 1;
flex-basis: 0%;
display: flex;
flex-direction: column;
padding-right: $euiSize;
}
.kbnSuggestionItem__type {
display: flex;
flex-direction: column;
flex-grow: 0;
flex-shrink: 0;
flex-basis: auto;
@ -138,25 +127,28 @@ $kbnTypeaheadTypes: (
}
.kbnSuggestionItem__text {
flex-grow: 0; /* 2 */
flex-basis: auto; /* 2 */
font-family: $euiCodeFontFamily;
width: 250px;
overflow: hidden;
text-overflow: ellipsis;
padding: $euiSizeXS $euiSizeS;
padding-left: $euiSizeS;
color: $euiTextColor;
min-width: 250px;
}
.kbnSuggestionItem__description {
color: $euiColorDarkShade;
overflow: hidden;
text-overflow: ellipsis;
margin-left: $euiSizeXL;
flex-shrink: 1;
// In case the description contains a paragraph in which the truncation needs to be at this level
> p {
overflow: hidden;
text-overflow: ellipsis;
}
&:empty {
flex-grow: 0;
margin-left: 0;
width: 0;
}
}
@ -167,8 +159,3 @@ $kbnTypeaheadTypes: (
padding: 0 $euiSizeXS;
display: inline-block;
}
.kbnSuggestionItem--value .kbnSuggestionItem__text {
flex-basis: 50%;
flex-grow: 1;
}

View file

@ -75,7 +75,12 @@ export const SuggestionComponent = React.memo(function SuggestionComponent(props
props.suggestion.type
}-${props.suggestion.text.replace(/\s/g, '-')}`}
>
<div className={'kbnSuggestionItem kbnSuggestionItem--' + props.suggestion.type}>
<div
className={classNames({
kbnSuggestionItem: true,
['kbnSuggestionItem--' + props.suggestion.type]: true,
})}
>
<div className="kbnSuggestionItem__type">
<EuiIcon type={getEuiIconType(props.suggestion.type)} />
</div>
@ -83,7 +88,12 @@ export const SuggestionComponent = React.memo(function SuggestionComponent(props
{props.suggestion.text}
</div>
{props.shouldDisplayDescription && (
<div className="kbnSuggestionItem__description">{props.suggestion.description}</div>
<div
className="kbnSuggestionItem__description"
data-test-subj="autoCompleteSuggestionDescription"
>
{props.suggestion.description}
</div>
)}
</div>
</div>

View file

@ -88,14 +88,14 @@ export default class SuggestionsComponent extends PureComponent<SuggestionsCompo
inputContainer={this.props.inputContainer}
suggestionsSize={this.props.size}
>
{(containerWidth: number) => (
{(rect: DOMRect) => (
<div
id="kbnTypeahead__items"
role="listbox"
ref={this.assignParentNode}
onScroll={this.handleScroll}
>
{renderSuggestions(containerWidth)}
{renderSuggestions(rect.width)}
</div>
)}
</ResizableSuggestionsListDiv>
@ -158,9 +158,9 @@ const StyledSuggestionsListDiv = styled.div`
const ResizableSuggestionsListDiv: React.FC<{
inputContainer: HTMLElement;
suggestionsSize?: SuggestionsListSize;
children: (rect: DOMRect) => ReactNode;
}> = React.memo((props) => {
const inputContainer = props.inputContainer;
const children = props.children as (rect: DOMRect) => ReactNode;
const [{ documentHeight }, { pageYOffset }, containerRect] = useDimensions(inputContainer);
@ -191,7 +191,7 @@ const ResizableSuggestionsListDiv: React.FC<{
['kbnTypeahead__popover--top']: !isSuggestionsListFittable,
})}
>
{children(containerRect)}
{props.children(containerRect)}
</div>
</div>
</StyledSuggestionsListDiv>

View file

@ -96,4 +96,16 @@ export class QueryBarService extends FtrService {
}
});
}
private async getSuggestionsDescription() {
const suggestions = await this.testSubjects.findAll('autoCompleteSuggestionDescription');
return Promise.all(suggestions.map((suggestion) => suggestion.getVisibleText()));
}
public async expectSuggestionsDescription({ count }: { count: number }) {
await this.retry.try(async () => {
const suggestions = await this.getSuggestionsDescription();
expect(suggestions.length).to.be(count);
});
}
}

View file

@ -1616,7 +1616,6 @@
"data.kueryAutocomplete.equalOperatorDescription.equalsText": "égale",
"data.kueryAutocomplete.existOperatorDescription": "{exists} sous un certain format",
"data.kueryAutocomplete.existOperatorDescription.existsText": "existe",
"data.kueryAutocomplete.filterResultsDescription": "Filtrer les résultats contenant {fieldName}",
"data.kueryAutocomplete.greaterThanOperatorDescription": "est {greaterThan} une certaine valeur",
"data.kueryAutocomplete.greaterThanOperatorDescription.greaterThanText": "supérieur à",
"data.kueryAutocomplete.greaterThanOrEqualOperatorDescription": "est {greaterThanOrEqualTo} une certaine valeur",

View file

@ -1939,7 +1939,6 @@
"data.kueryAutocomplete.equalOperatorDescription.equalsText": "一致する",
"data.kueryAutocomplete.existOperatorDescription": "いずれかの形式中に{exists}",
"data.kueryAutocomplete.existOperatorDescription.existsText": "存在する",
"data.kueryAutocomplete.filterResultsDescription": "{fieldName}を含む結果をフィルタリング",
"data.kueryAutocomplete.greaterThanOperatorDescription": "が一部の値{greaterThan}",
"data.kueryAutocomplete.greaterThanOperatorDescription.greaterThanText": "より大きい",
"data.kueryAutocomplete.greaterThanOrEqualOperatorDescription": "が一部の値{greaterThanOrEqualTo}",

View file

@ -1944,7 +1944,6 @@
"data.kueryAutocomplete.equalOperatorDescription.equalsText": "等于",
"data.kueryAutocomplete.existOperatorDescription": "以任意形式{exists}",
"data.kueryAutocomplete.existOperatorDescription.existsText": "存在",
"data.kueryAutocomplete.filterResultsDescription": "筛选包含 {fieldName} 的结果",
"data.kueryAutocomplete.greaterThanOperatorDescription": "{greaterThan}某一值",
"data.kueryAutocomplete.greaterThanOperatorDescription.greaterThanText": "大于",
"data.kueryAutocomplete.greaterThanOrEqualOperatorDescription": "{greaterThanOrEqualTo}某一值",

View file

@ -68,6 +68,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await queryBar.setQuery('extension.raw : ');
await queryBar.expectSuggestions({ count: 5, contains: '"jpg"' });
});
it('also displays descriptions for operators', async () => {
await PageObjects.timePicker.setDefaultAbsoluteRange();
await queryBar.setQuery('extension.raw');
await queryBar.expectSuggestionsDescription({ count: 2 });
});
});
describe('context', () => {