mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Lens][Unified search] Auto expand comboboxes and popovers based on the content (#171573)
## Summary Fixes partially two remaining tasks from https://github.com/elastic/kibana/issues/168753 Fixes partially dataview issue from https://github.com/elastic/kibana/issues/170398 It stretches to maximum approximate 60 characters if any of the labels in the list is of this length. If the content doesn't need the container to stretch, it doesn't do it. <details> <summary> Field picker in Lens</summary> minimum width: <img width="445" alt="Screenshot 2023-11-21 at 15 56 03" src="2f0f8482
-bd00-4ec2-bbde-cbc4f3198eed"> auto-expanded width: <img width="575" alt="Screenshot 2023-11-21 at 15 58 22" src="df7bab4d
-0a08-4d49-8a91-9386eba15d93"> </details> <details> <summary>Layer data view picker in Lens</summary> <img width="376" alt="Screenshot 2023-11-21 at 16 01 17" src="b8a98d83
-dabc-49bd-a3cc-fc3856de6d3e"> <img width="455" alt="Screenshot 2023-11-21 at 15 58 09" src="f2c5bde8
-7a4a-485f-bf97-fc2179171e50"> <img width="615" alt="Screenshot 2023-11-21 at 15 56 27" src="0574fc6c
-69a3-44e9-9d48-8d427c1c5dba"> </details> <details> <summary>Data view picker in Unified Search</summary> <img width="341" alt="Screenshot 2023-11-21 at 16 00 29" src="1c838ded
-0dc5-4632-94e4-1d94586f667c"> <img width="441" alt="Screenshot 2023-11-21 at 15 58 04" src="87e4f1c0
-7922-4b94-a114-f23ece544395"> <img width="561" alt="Screenshot 2023-11-21 at 15 56 20" src="3ea0f222
-5241-4c5b-b00b-4311972754cc"> </details> <details> <summary> Data view picker in dashboard Create control flyout</summary> <img width="677" alt="Screenshot 2023-11-21 at 16 14 00" src="0455b6ed
-555d-4cff-9e34-0de377be6e04"> <img width="682" alt="Screenshot 2023-11-21 at 15 54 56" src="2a67685c
-379d-4c0b-bf56-dbf7c35b3bd4"> </details> <details> <summary> Unified search data view select component (tested in maps)</summary> <img width="570" alt="Screenshot 2023-11-22 at 14 38 25" src="bb52ab22
-626d-4556-b40c-c9bcc925f426"> </details> <details> <summary>Unified search field and value picker</summary> Adds `panelMinWidth`, removes the custom flex width change behavior <img width="1142" alt="Screenshot 2023-11-22 at 14 40 26" src="2450957f
-38b7-4a73-b531-7acb29cb56bc">f4f33624
-9287-403e-8472-81f705440f97 </details> <details> <summary> Discover breakdown field</summary> Removes the focus stretching and instead uses the panelMinWidth prop <img width="419" alt="Screenshot 2023-11-21 at 16 46 50" src="e35125ad
-8823-4bff-954b-8119a352829c"> <img width="619" alt="Screenshot 2023-11-21 at 16 48 20" src="89e63daf
-a59e-43e1-a6ec-91d1b15b0fcd"> </details> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
a4aa7117bb
commit
085878c289
34 changed files with 519 additions and 188 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -55,6 +55,7 @@ packages/kbn-bazel-runner @elastic/kibana-operations
|
|||
examples/bfetch_explorer @elastic/appex-sharedux
|
||||
src/plugins/bfetch @elastic/appex-sharedux
|
||||
packages/kbn-calculate-auto @elastic/obs-ux-management-team
|
||||
packages/kbn-calculate-width-from-char-count @elastic/kibana-visualizations
|
||||
x-pack/plugins/canvas @elastic/kibana-presentation
|
||||
x-pack/test/cases_api_integration/common/plugins/cases @elastic/response-ops
|
||||
packages/kbn-cases-components @elastic/response-ops
|
||||
|
|
|
@ -167,6 +167,7 @@
|
|||
"@kbn/bfetch-explorer-plugin": "link:examples/bfetch_explorer",
|
||||
"@kbn/bfetch-plugin": "link:src/plugins/bfetch",
|
||||
"@kbn/calculate-auto": "link:packages/kbn-calculate-auto",
|
||||
"@kbn/calculate-width-from-char-count": "link:packages/kbn-calculate-width-from-char-count",
|
||||
"@kbn/canvas-plugin": "link:x-pack/plugins/canvas",
|
||||
"@kbn/cases-api-integration-test-plugin": "link:x-pack/test/cases_api_integration/common/plugins/cases",
|
||||
"@kbn/cases-components": "link:packages/kbn-cases-components",
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = require('@kbn/storybook').defaultConfig;
|
3
packages/kbn-calculate-width-from-char-count/README.md
Normal file
3
packages/kbn-calculate-width-from-char-count/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# @kbn/calculate-width-from-char-count
|
||||
|
||||
This package contains a function that calculates the approximate width of the component from a text length.
|
9
packages/kbn-calculate-width-from-char-count/index.ts
Normal file
9
packages/kbn-calculate-width-from-char-count/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './src';
|
13
packages/kbn-calculate-width-from-char-count/jest.config.js
Normal file
13
packages/kbn-calculate-width-from-char-count/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test/jest_node',
|
||||
rootDir: '../..',
|
||||
roots: ['<rootDir>/packages/kbn-calculate-width-from-char-count'],
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/calculate-width-from-char-count",
|
||||
"owner": "@elastic/kibana-visualizations"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "@kbn/calculate-width-from-char-count",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0",
|
||||
"sideEffects": false
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { calculateWidthFromCharCount, MAX_WIDTH } from './calculate_width_from_char_count';
|
||||
|
||||
describe('calculateWidthFromCharCount', () => {
|
||||
it('should return minimum width if char count is smaller than minWidth', () => {
|
||||
expect(calculateWidthFromCharCount(10, { minWidth: 300 })).toBe(300);
|
||||
});
|
||||
it('should return calculated width', () => {
|
||||
expect(calculateWidthFromCharCount(30)).toBe(30 * 7 + 116);
|
||||
});
|
||||
it('should return maximum width if char count is bigger than maxWidth', () => {
|
||||
expect(calculateWidthFromCharCount(1000)).toBe(MAX_WIDTH);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export interface LIMITS {
|
||||
paddingsWidth: number;
|
||||
minWidth?: number;
|
||||
avCharWidth: number;
|
||||
maxWidth: number;
|
||||
}
|
||||
|
||||
export const MAX_WIDTH = 550;
|
||||
const PADDINGS_WIDTH = 116;
|
||||
const AVERAGE_CHAR_WIDTH = 7;
|
||||
|
||||
const defaultPanelWidths: LIMITS = {
|
||||
maxWidth: MAX_WIDTH,
|
||||
avCharWidth: AVERAGE_CHAR_WIDTH,
|
||||
paddingsWidth: PADDINGS_WIDTH,
|
||||
};
|
||||
|
||||
export function calculateWidthFromCharCount(
|
||||
labelLength: number,
|
||||
overridesPanelWidths?: Partial<LIMITS>
|
||||
) {
|
||||
const { maxWidth, avCharWidth, paddingsWidth, minWidth } = {
|
||||
...defaultPanelWidths,
|
||||
...overridesPanelWidths,
|
||||
};
|
||||
const widthForCharCount = paddingsWidth + labelLength * avCharWidth;
|
||||
|
||||
if (minWidth && widthForCharCount < minWidth) {
|
||||
return minWidth;
|
||||
}
|
||||
|
||||
return Math.min(widthForCharCount, maxWidth);
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 { calculateWidthFromEntries } from './calculate_width_from_entries';
|
||||
import { MAX_WIDTH } from './calculate_width_from_char_count';
|
||||
import faker from 'faker';
|
||||
|
||||
const generateLabel = (length: number) => faker.random.alpha({ count: length });
|
||||
|
||||
const generateObjectWithLabelOfLength = (length: number, propOverrides?: Record<string, any>) => ({
|
||||
label: generateLabel(length),
|
||||
...propOverrides,
|
||||
});
|
||||
|
||||
describe('calculateWidthFromEntries', () => {
|
||||
it('calculates width for array of strings', () => {
|
||||
const shortLabels = [10, 20].map(generateLabel);
|
||||
expect(calculateWidthFromEntries(shortLabels)).toBe(256);
|
||||
|
||||
const mediumLabels = [50, 55, 10, 20].map(generateLabel);
|
||||
expect(calculateWidthFromEntries(mediumLabels)).toBe(501);
|
||||
|
||||
const longLabels = [80, 90, 10].map(generateLabel);
|
||||
expect(calculateWidthFromEntries(longLabels)).toBe(MAX_WIDTH);
|
||||
});
|
||||
|
||||
it('calculates width for array of objects with keys', () => {
|
||||
const shortLabels = [10, 20].map((v) => generateObjectWithLabelOfLength(v));
|
||||
expect(calculateWidthFromEntries(shortLabels, ['label'])).toBe(256);
|
||||
|
||||
const mediumLabels = [50, 55, 10, 20].map((v) => generateObjectWithLabelOfLength(v));
|
||||
expect(calculateWidthFromEntries(mediumLabels, ['label'])).toBe(501);
|
||||
|
||||
const longLabels = [80, 90, 10].map((v) => generateObjectWithLabelOfLength(v));
|
||||
expect(calculateWidthFromEntries(longLabels, ['label'])).toBe(MAX_WIDTH);
|
||||
});
|
||||
it('calculates width for array of objects for fallback keys', () => {
|
||||
const shortLabels = [10, 20].map((v) =>
|
||||
generateObjectWithLabelOfLength(v, { label: undefined, name: generateLabel(v) })
|
||||
);
|
||||
expect(calculateWidthFromEntries(shortLabels, ['id', 'label', 'name'])).toBe(256);
|
||||
|
||||
const mediumLabels = [50, 55, 10, 20].map((v) =>
|
||||
generateObjectWithLabelOfLength(v, { label: undefined, name: generateLabel(v) })
|
||||
);
|
||||
expect(calculateWidthFromEntries(mediumLabels, ['id', 'label', 'name'])).toBe(501);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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 { LIMITS, calculateWidthFromCharCount } from './calculate_width_from_char_count';
|
||||
|
||||
type GenericObject<T = Record<string, any>> = T;
|
||||
|
||||
const getMaxLabelLengthForObjects = (
|
||||
entries: GenericObject[],
|
||||
labelKeys: Array<keyof GenericObject>
|
||||
) =>
|
||||
entries.reduce((acc, curr) => {
|
||||
const labelKey = labelKeys.find((key) => curr[key]);
|
||||
if (!labelKey) {
|
||||
return acc;
|
||||
}
|
||||
const labelLength = curr[labelKey].length;
|
||||
return acc > labelLength ? acc : labelLength;
|
||||
}, 0);
|
||||
|
||||
const getMaxLabelLengthForStrings = (arr: string[]) =>
|
||||
arr.reduce((acc, curr) => (acc > curr.length ? acc : curr.length), 0);
|
||||
|
||||
export function calculateWidthFromEntries(
|
||||
entries: GenericObject[] | string[],
|
||||
labelKeys?: Array<keyof GenericObject>,
|
||||
overridesPanelWidths?: Partial<LIMITS>
|
||||
) {
|
||||
const maxLabelLength = labelKeys
|
||||
? getMaxLabelLengthForObjects(entries as GenericObject[], labelKeys)
|
||||
: getMaxLabelLengthForStrings(entries as string[]);
|
||||
|
||||
return calculateWidthFromCharCount(maxLabelLength, overridesPanelWidths);
|
||||
}
|
11
packages/kbn-calculate-width-from-char-count/src/index.ts
Normal file
11
packages/kbn-calculate-width-from-char-count/src/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { calculateWidthFromCharCount } from './calculate_width_from_char_count';
|
||||
|
||||
export { calculateWidthFromEntries } from './calculate_width_from_entries';
|
19
packages/kbn-calculate-width-from-char-count/tsconfig.json
Normal file
19
packages/kbn-calculate-width-from-char-count/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react",
|
||||
],
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"kbn_references": [],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
]
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { FieldPicker, FieldPickerProps } from './field_picker';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import faker from 'faker';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { DataType, FieldOptionValue } from './types';
|
||||
|
||||
const generateFieldWithLabelOfLength = (length: number) => ({
|
||||
label: faker.random.alpha({ count: length }),
|
||||
value: {
|
||||
type: 'field' as const,
|
||||
field: faker.random.alpha({ count: length }),
|
||||
dataType: 'date' as DataType,
|
||||
operationType: 'count',
|
||||
},
|
||||
exists: true,
|
||||
compatible: 1,
|
||||
});
|
||||
|
||||
const generateProps = (customField = generateFieldWithLabelOfLength(20)) =>
|
||||
({
|
||||
selectedOptions: [
|
||||
{
|
||||
label: 'Category',
|
||||
value: {
|
||||
type: 'field' as const,
|
||||
field: 'category.keyword',
|
||||
dataType: 'keyword' as DataType,
|
||||
operationType: 'count',
|
||||
},
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
label: 'nested options',
|
||||
exists: true,
|
||||
compatible: 1,
|
||||
value: generateFieldWithLabelOfLength(20),
|
||||
options: [
|
||||
generateFieldWithLabelOfLength(20),
|
||||
customField,
|
||||
generateFieldWithLabelOfLength(20),
|
||||
],
|
||||
},
|
||||
],
|
||||
onChoose: jest.fn(),
|
||||
fieldIsInvalid: false,
|
||||
} as unknown as FieldPickerProps<FieldOptionValue>);
|
||||
|
||||
describe('field picker', () => {
|
||||
const renderFieldPicker = (customField = generateFieldWithLabelOfLength(20)) => {
|
||||
const props = generateProps(customField);
|
||||
const rtlRender = render(<FieldPicker {...props} />);
|
||||
return {
|
||||
openCombobox: () => userEvent.click(screen.getByLabelText(/open list of options/i)),
|
||||
...rtlRender,
|
||||
};
|
||||
};
|
||||
|
||||
it('should render minimum width dropdown list if all labels are short', async () => {
|
||||
const { openCombobox } = renderFieldPicker();
|
||||
openCombobox();
|
||||
const popover = screen.getByRole('dialog');
|
||||
expect(popover).toHaveStyle('inline-size: 256px');
|
||||
});
|
||||
|
||||
it('should render calculated width dropdown list if the longest label is longer than min width', async () => {
|
||||
const { openCombobox } = renderFieldPicker(generateFieldWithLabelOfLength(50));
|
||||
openCombobox();
|
||||
|
||||
const popover = screen.getByRole('dialog');
|
||||
expect(popover).toHaveStyle('inline-size: 466px');
|
||||
});
|
||||
|
||||
it('should render maximum width dropdown list if the longest label is longer than max width', async () => {
|
||||
const { openCombobox } = renderFieldPicker(generateFieldWithLabelOfLength(80));
|
||||
openCombobox();
|
||||
const popover = screen.getByRole('dialog');
|
||||
expect(popover).toHaveStyle('inline-size: 550px');
|
||||
});
|
||||
});
|
|
@ -9,9 +9,10 @@
|
|||
import './field_picker.scss';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import classNames from 'classnames';
|
||||
import { EuiComboBox, EuiComboBoxProps } from '@elastic/eui';
|
||||
import { FieldIcon } from '@kbn/field-utils/src/components/field_icon';
|
||||
import classNames from 'classnames';
|
||||
import { calculateWidthFromCharCount } from '@kbn/calculate-width-from-char-count';
|
||||
import type { FieldOptionValue, FieldOption } from './types';
|
||||
|
||||
export interface FieldPickerProps<T extends FieldOptionValue>
|
||||
|
@ -27,23 +28,26 @@ export interface FieldPickerProps<T extends FieldOptionValue>
|
|||
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 = '';
|
||||
export function FieldPicker<T extends FieldOptionValue = FieldOptionValue>(
|
||||
props: FieldPickerProps<T>
|
||||
) {
|
||||
const {
|
||||
selectedOptions,
|
||||
options,
|
||||
onChoose,
|
||||
onDelete,
|
||||
fieldIsInvalid,
|
||||
['data-test-subj']: dataTestSub,
|
||||
...rest
|
||||
} = props;
|
||||
let maxLabelLength = 0;
|
||||
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;
|
||||
if (fieldOption.label.length > maxLabelLength) {
|
||||
maxLabelLength = fieldOption.label.length;
|
||||
}
|
||||
return {
|
||||
...fieldOption,
|
||||
|
@ -75,7 +79,6 @@ export function FieldPicker<T extends FieldOptionValue = FieldOptionValue>({
|
|||
};
|
||||
});
|
||||
|
||||
const panelMinWidth = getPanelMinWidth(theLongestLabel.length);
|
||||
return (
|
||||
<EuiComboBox
|
||||
fullWidth
|
||||
|
@ -90,7 +93,10 @@ export function FieldPicker<T extends FieldOptionValue = FieldOptionValue>({
|
|||
selectedOptions={selectedOptions}
|
||||
singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS}
|
||||
truncationProps={MIDDLE_TRUNCATION_PROPS}
|
||||
inputPopoverProps={{ panelMinWidth }}
|
||||
inputPopoverProps={{
|
||||
panelMinWidth: calculateWidthFromCharCount(maxLabelLength),
|
||||
anchorPosition: 'downRight',
|
||||
}}
|
||||
onChange={(choices) => {
|
||||
if (choices.length === 0) {
|
||||
onDelete?.();
|
||||
|
@ -102,20 +108,3 @@ export function FieldPicker<T extends FieldOptionValue = FieldOptionValue>({
|
|||
/>
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -31,5 +31,6 @@
|
|||
"@kbn/coloring",
|
||||
"@kbn/field-formats-plugin",
|
||||
"@kbn/field-utils",
|
||||
"@kbn/calculate-width-from-char-count"
|
||||
],
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import React, { useState } from 'react';
|
||||
import { EuiSelectable, EuiInputPopover, EuiSelectableProps } from '@elastic/eui';
|
||||
import { DataViewListItem } from '@kbn/data-views-plugin/common';
|
||||
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
|
||||
|
||||
import { ToolbarButton, ToolbarButtonProps } from '@kbn/shared-ux-button-toolbar';
|
||||
|
||||
|
@ -67,6 +68,7 @@ export function DataViewPicker({
|
|||
isOpen={isPopoverOpen}
|
||||
input={createTrigger()}
|
||||
closePopover={() => setPopoverIsOpen(false)}
|
||||
panelMinWidth={calculateWidthFromEntries(dataViews, ['name', 'id'])}
|
||||
panelProps={{
|
||||
'data-test-subj': 'data-view-picker-popover',
|
||||
}}
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
"@kbn/ui-actions-plugin",
|
||||
"@kbn/saved-objects-finder-plugin",
|
||||
"@kbn/content-management-plugin",
|
||||
"@kbn/shared-ux-button-toolbar"
|
||||
"@kbn/shared-ux-button-toolbar",
|
||||
"@kbn/calculate-width-from-char-count"
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import { EuiComboBox, EuiComboBoxOptionOption, EuiToolTip, useEuiTheme } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { DataView, DataViewField } from '@kbn/data-views-plugin/common';
|
||||
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { UnifiedHistogramBreakdownContext } from '../types';
|
||||
|
@ -59,11 +60,10 @@ export const BreakdownFieldSelector = ({
|
|||
const breakdownCss = css`
|
||||
width: 100%;
|
||||
max-width: ${euiTheme.base * 22}px;
|
||||
&:focus-within {
|
||||
max-width: ${euiTheme.base * 30}px;
|
||||
}
|
||||
`;
|
||||
|
||||
const panelMinWidth = calculateWidthFromEntries(fieldOptions, ['label']);
|
||||
|
||||
return (
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
|
@ -81,6 +81,7 @@ export const BreakdownFieldSelector = ({
|
|||
aria-label={i18n.translate('unifiedHistogram.breakdownFieldSelectorAriaLabel', {
|
||||
defaultMessage: 'Break down by',
|
||||
})}
|
||||
inputPopoverProps={{ panelMinWidth, anchorPosition: 'downRight' }}
|
||||
singleSelection={SINGLE_SELECTION}
|
||||
options={fieldOptions}
|
||||
selectedOptions={selectedFields}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"@kbn/visualizations-plugin",
|
||||
"@kbn/discover-utils",
|
||||
"@kbn/resizable-layout",
|
||||
"@kbn/calculate-width-from-char-count",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -6,15 +6,24 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const DATA_VIEW_POPOVER_CONTENT_WIDTH = 280;
|
||||
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
|
||||
import { DataViewListItemEnhanced } from './dataview_list';
|
||||
|
||||
export const changeDataViewStyles = ({ fullWidth }: { fullWidth?: boolean }) => {
|
||||
const MIN_WIDTH = 300;
|
||||
|
||||
export const changeDataViewStyles = ({
|
||||
fullWidth,
|
||||
dataViewsList,
|
||||
}: {
|
||||
fullWidth?: boolean;
|
||||
dataViewsList: DataViewListItemEnhanced[];
|
||||
}) => {
|
||||
return {
|
||||
trigger: {
|
||||
maxWidth: fullWidth ? undefined : DATA_VIEW_POPOVER_CONTENT_WIDTH,
|
||||
maxWidth: fullWidth ? undefined : MIN_WIDTH,
|
||||
},
|
||||
popoverContent: {
|
||||
width: DATA_VIEW_POPOVER_CONTENT_WIDTH,
|
||||
width: calculateWidthFromEntries(dataViewsList, ['name', 'id'], { minWidth: MIN_WIDTH }),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -96,7 +96,9 @@ export function ChangeDataView({
|
|||
const { application, data, storage, dataViews, dataViewEditor, appName, usageCollection } =
|
||||
kibana.services;
|
||||
const reportUiCounter = usageCollection?.reportUiCounter.bind(usageCollection, appName);
|
||||
const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth });
|
||||
|
||||
const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth, dataViewsList });
|
||||
|
||||
const [isTextLangTransitionModalDismissed, setIsTextLangTransitionModalDismissed] = useState(() =>
|
||||
Boolean(storage.get(TEXT_LANG_TRANSITION_MODAL_KEY))
|
||||
);
|
||||
|
|
|
@ -10,6 +10,7 @@ import { InjectedIntl, injectI18n } from '@kbn/i18n-react';
|
|||
import { uniq } from 'lodash';
|
||||
import React from 'react';
|
||||
import { withKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
|
||||
import { GenericComboBox, GenericComboBoxProps } from './generic_combo_box';
|
||||
import { PhraseSuggestorUI, PhraseSuggestorProps } from './phrase_suggestor';
|
||||
import { ValueInputType } from './value_input_type';
|
||||
|
@ -26,7 +27,6 @@ interface PhraseValueInputProps extends PhraseSuggestorProps {
|
|||
}
|
||||
|
||||
class PhraseValueInputUI extends PhraseSuggestorUI<PhraseValueInputProps> {
|
||||
comboBoxWrapperRef = React.createRef<HTMLDivElement>();
|
||||
inputRef: HTMLInputElement | null = null;
|
||||
|
||||
public render() {
|
||||
|
@ -59,43 +59,39 @@ class PhraseValueInputUI extends PhraseSuggestorUI<PhraseValueInputProps> {
|
|||
// there are cases when the value is a number, this would cause an exception
|
||||
const valueAsStr = String(value);
|
||||
const options = value ? uniq([valueAsStr, ...suggestions]) : suggestions;
|
||||
const panelMinWidth = calculateWidthFromEntries(options);
|
||||
return (
|
||||
<div ref={this.comboBoxWrapperRef}>
|
||||
<StringComboBox
|
||||
async
|
||||
isLoading={isLoading}
|
||||
inputRef={(ref) => {
|
||||
this.inputRef = ref;
|
||||
}}
|
||||
isDisabled={this.props.disabled}
|
||||
fullWidth={fullWidth}
|
||||
compressed={this.props.compressed}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'unifiedSearch.filter.filterEditor.valueSelectPlaceholder',
|
||||
defaultMessage: 'Select a value',
|
||||
})}
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'unifiedSearch.filter.filterEditor.valueSelectPlaceholder',
|
||||
defaultMessage: 'Select a value',
|
||||
})}
|
||||
options={options}
|
||||
getLabel={(option) => option}
|
||||
selectedOptions={value ? [valueAsStr] : []}
|
||||
onChange={([newValue = '']) => {
|
||||
onChange(newValue);
|
||||
setTimeout(() => {
|
||||
// Note: requires a tick skip to correctly blur element focus
|
||||
this.inputRef?.blur();
|
||||
});
|
||||
}}
|
||||
onSearchChange={this.onSearchChange}
|
||||
onCreateOption={onChange}
|
||||
isClearable={false}
|
||||
data-test-subj="filterParamsComboBox phraseParamsComboxBox"
|
||||
singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS}
|
||||
truncationProps={MIDDLE_TRUNCATION_PROPS}
|
||||
/>
|
||||
</div>
|
||||
<StringComboBox
|
||||
async
|
||||
isLoading={isLoading}
|
||||
inputRef={(ref) => {
|
||||
this.inputRef = ref;
|
||||
}}
|
||||
isDisabled={this.props.disabled}
|
||||
fullWidth={fullWidth}
|
||||
compressed={this.props.compressed}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'unifiedSearch.filter.filterEditor.valueSelectPlaceholder',
|
||||
defaultMessage: 'Select a value',
|
||||
})}
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'unifiedSearch.filter.filterEditor.valueSelectPlaceholder',
|
||||
defaultMessage: 'Select a value',
|
||||
})}
|
||||
options={options}
|
||||
getLabel={(option) => option}
|
||||
selectedOptions={value ? [valueAsStr] : []}
|
||||
onChange={([newValue = '']) => {
|
||||
onChange(newValue);
|
||||
}}
|
||||
onSearchChange={this.onSearchChange}
|
||||
onCreateOption={onChange}
|
||||
isClearable={false}
|
||||
data-test-subj="filterParamsComboBox phraseParamsComboxBox"
|
||||
singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS}
|
||||
truncationProps={MIDDLE_TRUNCATION_PROPS}
|
||||
inputPopoverProps={{ panelMinWidth, anchorPosition: 'downRight' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { uniq } from 'lodash';
|
|||
import React from 'react';
|
||||
import { withKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { withEuiTheme, WithEuiThemeProps } from '@elastic/eui';
|
||||
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
|
||||
import { GenericComboBox, GenericComboBoxProps } from './generic_combo_box';
|
||||
import { PhraseSuggestorUI, PhraseSuggestorProps } from './phrase_suggestor';
|
||||
import { phrasesValuesComboboxCss } from './phrases_values_input.styles';
|
||||
|
@ -28,45 +29,42 @@ interface Props {
|
|||
|
||||
export type PhrasesValuesInputProps = Props & PhraseSuggestorProps & WithEuiThemeProps;
|
||||
class PhrasesValuesInputUI extends PhraseSuggestorUI<PhrasesValuesInputProps> {
|
||||
comboBoxWrapperRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
public render() {
|
||||
const { suggestions, isLoading } = this.state;
|
||||
const { values, intl, onChange, fullWidth, onParamsUpdate, compressed, disabled } = this.props;
|
||||
const options = values ? uniq([...values, ...suggestions]) : suggestions;
|
||||
|
||||
const panelMinWidth = calculateWidthFromEntries(options);
|
||||
return (
|
||||
<div ref={this.comboBoxWrapperRef}>
|
||||
<StringComboBox
|
||||
async
|
||||
isLoading={isLoading}
|
||||
fullWidth={fullWidth}
|
||||
compressed={compressed}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'unifiedSearch.filter.filterEditor.valuesSelectPlaceholder',
|
||||
defaultMessage: 'Select values',
|
||||
})}
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'unifiedSearch.filter.filterEditor.valuesSelectPlaceholder',
|
||||
defaultMessage: 'Select values',
|
||||
})}
|
||||
delimiter=","
|
||||
isCaseSensitive={true}
|
||||
options={options}
|
||||
getLabel={(option) => option}
|
||||
selectedOptions={values || []}
|
||||
onSearchChange={this.onSearchChange}
|
||||
onCreateOption={(option: string) => {
|
||||
onParamsUpdate(option.trim());
|
||||
}}
|
||||
className={phrasesValuesComboboxCss(this.props.theme)}
|
||||
onChange={onChange}
|
||||
isClearable={false}
|
||||
data-test-subj="filterParamsComboBox phrasesParamsComboxBox"
|
||||
isDisabled={disabled}
|
||||
truncationProps={MIDDLE_TRUNCATION_PROPS}
|
||||
/>
|
||||
</div>
|
||||
<StringComboBox
|
||||
async
|
||||
isLoading={isLoading}
|
||||
fullWidth={fullWidth}
|
||||
compressed={compressed}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'unifiedSearch.filter.filterEditor.valuesSelectPlaceholder',
|
||||
defaultMessage: 'Select values',
|
||||
})}
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'unifiedSearch.filter.filterEditor.valuesSelectPlaceholder',
|
||||
defaultMessage: 'Select values',
|
||||
})}
|
||||
delimiter=","
|
||||
isCaseSensitive={true}
|
||||
options={options}
|
||||
getLabel={(option) => option}
|
||||
selectedOptions={values || []}
|
||||
onSearchChange={this.onSearchChange}
|
||||
onCreateOption={(option: string) => {
|
||||
onParamsUpdate(option.trim());
|
||||
}}
|
||||
className={phrasesValuesComboboxCss(this.props.theme)}
|
||||
onChange={onChange}
|
||||
isClearable={false}
|
||||
data-test-subj="filterParamsComboBox phrasesParamsComboxBox"
|
||||
isDisabled={disabled}
|
||||
truncationProps={MIDDLE_TRUNCATION_PROPS}
|
||||
inputPopoverProps={{ panelMinWidth, anchorPosition: 'downRight' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FieldIcon } from '@kbn/react-field';
|
||||
import { KBN_FIELD_TYPES } from '@kbn/field-types';
|
||||
import type { DataView, DataViewField } from '@kbn/data-views-plugin/common';
|
||||
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
|
||||
import { useGeneratedHtmlId, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
|
||||
import { getFilterableFields } from '../../filter_bar/filter_editor';
|
||||
import { FiltersBuilderContextType } from '../context';
|
||||
|
@ -36,7 +37,6 @@ export function FieldInput({ field, dataView, onHandleField }: FieldInputProps)
|
|||
const { disabled, suggestionsAbstraction } = useContext(FiltersBuilderContextType);
|
||||
const fields = dataView ? getFilterableFields(dataView) : [];
|
||||
const id = useGeneratedHtmlId({ prefix: 'fieldInput' });
|
||||
const comboBoxWrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const onFieldChange = useCallback(
|
||||
|
@ -72,40 +72,30 @@ 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();
|
||||
};
|
||||
const panelMinWidth = calculateWidthFromEntries(euiOptions, ['label']);
|
||||
|
||||
return (
|
||||
<div ref={comboBoxWrapperRef}>
|
||||
<EuiComboBox
|
||||
id={id}
|
||||
inputRef={(ref) => {
|
||||
inputRef.current = ref;
|
||||
}}
|
||||
options={euiOptions}
|
||||
selectedOptions={selectedEuiOptions}
|
||||
onChange={onComboBoxChange}
|
||||
isDisabled={disabled}
|
||||
placeholder={strings.getFieldSelectPlaceholderLabel()}
|
||||
sortMatchesBy="startsWith"
|
||||
aria-label={strings.getFieldSelectPlaceholderLabel()}
|
||||
isClearable={false}
|
||||
compressed
|
||||
fullWidth
|
||||
onFocus={handleFocus}
|
||||
data-test-subj="filterFieldSuggestionList"
|
||||
singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS}
|
||||
truncationProps={MIDDLE_TRUNCATION_PROPS}
|
||||
/>
|
||||
</div>
|
||||
<EuiComboBox
|
||||
id={id}
|
||||
inputRef={(ref) => {
|
||||
inputRef.current = ref;
|
||||
}}
|
||||
options={euiOptions}
|
||||
selectedOptions={selectedEuiOptions}
|
||||
onChange={onComboBoxChange}
|
||||
isDisabled={disabled}
|
||||
placeholder={strings.getFieldSelectPlaceholderLabel()}
|
||||
sortMatchesBy="startsWith"
|
||||
aria-label={strings.getFieldSelectPlaceholderLabel()}
|
||||
isClearable={false}
|
||||
compressed
|
||||
fullWidth
|
||||
data-test-subj="filterFieldSuggestionList"
|
||||
singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS}
|
||||
truncationProps={MIDDLE_TRUNCATION_PROPS}
|
||||
inputPopoverProps={{ panelMinWidth }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,9 +26,6 @@ export const fieldAndParamCss = (euiTheme: EuiThemeComputed) => css`
|
|||
.euiFormRow {
|
||||
max-width: 800px;
|
||||
}
|
||||
&:focus-within {
|
||||
flex-grow: 4;
|
||||
}
|
||||
`;
|
||||
|
||||
export const operationCss = (euiTheme: EuiThemeComputed) => css`
|
||||
|
|
|
@ -11,7 +11,9 @@ import React, { Component } from 'react';
|
|||
|
||||
import { Required } from '@kbn/utility-types';
|
||||
import { EuiComboBox, EuiComboBoxProps } from '@elastic/eui';
|
||||
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
|
||||
import type { DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
import { MIDDLE_TRUNCATION_PROPS } from '../filter_bar/filter_editor/lib/helpers';
|
||||
|
||||
export type IndexPatternSelectProps = Required<
|
||||
Omit<EuiComboBoxProps<any>, 'onSearchChange' | 'options' | 'selectedOptions' | 'onChange'>,
|
||||
|
@ -28,7 +30,7 @@ export type IndexPatternSelectInternalProps = IndexPatternSelectProps & {
|
|||
|
||||
interface IndexPatternSelectState {
|
||||
isLoading: boolean;
|
||||
options: [];
|
||||
options: Array<{ value: string; label: string }>;
|
||||
selectedIndexPattern: { value: string; label: string } | undefined;
|
||||
searchValue: string | undefined;
|
||||
}
|
||||
|
@ -147,6 +149,8 @@ export default class IndexPatternSelect extends Component<IndexPatternSelectInte
|
|||
...rest
|
||||
} = this.props;
|
||||
|
||||
const panelMinWidth = calculateWidthFromEntries(this.state.options, ['label']);
|
||||
|
||||
return (
|
||||
<EuiComboBox
|
||||
{...rest}
|
||||
|
@ -157,6 +161,8 @@ export default class IndexPatternSelect extends Component<IndexPatternSelectInte
|
|||
options={this.state.options}
|
||||
selectedOptions={this.state.selectedIndexPattern ? [this.state.selectedIndexPattern] : []}
|
||||
onChange={this.onChange}
|
||||
truncationProps={MIDDLE_TRUNCATION_PROPS}
|
||||
inputPopoverProps={{ panelMinWidth }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
"@kbn/core-doc-links-browser",
|
||||
"@kbn/core-lifecycle-browser",
|
||||
"@kbn/ml-string-hash",
|
||||
"@kbn/calculate-width-from-char-count"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -104,6 +104,8 @@
|
|||
"@kbn/bfetch-plugin/*": ["src/plugins/bfetch/*"],
|
||||
"@kbn/calculate-auto": ["packages/kbn-calculate-auto"],
|
||||
"@kbn/calculate-auto/*": ["packages/kbn-calculate-auto/*"],
|
||||
"@kbn/calculate-width-from-char-count": ["packages/kbn-calculate-width-from-char-count"],
|
||||
"@kbn/calculate-width-from-char-count/*": ["packages/kbn-calculate-width-from-char-count/*"],
|
||||
"@kbn/canvas-plugin": ["x-pack/plugins/canvas"],
|
||||
"@kbn/canvas-plugin/*": ["x-pack/plugins/canvas/*"],
|
||||
"@kbn/cases-api-integration-test-plugin": ["x-pack/test/cases_api_integration/common/plugins/cases"],
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count';
|
||||
import React, { useState } from 'react';
|
||||
import { EuiPopover, EuiPopoverTitle, EuiSelectableProps } from '@elastic/eui';
|
||||
import { DataViewsList } from '@kbn/unified-search-plugin/public';
|
||||
import { css } from '@emotion/react';
|
||||
import { type IndexPatternRef } from '../../types';
|
||||
import { type ChangeIndexPatternTriggerProps, TriggerButton } from './trigger';
|
||||
|
||||
|
@ -30,43 +32,47 @@ export function ChangeIndexPattern({
|
|||
const [isPopoverOpen, setPopoverIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiPopover
|
||||
panelClassName="lnsChangeIndexPatternPopover"
|
||||
button={
|
||||
<TriggerButton
|
||||
{...trigger}
|
||||
isMissingCurrent={isMissingCurrent}
|
||||
togglePopover={() => setPopoverIsOpen(!isPopoverOpen)}
|
||||
/>
|
||||
}
|
||||
panelProps={{
|
||||
['data-test-subj']: 'lnsChangeIndexPatternPopover',
|
||||
}}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={() => setPopoverIsOpen(false)}
|
||||
display="block"
|
||||
panelPaddingSize="none"
|
||||
ownFocus
|
||||
<EuiPopover
|
||||
button={
|
||||
<TriggerButton
|
||||
{...trigger}
|
||||
isMissingCurrent={isMissingCurrent}
|
||||
togglePopover={() => setPopoverIsOpen(!isPopoverOpen)}
|
||||
/>
|
||||
}
|
||||
panelProps={{
|
||||
['data-test-subj']: 'lnsChangeIndexPatternPopover',
|
||||
}}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={() => setPopoverIsOpen(false)}
|
||||
display="block"
|
||||
panelPaddingSize="none"
|
||||
ownFocus
|
||||
>
|
||||
<div
|
||||
css={css`
|
||||
width: ${calculateWidthFromEntries(indexPatternRefs, ['name', 'id'], {
|
||||
minWidth: 320,
|
||||
maxWidth: 600,
|
||||
})}px;
|
||||
`}
|
||||
>
|
||||
<div>
|
||||
<EuiPopoverTitle paddingSize="s">
|
||||
{i18n.translate('xpack.lens.indexPattern.changeDataViewTitle', {
|
||||
defaultMessage: 'Data view',
|
||||
})}
|
||||
</EuiPopoverTitle>
|
||||
<EuiPopoverTitle paddingSize="s">
|
||||
{i18n.translate('xpack.lens.indexPattern.changeDataViewTitle', {
|
||||
defaultMessage: 'Data view',
|
||||
})}
|
||||
</EuiPopoverTitle>
|
||||
|
||||
<DataViewsList
|
||||
dataViewsList={indexPatternRefs}
|
||||
onChangeDataView={(newId) => {
|
||||
onChangeIndexPattern(newId);
|
||||
setPopoverIsOpen(false);
|
||||
}}
|
||||
currentDataViewId={indexPatternId}
|
||||
selectableProps={selectableProps}
|
||||
/>
|
||||
</div>
|
||||
</EuiPopover>
|
||||
</>
|
||||
<DataViewsList
|
||||
dataViewsList={indexPatternRefs}
|
||||
onChangeDataView={(newId) => {
|
||||
onChangeIndexPattern(newId);
|
||||
setPopoverIsOpen(false);
|
||||
}}
|
||||
currentDataViewId={indexPatternId}
|
||||
selectableProps={selectableProps}
|
||||
/>
|
||||
</div>
|
||||
</EuiPopover>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -170,7 +170,6 @@ function DataLayerHeader(props: VisualizationLayerWidgetProps<State>) {
|
|||
|
||||
return (
|
||||
<EuiPopover
|
||||
panelClassName="lnsChangeIndexPatternPopover"
|
||||
button={
|
||||
<DataLayerHeaderTrigger
|
||||
onClick={() => setPopoverIsOpen(!isPopoverOpen)}
|
||||
|
@ -188,7 +187,11 @@ function DataLayerHeader(props: VisualizationLayerWidgetProps<State>) {
|
|||
defaultMessage: 'Layer visualization type',
|
||||
})}
|
||||
</EuiPopoverTitle>
|
||||
<div>
|
||||
<div
|
||||
css={css`
|
||||
width: 320px;
|
||||
`}
|
||||
>
|
||||
<EuiSelectable<{
|
||||
key?: string;
|
||||
label: string;
|
||||
|
|
|
@ -92,9 +92,10 @@
|
|||
"@kbn/core-plugins-server",
|
||||
"@kbn/text-based-languages",
|
||||
"@kbn/field-utils",
|
||||
"@kbn/discover-utils",
|
||||
"@kbn/shared-ux-button-toolbar",
|
||||
"@kbn/cell-actions",
|
||||
"@kbn/shared-ux-button-toolbar"
|
||||
"@kbn/calculate-width-from-char-count",
|
||||
"@kbn/discover-utils"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -3145,6 +3145,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/calculate-width-from-char-count@link:packages/kbn-calculate-width-from-char-count":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/canvas-plugin@link:x-pack/plugins/canvas":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue