Skip to content

Commit

Permalink
[Maps] Fix long field truncation on Comboboxes (#171829)
Browse files Browse the repository at this point in the history
## Summary

Fixes #171509 (fixed in all the
places in maps)

Adds middle truncation and combobox auto-expansion to the content to
approximate of 60 character maximum for `SingleFieldSelect` and
`FieldSelect` components in maps.
I removed custom `renderOption` prop so the combobox can take care of
the proper truncation while searching through the fields. One case I had
to hack was to display a tooltip for a disabled state. I used `prepend`
and some custom styling to do so - it works as before (check out the
screenshot below).

<details>
 <summary> FieldSelect component before</summary>
<img width="471" alt="Screenshot 2023-11-22 at 16 08 21"
src="https://github.com/elastic/kibana/assets/4283304/eb8a682a-0dfe-4bb0-9a88-8dc5863a5aff">

</details>

<details>
 <summary> FieldSelect component after</summary>
<img width="582" alt="Screenshot 2023-11-22 at 16 10 02"
src="https://github.com/elastic/kibana/assets/4283304/b840fb7e-0789-46b7-b783-8e0aadb270ac">

</details>

<details>
 <summary> SingleFieldSelect component before</summary>
<img width="430" alt="Screenshot 2023-11-23 at 10 23 15"
src="https://github.com/elastic/kibana/assets/4283304/fc4af30c-32ad-40d8-a051-4b123be8aa54">
</details>

<details>
 <summary> SingleFieldSelect component after</summary>
<img width="576" alt="Screenshot 2023-11-22 at 16 32 43"
src="https://github.com/elastic/kibana/assets/4283304/2c3f496e-2e1b-4cf9-aa5b-f38c7a7eace2">

#### disabled state 
<img width="829" alt="Screenshot 2023-11-22 at 17 10 48"
src="https://github.com/elastic/kibana/assets/4283304/8db0b076-d3f5-4ec4-9e7c-0e7121b14e04">

</details>

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
mbondyra and kibanamachine authored Nov 29, 2023
1 parent bf5f8cb commit 37d5aca
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 71 deletions.
3 changes: 3 additions & 0 deletions x-pack/plugins/maps/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,6 @@ export enum MASK_OPERATOR {
// Maplibre does not provide any feedback when rendering is complete.
// Workaround is hard-coded timeout period.
export const RENDER_TIMEOUT = 1000;

export const MIDDLE_TRUNCATION_PROPS = { truncation: 'middle' as const };
export const SINGLE_SELECTION_AS_TEXT_PROPS = { asPlainText: true };
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,18 @@
*/

import React from 'react';

import {
EuiComboBox,
EuiComboBoxProps,
EuiComboBoxOptionOption,
EuiHighlight,
EuiFlexGroup,
EuiFlexItem,
} from '@elastic/eui';
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
import { EuiComboBox, EuiComboBoxProps, EuiComboBoxOptionOption } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FieldIcon } from '@kbn/react-field';
import { FIELD_ORIGIN, VECTOR_STYLES } from '../../../../../common/constants';
import {
FIELD_ORIGIN,
MIDDLE_TRUNCATION_PROPS,
SINGLE_SELECTION_AS_TEXT_PROPS,
VECTOR_STYLES,
} from '../../../../../common/constants';
import { StyleField } from '../style_fields_helper';

function renderOption(
option: EuiComboBoxOptionOption<StyleField>,
searchValue: string,
contentClassName: string
) {
const fieldIcon = option.value ? <FieldIcon type={option.value.type} fill="none" /> : null;
return (
<EuiFlexGroup className={contentClassName} gutterSize="s" alignItems="center">
<EuiFlexItem grow={null}>{fieldIcon}</EuiFlexItem>
<EuiFlexItem>
<EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
</EuiFlexItem>
</EuiFlexGroup>
);
}

function groupFieldsByOrigin(fields: StyleField[]) {
const fieldsByOriginMap = new Map<FIELD_ORIGIN, StyleField[]>();
fields.forEach((field) => {
Expand All @@ -56,6 +38,9 @@ function groupFieldsByOrigin(fields: StyleField[]) {
label: field.label,
disabled: field.isUnsupported,
title: field.unsupportedMsg,
prepend: field.type ? (
<FieldIcon type={field.type} fill="none" className="eui-alignMiddle" />
) : null,
};
})
.sort((a, b) => {
Expand Down Expand Up @@ -129,19 +114,24 @@ export function FieldSelect({ fields, selectedFieldName, onChange, styleName, ..
}
}

const options = groupFieldsByOrigin(fields);

const panelMinWidth = calculateWidthFromEntries(fields, ['label']);

return (
<EuiComboBox
selectedOptions={selectedOption ? [selectedOption] : []}
options={groupFieldsByOrigin(fields)}
options={options}
onChange={onFieldChange}
singleSelection={{ asPlainText: true }}
isClearable={false}
fullWidth
placeholder={i18n.translate('xpack.maps.styles.vector.selectFieldPlaceholder', {
defaultMessage: 'Select a field',
})}
renderOption={renderOption}
data-test-subj={`styleFieldSelect_${styleName}`}
singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS}
truncationProps={MIDDLE_TRUNCATION_PROPS}
inputPopoverProps={{ panelMinWidth }}
{...rest}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import _ from 'lodash';
import React, { ChangeEvent, Component } from 'react';
import { EuiComboBox, EuiComboBoxOptionOption, EuiFieldText } from '@elastic/eui';
import { SINGLE_SELECTION_AS_TEXT_PROPS } from '../../../../../common/constants';
import { IField } from '../../../fields/field';

interface Props {
Expand Down Expand Up @@ -132,7 +133,7 @@ export class StopInput extends Component<Props, State> {
<EuiComboBox
options={suggestionOptions}
selectedOptions={selectedOptions}
singleSelection={{ asPlainText: true }}
singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS}
onChange={this._onChange}
onSearchChange={this._onSearchChange}
onCreateOption={this._onCreateOption}
Expand Down
85 changes: 44 additions & 41 deletions x-pack/plugins/maps/public/components/single_field_select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,61 @@

import _ from 'lodash';
import React from 'react';

import {
EuiComboBox,
EuiComboBoxProps,
EuiComboBoxOptionOption,
EuiHighlight,
EuiFlexGroup,
EuiFlexItem,
EuiToolTip,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { EuiComboBox, EuiComboBoxProps, EuiComboBoxOptionOption, EuiToolTip } from '@elastic/eui';
import { FieldIcon } from '@kbn/react-field';
import { DataViewField } from '@kbn/data-views-plugin/public';
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
import { MIDDLE_TRUNCATION_PROPS } from '../../common/constants';

function fieldsToOptions(
fields?: DataViewField[],
isFieldDisabled?: (field: DataViewField) => boolean
isFieldDisabled?: (field: DataViewField) => boolean,
getFieldDisabledReason?: (field: DataViewField) => string | null
): Array<EuiComboBoxOptionOption<DataViewField>> {
if (!fields) {
return [];
}

return fields
.map((field) => {
const FieldTypeIcon = field.type ? (
<FieldIcon type={field.type} fill="none" className="eui-alignMiddle" />
) : null;
const option: EuiComboBoxOptionOption<DataViewField> = {
value: field,
label: field.displayName ? field.displayName : field.name,
prepend: FieldTypeIcon,
};
if (isFieldDisabled && isFieldDisabled(field)) {
option.disabled = true;

const disabledReason =
option.disabled && getFieldDisabledReason ? getFieldDisabledReason(option.value!) : null;

if (disabledReason) {
option.prepend = (
<>
{FieldTypeIcon}
<EuiToolTip
position="left"
content={disabledReason}
anchorProps={{
css: css`
position: absolute;
width: 100%;
top: 0;
bottom: 0;
left: 0;
right: 0;
`,
}}
>
<div />
</EuiToolTip>
</>
);
}
}
return option;
})
Expand Down Expand Up @@ -63,34 +89,6 @@ export function SingleFieldSelect({
value,
...rest
}: Props) {
function renderOption(
option: EuiComboBoxOptionOption<DataViewField>,
searchValue: string,
contentClassName: string
) {
const content = (
<EuiFlexGroup className={contentClassName} gutterSize="s" alignItems="center">
<EuiFlexItem grow={null}>
<FieldIcon type={option.value!.type} fill="none" />
</EuiFlexItem>
<EuiFlexItem>
<EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
</EuiFlexItem>
</EuiFlexGroup>
);

const disabledReason =
option.disabled && getFieldDisabledReason ? getFieldDisabledReason(option.value!) : null;

return disabledReason ? (
<EuiToolTip position="left" content={disabledReason}>
{content}
</EuiToolTip>
) : (
content
);
}

const onSelection = (selectedOptions: Array<EuiComboBoxOptionOption<DataViewField>>) => {
onChange(_.get(selectedOptions, '0.value.name'));
};
Expand All @@ -108,14 +106,19 @@ export function SingleFieldSelect({
}
}

const options = fieldsToOptions(fields, isFieldDisabled, getFieldDisabledReason);

const panelMinWidth = calculateWidthFromEntries(options, ['label']);

return (
<EuiComboBox
singleSelection={true}
options={fieldsToOptions(fields, isFieldDisabled)}
options={options}
selectedOptions={selectedOptions}
onChange={onSelection}
isDisabled={!fields || fields.length === 0}
renderOption={renderOption}
truncationProps={MIDDLE_TRUNCATION_PROPS}
inputPopoverProps={{ panelMinWidth }}
{...rest}
/>
);
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/maps/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"@kbn/serverless",
"@kbn/logging",
"@kbn/search-response-warnings",
"@kbn/calculate-width-from-char-count",
],
"exclude": [
"target/**/*",
Expand Down

0 comments on commit 37d5aca

Please sign in to comment.