From c6770af9a6bbd2e513b2e1579c7ed200732a8c67 Mon Sep 17 00:00:00 2001 From: Tim Schnell Date: Mon, 11 Sep 2023 02:35:53 -0500 Subject: [PATCH] fix unified search for long field inputs (#166024) ## Summary Fixes https://github.com/elastic/kibana/issues/166019 ![long-field-name-input](https://github.com/elastic/kibana/assets/1280964/be343d1b-e639-4b04-9742-0bcf1e9f032d) ### Checklist Delete any items that are not applicable to this PR. - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: nickofthyme Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli --- .../filter_editor/generic_combo_box.tsx | 1 + .../filter_editor/phrase_value_input.tsx | 23 +++++++++++-------- .../filter_editor/phrases_values_input.tsx | 11 +++------ .../filter_editor/truncated_label.test.tsx | 8 +++---- .../filter_editor/truncated_label.tsx | 13 +++++------ .../filter_item/field_input.tsx | 21 ++++++++++++++--- .../filter_item/filter_item.styles.ts | 7 ++++++ 7 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/generic_combo_box.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/generic_combo_box.tsx index 04379d34a3946..aa941fe395733 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/generic_combo_box.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/generic_combo_box.tsx @@ -20,6 +20,7 @@ export interface GenericComboBoxProps { searchValue: string, OPTION_CONTENT_CLASSNAME: string ) => React.ReactNode; + inputRef?: ((instance: HTMLInputElement | null) => void) | undefined; [propName: string]: any; } diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_value_input.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_value_input.tsx index ae87024bb61a9..0a466c61770ce 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_value_input.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_value_input.tsx @@ -31,12 +31,8 @@ const COMBOBOX_PADDINGS = 10; const DEFAULT_FONT = '14px Inter'; class PhraseValueInputUI extends PhraseSuggestorUI { - comboBoxRef: React.RefObject; - - constructor(props: PhraseValueInputProps) { - super(props); - this.comboBoxRef = React.createRef(); - } + comboBoxWrapperRef = React.createRef(); + inputRef: HTMLInputElement | null = null; public render() { return ( @@ -69,8 +65,11 @@ class PhraseValueInputUI extends PhraseSuggestorUI { const valueAsStr = String(value); const options = value ? uniq([valueAsStr, ...suggestions]) : suggestions; return ( -
+
{ + this.inputRef = ref; + }} isDisabled={this.props.disabled} fullWidth={fullWidth} compressed={this.props.compressed} @@ -85,7 +84,13 @@ class PhraseValueInputUI extends PhraseSuggestorUI { options={options} getLabel={(option) => option} selectedOptions={value ? [valueAsStr] : []} - onChange={([newValue = '']) => onChange(newValue)} + onChange={([newValue = '']) => { + onChange(newValue); + setTimeout(() => { + // Note: requires a tick skip to correctly blur element focus + this.inputRef?.blur(); + }); + }} onSearchChange={this.onSearchChange} singleSelection={{ asPlainText: true }} onCreateOption={onChange} @@ -98,7 +103,7 @@ class PhraseValueInputUI extends PhraseSuggestorUI { defaultComboboxWidth={DEFAULT_COMBOBOX_WIDTH} defaultFont={DEFAULT_FONT} comboboxPaddings={COMBOBOX_PADDINGS} - comboBoxRef={this.comboBoxRef} + comboBoxWrapperRef={this.comboBoxWrapperRef} label={option.label} search={searchValue} /> diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/phrases_values_input.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/phrases_values_input.tsx index 336849c4ee65a..b31e6aad7d438 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/phrases_values_input.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/phrases_values_input.tsx @@ -33,12 +33,7 @@ const COMBOBOX_PADDINGS = 20; const DEFAULT_FONT = '14px Inter'; class PhrasesValuesInputUI extends PhraseSuggestorUI { - comboBoxRef: React.RefObject; - - constructor(props: PhrasesValuesInputProps) { - super(props); - this.comboBoxRef = React.createRef(); - } + comboBoxWrapperRef = React.createRef(); public render() { const { suggestions } = this.state; @@ -46,7 +41,7 @@ class PhrasesValuesInputUI extends PhraseSuggestorUI { const options = values ? uniq([...values, ...suggestions]) : suggestions; return ( -
+
{ defaultComboboxWidth={DEFAULT_COMBOBOX_WIDTH} defaultFont={DEFAULT_FONT} comboboxPaddings={COMBOBOX_PADDINGS} - comboBoxRef={this.comboBoxRef} + comboBoxWrapperRef={this.comboBoxWrapperRef} label={option.label} search={searchValue} /> diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/truncated_label.test.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/truncated_label.test.tsx index 47ea4cbf2c0eb..08236041ab93a 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/truncated_label.test.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/truncated_label.test.tsx @@ -6,18 +6,16 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { ComponentProps } from 'react'; import { mount } from 'enzyme'; import { TruncatedLabel } from './truncated_label'; describe('truncated_label', () => { - const defaultProps = { + const defaultProps: ComponentProps = { defaultFont: '14px Inter', - // jest-canvas-mock mocks measureText as the number of string characters, thats why the width is so low - width: 30, defaultComboboxWidth: 130, comboboxPaddings: 100, - comboBoxRef: React.createRef(), + comboBoxWrapperRef: React.createRef(), search: '', label: 'example_field', }; diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/truncated_label.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/truncated_label.tsx index 9f268e46d7929..21304ad244edf 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/truncated_label.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/truncated_label.tsx @@ -15,7 +15,7 @@ import { throttle } from 'lodash'; interface TruncatedLabelProps { label: string; search: string; - comboBoxRef: RefObject; + comboBoxWrapperRef: RefObject; defaultFont: string; defaultComboboxWidth: number; comboboxPaddings: number; @@ -56,7 +56,7 @@ const truncateLabel = ( export const TruncatedLabel = React.memo(function TruncatedLabel({ label, - comboBoxRef, + comboBoxWrapperRef, search, defaultFont, defaultComboboxWidth, @@ -69,15 +69,14 @@ export const TruncatedLabel = React.memo(function TruncatedLabel({ width: defaultComboboxWidth - comboboxPaddings, font: defaultFont, }); - const computeStyles = (_e: UIEvent | undefined, shouldRecomputeAll = false) => { - if (comboBoxRef.current) { + if (comboBoxWrapperRef.current) { const current = { ...labelProps, - width: comboBoxRef.current?.clientWidth - comboboxPaddings, + width: comboBoxWrapperRef.current.clientWidth - comboboxPaddings, }; if (shouldRecomputeAll) { - current.font = window.getComputedStyle(comboBoxRef.current).font; + current.font = window.getComputedStyle(comboBoxWrapperRef.current).font; } setLabelProps(current); } @@ -88,7 +87,7 @@ export const TruncatedLabel = React.memo(function TruncatedLabel({ }, 50); useEffectOnce(() => { - if (comboBoxRef.current) { + if (comboBoxWrapperRef.current) { handleResize(undefined, true); } diff --git a/src/plugins/unified_search/public/filters_builder/filter_item/field_input.tsx b/src/plugins/unified_search/public/filters_builder/filter_item/field_input.tsx index 3dceef3ef52c9..8c3dc65758c29 100644 --- a/src/plugins/unified_search/public/filters_builder/filter_item/field_input.tsx +++ b/src/plugins/unified_search/public/filters_builder/filter_item/field_input.tsx @@ -43,7 +43,8 @@ export function FieldInput({ field, dataView, onHandleField }: FieldInputProps) const { disabled, suggestionsAbstraction } = useContext(FiltersBuilderContextType); const fields = dataView ? getFilterableFields(dataView) : []; const id = useGeneratedHtmlId({ prefix: 'fieldInput' }); - const comboBoxRef = useRef(null); + const comboBoxWrapperRef = useRef(null); + const inputRef = useRef(null); const onFieldChange = useCallback( ([selectedField]: DataViewField[]) => { @@ -77,12 +78,25 @@ export function FieldInput({ field, dataView, onHandleField }: FieldInputProps) ({ label }) => fields[optionFields.findIndex((optionField) => optionField.label === label)] ); onFieldChange(newValues); + + setTimeout(() => { + // Note: requires a tick skip to correctly blur element focus + inputRef?.current?.blur(); + }); + }; + + const handleFocus: React.FocusEventHandler = () => { + // Force focus on input due to https://github.com/elastic/eui/issues/7170 + inputRef?.current?.focus(); }; return ( -
+
{ + inputRef.current = ref; + }} options={euiOptions} selectedOptions={selectedEuiOptions} onChange={onComboBoxChange} @@ -94,6 +108,7 @@ export function FieldInput({ field, dataView, onHandleField }: FieldInputProps) isClearable={false} compressed fullWidth + onFocus={handleFocus} data-test-subj="filterFieldSuggestionList" renderOption={(option, searchValue) => ( @@ -105,7 +120,7 @@ export function FieldInput({ field, dataView, onHandleField }: FieldInputProps) defaultComboboxWidth={DEFAULT_COMBOBOX_WIDTH} defaultFont={DEFAULT_FONT} comboboxPaddings={COMBOBOX_PADDINGS} - comboBoxRef={comboBoxRef} + comboBoxWrapperRef={comboBoxWrapperRef} label={option.label} search={searchValue} /> diff --git a/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.styles.ts b/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.styles.ts index 5ca888735f5ad..6ec0ac9ab7058 100644 --- a/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.styles.ts +++ b/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.styles.ts @@ -22,6 +22,13 @@ export const cursorOrCss = css` export const fieldAndParamCss = (euiTheme: EuiThemeComputed) => css` min-width: calc(${euiTheme.size.xl} * 5); + flex-grow: 1; + .euiFormRow { + max-width: 800px; + } + &:focus-within { + flex-grow: 4; + } `; export const operationCss = (euiTheme: EuiThemeComputed) => css`