mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
New Select: decrease item height (#95952)
This commit is contained in:
parent
c8aaefd54a
commit
6a8755a8af
@ -166,7 +166,7 @@ describe('Combobox', () => {
|
|||||||
expect(onChangeHandler).toHaveBeenCalledWith(expect.objectContaining({ value: 'custom value' }));
|
expect(onChangeHandler).toHaveBeenCalledWith(expect.objectContaining({ value: 'custom value' }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should proivde custom string when all options are numbers', async () => {
|
it('should provide custom string when all options are numbers', async () => {
|
||||||
const options = [
|
const options = [
|
||||||
{ label: '1', value: 1 },
|
{ label: '1', value: 1 },
|
||||||
{ label: '2', value: 2 },
|
{ label: '2', value: 2 },
|
||||||
@ -333,7 +333,7 @@ describe('Combobox', () => {
|
|||||||
jest.advanceTimersByTime(500); // Custom value while typing
|
jest.advanceTimersByTime(500); // Custom value while typing
|
||||||
});
|
});
|
||||||
|
|
||||||
const customItem = screen.queryByRole('option', { name: 'fir Create custom value' });
|
const customItem = screen.queryByRole('option', { name: 'Custom value: fir' });
|
||||||
|
|
||||||
expect(customItem).toBeInTheDocument();
|
expect(customItem).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@ -13,8 +13,8 @@ import { Box } from '../Layout/Box/Box';
|
|||||||
import { Stack } from '../Layout/Stack/Stack';
|
import { Stack } from '../Layout/Stack/Stack';
|
||||||
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
|
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
|
||||||
|
|
||||||
import { getComboboxStyles } from './getComboboxStyles';
|
import { getComboboxStyles, MENU_OPTION_HEIGHT } from './getComboboxStyles';
|
||||||
import { useComboboxFloat, OPTION_HEIGHT } from './useComboboxFloat';
|
import { useComboboxFloat } from './useComboboxFloat';
|
||||||
import { StaleResultError, useLatestAsyncCall } from './useLatestAsyncCall';
|
import { StaleResultError, useLatestAsyncCall } from './useLatestAsyncCall';
|
||||||
|
|
||||||
export type ComboboxOption<T extends string | number = string> = {
|
export type ComboboxOption<T extends string | number = string> = {
|
||||||
@ -69,6 +69,9 @@ type AutoSizeConditionals =
|
|||||||
type ComboboxProps<T extends string | number> = ComboboxBaseProps<T> & AutoSizeConditionals;
|
type ComboboxProps<T extends string | number> = ComboboxBaseProps<T> & AutoSizeConditionals;
|
||||||
|
|
||||||
function itemToString<T extends string | number>(item: ComboboxOption<T> | null) {
|
function itemToString<T extends string | number>(item: ComboboxOption<T> | null) {
|
||||||
|
if (item?.label?.includes('Custom value: ')) {
|
||||||
|
return item?.value.toString();
|
||||||
|
}
|
||||||
return item?.label ?? item?.value.toString() ?? '';
|
return item?.label ?? item?.value.toString() ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,13 +125,18 @@ export const Combobox = <T extends string | number>(props: ComboboxProps<T>) =>
|
|||||||
let itemsToSet = items;
|
let itemsToSet = items;
|
||||||
|
|
||||||
if (inputValue && createCustomValue) {
|
if (inputValue && createCustomValue) {
|
||||||
const optionMatchingInput = items.find((opt) => opt.label === inputValue || opt.value === inputValue);
|
const optionMatchingInput = items.find(
|
||||||
|
(opt) => opt.label === 'Custom value: ' + inputValue || opt.value === inputValue
|
||||||
|
);
|
||||||
|
|
||||||
if (!optionMatchingInput) {
|
if (!optionMatchingInput) {
|
||||||
const customValueOption = {
|
const customValueOption = {
|
||||||
|
label: t('combobox.custom-value.label', 'Custom value: ') + inputValue,
|
||||||
// Type casting needed to make this work when T is a number
|
// Type casting needed to make this work when T is a number
|
||||||
value: inputValue as unknown as T,
|
value: inputValue as unknown as T,
|
||||||
|
/* TODO: Add this back when we do support descriptions and have need for it
|
||||||
description: t('combobox.custom-value.create', 'Create custom value'),
|
description: t('combobox.custom-value.create', 'Create custom value'),
|
||||||
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
itemsToSet = items.slice(0);
|
itemsToSet = items.slice(0);
|
||||||
@ -174,7 +182,7 @@ export const Combobox = <T extends string | number>(props: ComboboxProps<T>) =>
|
|||||||
const virtualizerOptions = {
|
const virtualizerOptions = {
|
||||||
count: items.length,
|
count: items.length,
|
||||||
getScrollElement: () => scrollRef.current,
|
getScrollElement: () => scrollRef.current,
|
||||||
estimateSize: () => OPTION_HEIGHT,
|
estimateSize: () => MENU_OPTION_HEIGHT,
|
||||||
overscan: 4,
|
overscan: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,7 +6,12 @@ import { GrafanaTheme2 } from '@grafana/data';
|
|||||||
// This should be in sync with the body font size in the theme.
|
// This should be in sync with the body font size in the theme.
|
||||||
export const MENU_ITEM_FONT_SIZE = 14;
|
export const MENU_ITEM_FONT_SIZE = 14;
|
||||||
export const MENU_ITEM_FONT_WEIGHT = 500;
|
export const MENU_ITEM_FONT_WEIGHT = 500;
|
||||||
export const MENU_ITEM_PADDING_X = 8;
|
export const MENU_ITEM_PADDING = 8;
|
||||||
|
export const MENU_ITEM_LINE_HEIGHT = 1.5;
|
||||||
|
|
||||||
|
// Used with Downshift to get the height of each item
|
||||||
|
export const MENU_OPTION_HEIGHT = MENU_ITEM_PADDING * 2 + MENU_ITEM_FONT_SIZE * MENU_ITEM_LINE_HEIGHT;
|
||||||
|
export const POPOVER_MAX_HEIGHT = MENU_OPTION_HEIGHT * 8.5;
|
||||||
|
|
||||||
export const getComboboxStyles = (theme: GrafanaTheme2) => {
|
export const getComboboxStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
@ -27,7 +32,7 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => {
|
|||||||
}),
|
}),
|
||||||
option: css({
|
option: css({
|
||||||
label: 'grafana-select-option',
|
label: 'grafana-select-option',
|
||||||
padding: MENU_ITEM_PADDING_X,
|
padding: MENU_ITEM_PADDING,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@ -65,7 +70,7 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => {
|
|||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
fontSize: theme.typography.bodySmall.fontSize,
|
fontSize: theme.typography.bodySmall.fontSize,
|
||||||
color: theme.colors.text.secondary,
|
color: theme.colors.text.secondary,
|
||||||
lineHeight: theme.typography.body.lineHeight,
|
lineHeight: MENU_ITEM_LINE_HEIGHT,
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}),
|
}),
|
||||||
@ -84,7 +89,7 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => {
|
|||||||
borderRadius: theme.shape.radius.default,
|
borderRadius: theme.shape.radius.default,
|
||||||
content: '" "',
|
content: '" "',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
height: '100%',
|
height: MENU_OPTION_HEIGHT,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
width: theme.spacing(0.5),
|
width: theme.spacing(0.5),
|
||||||
left: 0,
|
left: 0,
|
||||||
|
@ -4,15 +4,17 @@ import { useMemo, useRef, useState } from 'react';
|
|||||||
import { measureText } from '../../utils';
|
import { measureText } from '../../utils';
|
||||||
|
|
||||||
import { ComboboxOption } from './Combobox';
|
import { ComboboxOption } from './Combobox';
|
||||||
import { MENU_ITEM_FONT_SIZE, MENU_ITEM_FONT_WEIGHT, MENU_ITEM_PADDING_X } from './getComboboxStyles';
|
import {
|
||||||
|
MENU_ITEM_FONT_SIZE,
|
||||||
|
MENU_ITEM_FONT_WEIGHT,
|
||||||
|
MENU_ITEM_PADDING,
|
||||||
|
MENU_OPTION_HEIGHT,
|
||||||
|
POPOVER_MAX_HEIGHT,
|
||||||
|
} from './getComboboxStyles';
|
||||||
|
|
||||||
// Only consider the first n items when calculating the width of the popover.
|
// Only consider the first n items when calculating the width of the popover.
|
||||||
const WIDTH_CALCULATION_LIMIT_ITEMS = 100_000;
|
const WIDTH_CALCULATION_LIMIT_ITEMS = 100_000;
|
||||||
|
|
||||||
// Used with Downshift to get the height of each item
|
|
||||||
export const OPTION_HEIGHT = 45;
|
|
||||||
const POPOVER_MAX_HEIGHT = OPTION_HEIGHT * 8.5;
|
|
||||||
|
|
||||||
// Clearance around the popover to prevent it from being too close to the edge of the viewport
|
// Clearance around the popover to prevent it from being too close to the edge of the viewport
|
||||||
const POPOVER_PADDING = 16;
|
const POPOVER_PADDING = 16;
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ export const useComboboxFloat = (
|
|||||||
const preferredMaxHeight = availableHeight - POPOVER_PADDING;
|
const preferredMaxHeight = availableHeight - POPOVER_PADDING;
|
||||||
|
|
||||||
const width = Math.max(preferredMaxWidth, 0);
|
const width = Math.max(preferredMaxWidth, 0);
|
||||||
const height = Math.min(Math.max(preferredMaxHeight, OPTION_HEIGHT * 6), POPOVER_MAX_HEIGHT);
|
const height = Math.min(Math.max(preferredMaxHeight, MENU_OPTION_HEIGHT * 6), POPOVER_MAX_HEIGHT);
|
||||||
|
|
||||||
setPopoverMaxSize({ width, height });
|
setPopoverMaxSize({ width, height });
|
||||||
},
|
},
|
||||||
@ -68,7 +70,7 @@ export const useComboboxFloat = (
|
|||||||
|
|
||||||
const size = measureText(longestItem, MENU_ITEM_FONT_SIZE, MENU_ITEM_FONT_WEIGHT).width;
|
const size = measureText(longestItem, MENU_ITEM_FONT_SIZE, MENU_ITEM_FONT_WEIGHT).width;
|
||||||
|
|
||||||
return size + MENU_ITEM_PADDING_X * 2 + scrollbarWidth;
|
return size + MENU_ITEM_PADDING * 2 + scrollbarWidth;
|
||||||
}, [items, scrollbarWidth]);
|
}, [items, scrollbarWidth]);
|
||||||
|
|
||||||
const floatStyles = {
|
const floatStyles = {
|
||||||
|
@ -455,7 +455,7 @@
|
|||||||
"title": "Clear value"
|
"title": "Clear value"
|
||||||
},
|
},
|
||||||
"custom-value": {
|
"custom-value": {
|
||||||
"create": "Create custom value"
|
"label": "Custom value: "
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"no-found": "No options found."
|
"no-found": "No options found."
|
||||||
|
@ -455,7 +455,7 @@
|
|||||||
"title": "Cľęäř väľūę"
|
"title": "Cľęäř väľūę"
|
||||||
},
|
},
|
||||||
"custom-value": {
|
"custom-value": {
|
||||||
"create": "Cřęäŧę čūşŧőm väľūę"
|
"label": "Cūşŧőm väľūę: "
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"no-found": "Ńő őpŧįőʼnş ƒőūʼnđ."
|
"no-found": "Ńő őpŧįőʼnş ƒőūʼnđ."
|
||||||
|
Loading…
Reference in New Issue
Block a user