mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
145 lines
4.0 KiB
TypeScript
145 lines
4.0 KiB
TypeScript
import { css } from '@emotion/css';
|
|
import React, { FormEvent, useState, KeyboardEvent, useRef, useEffect } from 'react';
|
|
import { Draggable } from 'react-beautiful-dnd';
|
|
|
|
import { GrafanaTheme2 } from '@grafana/data';
|
|
import { Icon, Input, IconButton, HorizontalGroup, FieldValidationMessage, useStyles2 } from '@grafana/ui';
|
|
|
|
type EnumMappingRowProps = {
|
|
transformIndex: number;
|
|
value: string;
|
|
index: number;
|
|
mappedIndex: number;
|
|
onChangeEnumValue: (index: number, value: string) => void;
|
|
onRemoveEnumRow: (index: number) => void;
|
|
checkIsEnumUniqueValue: (value: string) => boolean;
|
|
};
|
|
|
|
const EnumMappingRow = ({
|
|
transformIndex,
|
|
value,
|
|
index,
|
|
mappedIndex,
|
|
onChangeEnumValue,
|
|
onRemoveEnumRow,
|
|
checkIsEnumUniqueValue,
|
|
}: EnumMappingRowProps) => {
|
|
const styles = useStyles2(getStyles);
|
|
|
|
const [enumValue, setEnumValue] = useState<string>(value);
|
|
// If the enum value is empty, we assume it is a new row and should be editable
|
|
const [isEditing, setIsEditing] = useState<boolean>(enumValue === '');
|
|
const [validationError, setValidationError] = useState<string | null>(null);
|
|
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
|
|
// Focus the input field if it is rendered
|
|
useEffect(() => {
|
|
if (inputRef.current) {
|
|
inputRef.current.focus();
|
|
}
|
|
}, [inputRef]);
|
|
|
|
const onEnumInputChange = (event: FormEvent<HTMLInputElement>) => {
|
|
if (
|
|
event.currentTarget.value !== '' &&
|
|
checkIsEnumUniqueValue(event.currentTarget.value) &&
|
|
event.currentTarget.value !== value
|
|
) {
|
|
setValidationError('Enum value already exists');
|
|
} else {
|
|
setValidationError(null);
|
|
}
|
|
|
|
setEnumValue(event.currentTarget.value);
|
|
};
|
|
|
|
const onEnumInputBlur = () => {
|
|
setIsEditing(false);
|
|
setValidationError(null);
|
|
|
|
// Do not add empty or duplicate enum values
|
|
if (enumValue === '' || validationError !== null) {
|
|
onRemoveEnumRow(mappedIndex);
|
|
return;
|
|
}
|
|
|
|
onChangeEnumValue(mappedIndex, enumValue);
|
|
};
|
|
|
|
const onEnumInputKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
if (event.key === 'Enter') {
|
|
event.preventDefault();
|
|
onEnumInputBlur();
|
|
}
|
|
};
|
|
|
|
const onEnumValueClick = () => {
|
|
setIsEditing(true);
|
|
};
|
|
|
|
const onRemoveButtonClick = () => {
|
|
onRemoveEnumRow(mappedIndex);
|
|
};
|
|
|
|
return (
|
|
<Draggable key={`${transformIndex}/${value}`} draggableId={`${transformIndex}/${value}`} index={index}>
|
|
{(provided) => (
|
|
<tr key={index} ref={provided.innerRef} {...provided.draggableProps}>
|
|
<td>
|
|
<div className={styles.dragHandle} {...provided.dragHandleProps}>
|
|
<Icon name="draggabledots" size="lg" />
|
|
</div>
|
|
</td>
|
|
{isEditing ? (
|
|
<td>
|
|
<Input
|
|
ref={inputRef}
|
|
type="text"
|
|
value={enumValue}
|
|
onChange={onEnumInputChange}
|
|
onBlur={onEnumInputBlur}
|
|
onKeyDown={onEnumInputKeyDown}
|
|
/>
|
|
{validationError && <FieldValidationMessage>{validationError}</FieldValidationMessage>}
|
|
</td>
|
|
) : (
|
|
<td onClick={onEnumValueClick} className={styles.clickableTableCell}>
|
|
{value && value !== '' ? value : 'Click to edit'}
|
|
</td>
|
|
)}
|
|
<td className={styles.textAlignCenter}>
|
|
<HorizontalGroup spacing="sm">
|
|
<IconButton
|
|
name="trash-alt"
|
|
onClick={onRemoveButtonClick}
|
|
data-testid="remove-enum-row"
|
|
aria-label="Delete enum row"
|
|
tooltip="Delete"
|
|
/>
|
|
</HorizontalGroup>
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</Draggable>
|
|
);
|
|
};
|
|
|
|
const getStyles = (theme: GrafanaTheme2) => ({
|
|
dragHandle: css({
|
|
cursor: 'grab',
|
|
}),
|
|
textAlignCenter: css({
|
|
textAlign: 'center',
|
|
}),
|
|
clickableTableCell: css({
|
|
cursor: 'pointer',
|
|
width: '100px',
|
|
'&:hover': {
|
|
color: theme.colors.text.maxContrast,
|
|
},
|
|
}),
|
|
});
|
|
|
|
export default EnumMappingRow;
|