mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
fix unified search for long field inputs (#166024)
## Summary
Fixes https://github.com/elastic/kibana/issues/166019

### 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 <nicholas.partridge@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
This commit is contained in:
parent
011a9d1e2c
commit
c6770af9a6
7 changed files with 52 additions and 32 deletions
|
@ -20,6 +20,7 @@ export interface GenericComboBoxProps<T> {
|
|||
searchValue: string,
|
||||
OPTION_CONTENT_CLASSNAME: string
|
||||
) => React.ReactNode;
|
||||
inputRef?: ((instance: HTMLInputElement | null) => void) | undefined;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,12 +31,8 @@ const COMBOBOX_PADDINGS = 10;
|
|||
const DEFAULT_FONT = '14px Inter';
|
||||
|
||||
class PhraseValueInputUI extends PhraseSuggestorUI<PhraseValueInputProps> {
|
||||
comboBoxRef: React.RefObject<HTMLInputElement>;
|
||||
|
||||
constructor(props: PhraseValueInputProps) {
|
||||
super(props);
|
||||
this.comboBoxRef = React.createRef();
|
||||
}
|
||||
comboBoxWrapperRef = React.createRef<HTMLDivElement>();
|
||||
inputRef: HTMLInputElement | null = null;
|
||||
|
||||
public render() {
|
||||
return (
|
||||
|
@ -69,8 +65,11 @@ class PhraseValueInputUI extends PhraseSuggestorUI<PhraseValueInputProps> {
|
|||
const valueAsStr = String(value);
|
||||
const options = value ? uniq([valueAsStr, ...suggestions]) : suggestions;
|
||||
return (
|
||||
<div ref={this.comboBoxRef}>
|
||||
<div ref={this.comboBoxWrapperRef}>
|
||||
<StringComboBox
|
||||
inputRef={(ref) => {
|
||||
this.inputRef = ref;
|
||||
}}
|
||||
isDisabled={this.props.disabled}
|
||||
fullWidth={fullWidth}
|
||||
compressed={this.props.compressed}
|
||||
|
@ -85,7 +84,13 @@ class PhraseValueInputUI extends PhraseSuggestorUI<PhraseValueInputProps> {
|
|||
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<PhraseValueInputProps> {
|
|||
defaultComboboxWidth={DEFAULT_COMBOBOX_WIDTH}
|
||||
defaultFont={DEFAULT_FONT}
|
||||
comboboxPaddings={COMBOBOX_PADDINGS}
|
||||
comboBoxRef={this.comboBoxRef}
|
||||
comboBoxWrapperRef={this.comboBoxWrapperRef}
|
||||
label={option.label}
|
||||
search={searchValue}
|
||||
/>
|
||||
|
|
|
@ -33,12 +33,7 @@ const COMBOBOX_PADDINGS = 20;
|
|||
const DEFAULT_FONT = '14px Inter';
|
||||
|
||||
class PhrasesValuesInputUI extends PhraseSuggestorUI<PhrasesValuesInputProps> {
|
||||
comboBoxRef: React.RefObject<HTMLInputElement>;
|
||||
|
||||
constructor(props: PhrasesValuesInputProps) {
|
||||
super(props);
|
||||
this.comboBoxRef = React.createRef();
|
||||
}
|
||||
comboBoxWrapperRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
public render() {
|
||||
const { suggestions } = this.state;
|
||||
|
@ -46,7 +41,7 @@ class PhrasesValuesInputUI extends PhraseSuggestorUI<PhrasesValuesInputProps> {
|
|||
const options = values ? uniq([...values, ...suggestions]) : suggestions;
|
||||
|
||||
return (
|
||||
<div ref={this.comboBoxRef}>
|
||||
<div ref={this.comboBoxWrapperRef}>
|
||||
<StringComboBox
|
||||
fullWidth={fullWidth}
|
||||
compressed={compressed}
|
||||
|
@ -79,7 +74,7 @@ class PhrasesValuesInputUI extends PhraseSuggestorUI<PhrasesValuesInputProps> {
|
|||
defaultComboboxWidth={DEFAULT_COMBOBOX_WIDTH}
|
||||
defaultFont={DEFAULT_FONT}
|
||||
comboboxPaddings={COMBOBOX_PADDINGS}
|
||||
comboBoxRef={this.comboBoxRef}
|
||||
comboBoxWrapperRef={this.comboBoxWrapperRef}
|
||||
label={option.label}
|
||||
search={searchValue}
|
||||
/>
|
||||
|
|
|
@ -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<typeof TruncatedLabel> = {
|
||||
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<HTMLInputElement>(),
|
||||
comboBoxWrapperRef: React.createRef<HTMLDivElement>(),
|
||||
search: '',
|
||||
label: 'example_field',
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ import { throttle } from 'lodash';
|
|||
interface TruncatedLabelProps {
|
||||
label: string;
|
||||
search: string;
|
||||
comboBoxRef: RefObject<HTMLInputElement>;
|
||||
comboBoxWrapperRef: RefObject<HTMLDivElement | null>;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<HTMLInputElement>(null);
|
||||
const comboBoxWrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement | null>(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<HTMLDivElement> = () => {
|
||||
// Force focus on input due to https://github.com/elastic/eui/issues/7170
|
||||
inputRef?.current?.focus();
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={comboBoxRef}>
|
||||
<div ref={comboBoxWrapperRef}>
|
||||
<EuiComboBox
|
||||
id={id}
|
||||
inputRef={(ref) => {
|
||||
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) => (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
|
||||
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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`
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue