ValueMappings: Make value mapping row focusable (#52337)

* Dimensions: Move drag handle to draggable container

* Accessibility: Added unique ids to preserve focus when dragging items using keyboard
pull/52406/head
Leo 3 years ago committed by GitHub
parent 9afe845d5c
commit 7b7b9ff4a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      public/app/features/dimensions/editors/ValueMappingsEditor/ValueMappingEditRow.tsx
  2. 85
      public/app/features/dimensions/editors/ValueMappingsEditor/ValueMappingsEditorModal.tsx

@ -17,6 +17,7 @@ export interface ValueMappingEditRowModel {
isNew?: boolean; isNew?: boolean;
specialMatch?: SpecialValueMatch; specialMatch?: SpecialValueMatch;
result: ValueMappingResult; result: ValueMappingResult;
id: string;
} }
interface Props { interface Props {
@ -29,7 +30,7 @@ interface Props {
} }
export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDuplicate, showIconPicker }: Props) { export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDuplicate, showIconPicker }: Props) {
const { key, result } = mapping; const { key, result, id } = mapping;
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const inputRef = useRef<HTMLInputElement | null>(null); const inputRef = useRef<HTMLInputElement | null>(null);
@ -126,11 +127,11 @@ export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDupl
]; ];
return ( return (
<Draggable draggableId={`mapping-${index}`} index={index}> <Draggable key={id} draggableId={id} index={index}>
{(provided) => ( {(provided) => (
<tr ref={provided.innerRef} {...provided.draggableProps}> <tr ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<td> <td>
<div {...provided.dragHandleProps} className={styles.dragHandle}> <div className={styles.dragHandle}>
<Icon name="draggabledots" size="lg" /> <Icon name="draggabledots" size="lg" />
</div> </div>
</td> </td>

@ -1,4 +1,5 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { uniqueId } from 'lodash';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
@ -54,18 +55,11 @@ export function ValueMappingsEditorModal({ value, onChange, onClose, showIconPic
]; ];
const onAddValueMapping = (value: SelectableValue<MappingType>) => { const onAddValueMapping = (value: SelectableValue<MappingType>) => {
updateRows([ updateRows([...rows, createRow({ type: value.value!, result: {}, isNew: true })]);
...rows,
{
type: value.value!,
isNew: true,
result: {},
},
]);
}; };
const onDuplicateMapping = (index: number) => { const onDuplicateMapping = (index: number) => {
const sourceRow = rows[index]; const sourceRow = duplicateRow(rows[index]);
const copy = [...rows]; const copy = [...rows];
copy.splice(index, 0, { ...sourceRow }); copy.splice(index, 0, { ...sourceRow });
@ -111,7 +105,7 @@ export function ValueMappingsEditorModal({ value, onChange, onClose, showIconPic
<tbody ref={provided.innerRef} {...provided.droppableProps}> <tbody ref={provided.innerRef} {...provided.droppableProps}>
{rows.map((row, index) => ( {rows.map((row, index) => (
<ValueMappingEditRow <ValueMappingEditRow
key={index.toString()} key={row.id}
mapping={row} mapping={row}
index={index} index={index}
onChange={onChangeMapping} onChange={onChangeMapping}
@ -178,6 +172,27 @@ export const getStyles = (theme: GrafanaTheme2) => ({
}), }),
}); });
function getRowUniqueId(): string {
return uniqueId('mapping-');
}
function createRow(row: Partial<ValueMappingEditRowModel>): ValueMappingEditRowModel {
return {
type: MappingType.ValueToText,
result: {},
id: getRowUniqueId(),
...row,
};
}
function duplicateRow(row: Partial<ValueMappingEditRowModel>): ValueMappingEditRowModel {
return {
...createRow(row),
// provide a new unique id to the duplicated row, to preserve focus when dragging 2 duplicated rows
id: getRowUniqueId(),
};
}
export function editModelToSaveModel(rows: ValueMappingEditRowModel[]) { export function editModelToSaveModel(rows: ValueMappingEditRowModel[]) {
const mappings: ValueMapping[] = []; const mappings: ValueMapping[] = [];
const valueMaps: ValueMapping = { const valueMaps: ValueMapping = {
@ -250,34 +265,42 @@ export function buildEditRowModels(value: ValueMapping[]) {
switch (mapping.type) { switch (mapping.type) {
case MappingType.ValueToText: case MappingType.ValueToText:
for (const key of Object.keys(mapping.options)) { for (const key of Object.keys(mapping.options)) {
editRows.push({ editRows.push(
type: mapping.type, createRow({
result: mapping.options[key], type: mapping.type,
key, result: mapping.options[key],
}); key,
})
);
} }
break; break;
case MappingType.RangeToText: case MappingType.RangeToText:
editRows.push({ editRows.push(
type: mapping.type, createRow({
result: mapping.options.result, type: mapping.type,
from: mapping.options.from ?? 0, result: mapping.options.result,
to: mapping.options.to ?? 0, from: mapping.options.from ?? 0,
}); to: mapping.options.to ?? 0,
})
);
break; break;
case MappingType.RegexToText: case MappingType.RegexToText:
editRows.push({ editRows.push(
type: mapping.type, createRow({
result: mapping.options.result, type: mapping.type,
pattern: mapping.options.pattern, result: mapping.options.result,
}); pattern: mapping.options.pattern,
})
);
break; break;
case MappingType.SpecialValue: case MappingType.SpecialValue:
editRows.push({ editRows.push(
type: mapping.type, createRow({
result: mapping.options.result, type: mapping.type,
specialMatch: mapping.options.match ?? SpecialValueMatch.Null, result: mapping.options.result,
}); specialMatch: mapping.options.match ?? SpecialValueMatch.Null,
})
);
} }
} }
} }

Loading…
Cancel
Save