kibana/packages/kbn-visualization-ui-components/components/field_picker/field_picker.tsx
Marta Bondyra 362ef64751
[Lens] [Unified Search] Fix field truncation on Combo boxes (#170889)
## Summary

Fixes 3/5 of https://github.com/elastic/kibana/issues/168753
Doesn't set up multilines. Doesn't remove auto-expanding logic. Middle
truncates.

(Unified Search) Field selector
<img width="984" alt="Screenshot 2023-11-13 at 11 30 20"
src="9acb6462-3205-4e5c-81bd-c3ae10c8323b">

(Unified Search) Value selector: 
<img width="972" alt="Screenshot 2023-11-13 at 11 30 30"
src="e58b09de-d582-431f-bbd6-97b7c5bd38de">


(Lens) Field picker within layer config: 
<img width="346" alt="Screenshot 2023-11-09 at 14 44 58"
src="4ecb0ea5-bb01-49e3-a54f-4c8c5884c418">

Also fixes tiny stylistic issue for dataview picker label cut on the
bottom:
<img width="368" alt="Screenshot 2023-11-09 at 15 06 38"
src="b9ae6956-c1ef-481e-905d-71ffe5e5545a">
<img width="386" alt="Screenshot 2023-11-09 at 15 07 08"
src="5d49ed7a-e8f2-40c1-ac53-a3580b82740e">
2023-11-15 15:36:08 +01:00

121 lines
3.9 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import './field_picker.scss';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiComboBox, EuiComboBoxProps } from '@elastic/eui';
import { FieldIcon } from '@kbn/field-utils/src/components/field_icon';
import classNames from 'classnames';
import type { FieldOptionValue, FieldOption } from './types';
export interface FieldPickerProps<T extends FieldOptionValue>
extends EuiComboBoxProps<FieldOption<T>['value']> {
options: Array<FieldOption<T>>;
selectedField?: string;
onChoose: (choice: T | undefined) => void;
onDelete?: () => void;
fieldIsInvalid: boolean;
'data-test-subj'?: string;
}
const MIDDLE_TRUNCATION_PROPS = { truncation: 'middle' as const };
const SINGLE_SELECTION_AS_TEXT_PROPS = { asPlainText: true };
export function FieldPicker<T extends FieldOptionValue = FieldOptionValue>({
selectedOptions,
options,
onChoose,
onDelete,
fieldIsInvalid,
['data-test-subj']: dataTestSub,
...rest
}: FieldPickerProps<T>) {
let theLongestLabel = '';
const styledOptions = options?.map(({ compatible, exists, ...otherAttr }) => {
if (otherAttr.options) {
return {
...otherAttr,
options: otherAttr.options.map(({ exists: fieldOptionExists, ...fieldOption }) => {
if (fieldOption.label.length > theLongestLabel.length) {
theLongestLabel = fieldOption.label;
}
return {
...fieldOption,
prepend: fieldOption.value.dataType ? (
<FieldIcon
type={fieldOption.value.dataType}
fill="none"
className="eui-alignMiddle"
/>
) : null,
className: classNames({
'lnFieldPicker__option--incompatible': !fieldOption.compatible,
'lnFieldPicker__option--nonExistant': !fieldOptionExists,
}),
};
}),
};
}
return {
...otherAttr,
compatible,
prepend: otherAttr.value.dataType ? (
<FieldIcon type={otherAttr.value.dataType} fill="none" className="eui-alignMiddle" />
) : null,
className: classNames({
'lnFieldPicker__option--incompatible': !compatible,
'lnFieldPicker__option--nonExistant': !exists,
}),
};
});
const panelMinWidth = getPanelMinWidth(theLongestLabel.length);
return (
<EuiComboBox
fullWidth
compressed
isClearable={false}
data-test-subj={dataTestSub ?? 'indexPattern-dimension-field'}
placeholder={i18n.translate('visualizationUiComponents.fieldPicker.fieldPlaceholder', {
defaultMessage: 'Select a field',
})}
options={styledOptions}
isInvalid={fieldIsInvalid}
selectedOptions={selectedOptions}
singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS}
truncationProps={MIDDLE_TRUNCATION_PROPS}
inputPopoverProps={{ panelMinWidth }}
onChange={(choices) => {
if (choices.length === 0) {
onDelete?.();
return;
}
onChoose(choices[0].value);
}}
{...rest}
/>
);
}
const MINIMUM_POPOVER_WIDTH = 300;
const MINIMUM_POPOVER_WIDTH_CHAR_COUNT = 28;
const AVERAGE_CHAR_WIDTH = 7;
const MAXIMUM_POPOVER_WIDTH_CHAR_COUNT = 60;
const MAXIMUM_POPOVER_WIDTH = 550; // fitting 60 characters
function getPanelMinWidth(labelLength: number) {
if (labelLength > MAXIMUM_POPOVER_WIDTH_CHAR_COUNT) {
return MAXIMUM_POPOVER_WIDTH;
}
if (labelLength > MINIMUM_POPOVER_WIDTH_CHAR_COUNT) {
const overflownChars = labelLength - MINIMUM_POPOVER_WIDTH_CHAR_COUNT;
return MINIMUM_POPOVER_WIDTH + overflownChars * AVERAGE_CHAR_WIDTH;
}
return MINIMUM_POPOVER_WIDTH;
}