Value mappings: scroll values when many items exist (#40733)
This commit is contained in:
@@ -89,10 +89,25 @@ export function Modal(props: PropsWithChildren<Props>) {
|
||||
);
|
||||
}
|
||||
|
||||
function ModalButtonRow({ children }: { children: React.ReactNode }) {
|
||||
function ModalButtonRow({ leftItems, children }: { leftItems?: React.ReactNode; children: React.ReactNode }) {
|
||||
const theme = useTheme2();
|
||||
const styles = getModalStyles(theme);
|
||||
|
||||
if (leftItems) {
|
||||
return (
|
||||
<div className={styles.modalButtonRow}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<HorizontalGroup justify="flex-start" spacing="md">
|
||||
{leftItems}
|
||||
</HorizontalGroup>
|
||||
<HorizontalGroup justify="flex-end" spacing="md">
|
||||
{children}
|
||||
</HorizontalGroup>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.modalButtonRow}>
|
||||
<HorizontalGroup justify="flex-end" spacing="md">
|
||||
|
||||
+3
-1
@@ -121,6 +121,7 @@ describe('When adding and updating range map', () => {
|
||||
it('should add new range map', async () => {
|
||||
const onChangeSpy = jest.fn();
|
||||
setup(onChangeSpy, { value: [] });
|
||||
screen.getAllByTestId('remove-value-mapping')[0].click();
|
||||
|
||||
fireEvent.click(screen.getByLabelText(selectors.components.ValuePicker.button('Add a new mapping')));
|
||||
const selectComponent = await screen.findByLabelText(selectors.components.ValuePicker.select('Add a new mapping'));
|
||||
@@ -148,10 +149,11 @@ describe('When adding and updating range map', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('When adding and updating tegex map', () => {
|
||||
describe('When adding and updating regex map', () => {
|
||||
it('should add new regex map', async () => {
|
||||
const onChangeSpy = jest.fn();
|
||||
setup(onChangeSpy, { value: [] });
|
||||
screen.getAllByTestId('remove-value-mapping')[0].click();
|
||||
|
||||
fireEvent.click(screen.getByLabelText(selectors.components.ValuePicker.button('Add a new mapping')));
|
||||
const selectComponent = await screen.findByLabelText(selectors.components.ValuePicker.select('Add a new mapping'));
|
||||
|
||||
+63
-43
@@ -81,51 +81,65 @@ export function ValueMappingsEditorModal({ value, onChange, onClose }: Props) {
|
||||
onClose();
|
||||
};
|
||||
|
||||
// Start with an empty row
|
||||
useEffect(() => {
|
||||
if (!value?.length) {
|
||||
onAddValueMapping({ value: MappingType.ValueToText });
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<table className={styles.editTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: '1%' }}></th>
|
||||
<th style={{ width: '40%', textAlign: 'left' }} colSpan={2}>
|
||||
Condition
|
||||
</th>
|
||||
<th style={{ textAlign: 'left' }}>Display text</th>
|
||||
<th style={{ width: '10%' }}>Color</th>
|
||||
<th style={{ width: '1%' }}></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<Droppable droppableId="sortable-field-mappings" direction="vertical">
|
||||
{(provided) => (
|
||||
<tbody ref={provided.innerRef} {...provided.droppableProps}>
|
||||
{rows.map((row, index) => (
|
||||
<ValueMappingEditRow
|
||||
key={index.toString()}
|
||||
mapping={row}
|
||||
index={index}
|
||||
onChange={onChangeMapping}
|
||||
onRemove={onRemoveRow}
|
||||
onDuplicate={onDuplicateMapping}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</tbody>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
</table>
|
||||
<ValuePicker
|
||||
label="Add a new mapping"
|
||||
variant="secondary"
|
||||
size="md"
|
||||
icon="plus"
|
||||
menuPlacement="auto"
|
||||
isFullWidth={false}
|
||||
options={mappingTypes}
|
||||
onChange={onAddValueMapping}
|
||||
/>
|
||||
<Modal.ButtonRow>
|
||||
<div className={styles.tableWrap}>
|
||||
<table className={styles.editTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: '1%' }}></th>
|
||||
<th style={{ width: '40%', textAlign: 'left' }} colSpan={2}>
|
||||
Condition
|
||||
</th>
|
||||
<th style={{ textAlign: 'left' }}>Display text</th>
|
||||
<th style={{ width: '10%' }}>Color</th>
|
||||
<th style={{ width: '1%' }}></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<Droppable droppableId="sortable-field-mappings" direction="vertical">
|
||||
{(provided) => (
|
||||
<tbody ref={provided.innerRef} {...provided.droppableProps}>
|
||||
{rows.map((row, index) => (
|
||||
<ValueMappingEditRow
|
||||
key={index.toString()}
|
||||
mapping={row}
|
||||
index={index}
|
||||
onChange={onChangeMapping}
|
||||
onRemove={onRemoveRow}
|
||||
onDuplicate={onDuplicateMapping}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</tbody>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<Modal.ButtonRow
|
||||
leftItems={
|
||||
<ValuePicker
|
||||
label="Add a new mapping"
|
||||
variant="secondary"
|
||||
size="md"
|
||||
icon="plus"
|
||||
menuPlacement="auto"
|
||||
minWidth={40}
|
||||
options={mappingTypes}
|
||||
onChange={onAddValueMapping}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Button variant="secondary" fill="outline" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
@@ -138,6 +152,12 @@ export function ValueMappingsEditorModal({ value, onChange, onClose }: Props) {
|
||||
}
|
||||
|
||||
export const getStyles = (theme: GrafanaTheme2) => ({
|
||||
tableWrap: css`
|
||||
max-height: calc(80vh - 170px);
|
||||
min-height: 40px;
|
||||
overflow: auto;
|
||||
`,
|
||||
|
||||
editTable: css({
|
||||
width: '100%',
|
||||
marginBottom: theme.spacing(2),
|
||||
|
||||
@@ -3,9 +3,9 @@ import { IconName } from '../../types';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Button, ButtonVariant } from '../Button';
|
||||
import { Select } from '../Select/Select';
|
||||
import { FullWidthButtonContainer } from '../Button/FullWidthButtonContainer';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { useTheme2 } from '../../themes';
|
||||
|
||||
export interface ValuePickerProps<T> {
|
||||
/** Label to display on the picker button */
|
||||
@@ -20,6 +20,8 @@ export interface ValuePickerProps<T> {
|
||||
variant?: ButtonVariant;
|
||||
/** Size of button */
|
||||
size?: ComponentSize;
|
||||
/** Min width for select in grid units */
|
||||
minWidth?: number;
|
||||
/** Should the picker cover the full width of its parent */
|
||||
isFullWidth?: boolean;
|
||||
/** Control where the menu is rendered */
|
||||
@@ -32,29 +34,31 @@ export function ValuePicker<T>({
|
||||
options,
|
||||
onChange,
|
||||
variant,
|
||||
minWidth = 16,
|
||||
size = 'sm',
|
||||
isFullWidth = true,
|
||||
menuPlacement,
|
||||
}: ValuePickerProps<T>) {
|
||||
const [isPicking, setIsPicking] = useState(false);
|
||||
const theme = useTheme2();
|
||||
|
||||
const buttonEl = (
|
||||
<Button
|
||||
size={size || 'sm'}
|
||||
icon={icon || 'plus'}
|
||||
onClick={() => setIsPicking(true)}
|
||||
variant={variant}
|
||||
aria-label={selectors.components.ValuePicker.button(label)}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{!isPicking && (isFullWidth ? <FullWidthButtonContainer>{buttonEl}</FullWidthButtonContainer> : buttonEl)}
|
||||
{!isPicking && (
|
||||
<Button
|
||||
size={size || 'sm'}
|
||||
icon={icon || 'plus'}
|
||||
onClick={() => setIsPicking(true)}
|
||||
variant={variant}
|
||||
fullWidth={isFullWidth}
|
||||
aria-label={selectors.components.ValuePicker.button(label)}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{isPicking && (
|
||||
<span>
|
||||
<span style={{ minWidth: theme.spacing(minWidth), flexGrow: isFullWidth ? 1 : undefined }}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
placeholder={label}
|
||||
|
||||
@@ -260,5 +260,6 @@ function getBorderTopStyles(theme: GrafanaTheme2) {
|
||||
return css({
|
||||
borderTop: `1px solid ${theme.colors.border.weak}`,
|
||||
padding: `${theme.spacing(2)}`,
|
||||
display: 'flex',
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user