From 351990068dca81fa27f1bb03ec30da84fffe6e84 Mon Sep 17 00:00:00 2001
From: Marta Bondyra
Date: Wed, 17 Feb 2021 16:48:26 +0100
Subject: [PATCH] [Lens] Drag and drop accessibility messages (#91494)
* [Lens] copy dnd
* Update x-pack/plugins/lens/public/drag_drop/providers.tsx
Co-authored-by: Wylie Conlon
Co-authored-by: Wylie Conlon
---
.../lens/public/drag_drop/announcements.tsx | 151 ++++++++++++++----
.../lens/public/drag_drop/drag_drop.test.tsx | 50 +++---
.../lens/public/drag_drop/drag_drop.tsx | 2 +-
.../lens/public/drag_drop/providers.tsx | 4 +-
.../plugins/lens/public/drag_drop/readme.md | 2 +-
.../draggable_dimension_button.tsx | 8 +-
.../config_panel/empty_dimension_button.tsx | 8 +-
.../config_panel/layer_panel.test.tsx | 20 ++-
.../public/editor_frame_service/mocks.tsx | 2 +-
.../dimension_panel/droppable.test.ts | 30 ++--
.../dimension_panel/droppable.ts | 76 ++++++---
.../indexpattern_datasource/indexpattern.tsx | 4 +-
x-pack/plugins/lens/public/types.ts | 4 +-
13 files changed, 249 insertions(+), 112 deletions(-)
diff --git a/x-pack/plugins/lens/public/drag_drop/announcements.tsx b/x-pack/plugins/lens/public/drag_drop/announcements.tsx
index 61785310bdcf..3c65008f8f38 100644
--- a/x-pack/plugins/lens/public/drag_drop/announcements.tsx
+++ b/x-pack/plugins/lens/public/drag_drop/announcements.tsx
@@ -11,6 +11,7 @@ export interface HumanData {
label: string;
groupLabel?: string;
position?: number;
+ nextLabel?: string;
}
type AnnouncementFunction = (draggedElement: HumanData, dropElement: HumanData) => string;
@@ -25,7 +26,7 @@ const selectedTargetReplace = (
{ label: dropLabel, groupLabel, position }: HumanData
) =>
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.replace', {
- defaultMessage: `Selected {dropLabel} in {groupLabel} group at position {position}. Press space or enter to replace {dropLabel} with {label}.`,
+ defaultMessage: `Replace {dropLabel} in {groupLabel} group at position {position} with {label}. Press space or enter to replace`,
values: {
label,
dropLabel,
@@ -34,65 +35,103 @@ const selectedTargetReplace = (
},
});
-const droppedReplace = ({ label }: HumanData, { label: dropLabel, groupLabel }: HumanData) =>
+const droppedReplace = (
+ { label }: HumanData,
+ { label: dropLabel, groupLabel, position }: HumanData
+) =>
i18n.translate('xpack.lens.dragDrop.announce.duplicated.replace', {
- defaultMessage:
- 'You have dropped the item. You have replaced {dropLabel} with {label} in {groupLabel} group.',
+ defaultMessage: 'Replaced {dropLabel} with {label} in {groupLabel} at position {position}',
values: {
label,
dropLabel,
groupLabel,
+ position,
},
});
export const announcements: CustomAnnouncementsType = {
selectedTarget: {
- reorder: ({ label, position: prevPosition }, { position }) =>
+ reorder: ({ label, groupLabel, position: prevPosition }, { position }) =>
prevPosition === position
? i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.reorderedBack', {
- defaultMessage: `You have moved the item {label} back to position {prevPosition}`,
+ defaultMessage: `{label} returned to its initial position {prevPosition}`,
values: {
label,
prevPosition,
},
})
: i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.reordered', {
- defaultMessage: `You have moved the item {label} from position {prevPosition} to position {position}`,
+ defaultMessage: `Reorder {label} in {groupLabel} group from position {prevPosition} to position {position}. Press space or enter to reorder`,
values: {
+ groupLabel,
label,
position,
prevPosition,
},
}),
- duplicate_in_group: ({ label }, { label: dropLabel, groupLabel, position }) =>
+ duplicate_in_group: ({ label }, { groupLabel, position }) =>
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.duplicated', {
- defaultMessage: `Selected {dropLabel} in {groupLabel} group at position {position}. Press space or enter to duplicate {label}.`,
+ defaultMessage: `Duplicate {label} to {groupLabel} group at position {position}. Press space or enter to duplicate`,
values: {
label,
- dropLabel,
groupLabel,
position,
},
}),
field_replace: selectedTargetReplace,
replace_compatible: selectedTargetReplace,
- replace_incompatible: selectedTargetReplace,
- },
- dropped: {
- reorder: ({ label, position: prevPosition }, { position }) =>
- i18n.translate('xpack.lens.dragDrop.announce.dropped.reordered', {
- defaultMessage:
- 'You have dropped the item {label}. You have moved the item from position {prevPosition} to positon {position}',
+ replace_incompatible: (
+ { label }: HumanData,
+ { label: dropLabel, groupLabel, position, nextLabel }: HumanData
+ ) =>
+ i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.replaceIncompatible', {
+ defaultMessage: `Convert {label} to {nextLabel} and replace {dropLabel} in {groupLabel} group at position {position}. Press space or enter to replace`,
values: {
label,
+ nextLabel,
+ dropLabel,
+ groupLabel,
+ position,
+ },
+ }),
+ move_incompatible: (
+ { label }: HumanData,
+ { label: groupLabel, position, nextLabel }: HumanData
+ ) =>
+ i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.moveIncompatible', {
+ defaultMessage: `Convert {label} to {nextLabel} and move to {groupLabel} group at position {position}. Press space or enter to move`,
+ values: {
+ label,
+ nextLabel,
+ groupLabel,
+ position,
+ },
+ }),
+ move_compatible: ({ label }: HumanData, { groupLabel, position }: HumanData) =>
+ i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.moveCompatible', {
+ defaultMessage: `Move {label} to {groupLabel} group at position {position}. Press space or enter to move`,
+ values: {
+ label,
+ groupLabel,
+ position,
+ },
+ }),
+ },
+ dropped: {
+ reorder: ({ label, groupLabel, position: prevPosition }, { position }) =>
+ i18n.translate('xpack.lens.dragDrop.announce.dropped.reordered', {
+ defaultMessage:
+ 'Reordered {label} in {groupLabel} group from position {prevPosition} to positon {position}',
+ values: {
+ label,
+ groupLabel,
position,
prevPosition,
},
}),
duplicate_in_group: ({ label }, { groupLabel, position }) =>
i18n.translate('xpack.lens.dragDrop.announce.dropped.duplicated', {
- defaultMessage:
- 'You have dropped the item. You have duplicated {label} in {groupLabel} group at position {position}',
+ defaultMessage: 'Duplicated {label} in {groupLabel} group at position {position}',
values: {
label,
groupLabel,
@@ -101,7 +140,42 @@ export const announcements: CustomAnnouncementsType = {
}),
field_replace: droppedReplace,
replace_compatible: droppedReplace,
- replace_incompatible: droppedReplace,
+ replace_incompatible: (
+ { label }: HumanData,
+ { label: dropLabel, groupLabel, position, nextLabel }: HumanData
+ ) =>
+ i18n.translate('xpack.lens.dragDrop.announce.dropped.replaceIncompatible', {
+ defaultMessage:
+ 'Converted {label} to {nextLabel} and replaced {dropLabel} in {groupLabel} group at position {position}',
+ values: {
+ label,
+ nextLabel,
+ dropLabel,
+ groupLabel,
+ position,
+ },
+ }),
+ move_incompatible: ({ label }: HumanData, { groupLabel, position, nextLabel }: HumanData) =>
+ i18n.translate('xpack.lens.dragDrop.announce.dropped.moveIncompatible', {
+ defaultMessage:
+ 'Converted {label} to {nextLabel} and moved to {groupLabel} group at position {position}',
+ values: {
+ label,
+ nextLabel,
+ groupLabel,
+ position,
+ },
+ }),
+
+ move_compatible: ({ label }: HumanData, { groupLabel, position }: HumanData) =>
+ i18n.translate('xpack.lens.dragDrop.announce.dropped.moveCompatible', {
+ defaultMessage: 'Moved {label} to {groupLabel} group at position {position}',
+ values: {
+ label,
+ groupLabel,
+ position,
+ },
+ }),
},
};
@@ -113,13 +187,29 @@ const defaultAnnouncements = {
label,
},
}),
- cancelled: () =>
- i18n.translate('xpack.lens.dragDrop.announce.cancelled', {
- defaultMessage: 'Movement cancelled',
- }),
+ cancelled: ({ label, groupLabel, position }: HumanData) => {
+ if (!groupLabel || !position) {
+ return i18n.translate('xpack.lens.dragDrop.announce.cancelled', {
+ defaultMessage: 'Movement cancelled. {label} returned to its initial position',
+ values: {
+ label,
+ },
+ });
+ }
+ return i18n.translate('xpack.lens.dragDrop.announce.cancelledItem', {
+ defaultMessage:
+ 'Movement cancelled. {label} returned to {groupLabel} group at position {position}',
+ values: {
+ label,
+ groupLabel,
+ position,
+ },
+ });
+ },
+
noTarget: () => {
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.noSelected', {
- defaultMessage: `No target selected. Use arrow keys to select a target.`,
+ defaultMessage: `No target selected. Use arrow keys to select a target`,
});
},
@@ -129,17 +219,15 @@ const defaultAnnouncements = {
) =>
dropGroupLabel && position
? i18n.translate('xpack.lens.dragDrop.announce.droppedDefault', {
- defaultMessage:
- 'You have dropped {label} to {dropLabel} in {dropGroupLabel} group at position {position}',
+ defaultMessage: 'Added {label} in {dropGroupLabel} group at position {position}',
values: {
label,
dropGroupLabel,
position,
- dropLabel,
},
})
: i18n.translate('xpack.lens.dragDrop.announce.droppedNoPosition', {
- defaultMessage: 'You have dropped {label} to {dropLabel}',
+ defaultMessage: 'Added {label} to {dropLabel}',
values: {
label,
dropLabel,
@@ -151,16 +239,15 @@ const defaultAnnouncements = {
) => {
return dropGroupLabel && position
? i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.default', {
- defaultMessage: `Selected {dropLabel} in {dropGroupLabel} group at position {position}. Press space or enter to drop {label}`,
+ defaultMessage: `Add {label} to {dropGroupLabel} group at position {position}. Press space or enter to add`,
values: {
- dropLabel,
label,
dropGroupLabel,
position,
},
})
: i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.defaultNoPosition', {
- defaultMessage: `Selected {dropLabel}. Press space or enter to drop {label}`,
+ defaultMessage: `Add {label} to {dropLabel}. Press space or enter to add`,
values: {
dropLabel,
label,
diff --git a/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx b/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx
index f9bae547a563..f2a2fda73038 100644
--- a/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx
+++ b/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx
@@ -110,7 +110,7 @@ describe('DragDrop', () => {
const component = mount(
@@ -126,7 +126,7 @@ describe('DragDrop', () => {
expect(preventDefault).toBeCalled();
expect(stopPropagation).toBeCalled();
expect(setDragging).toBeCalledWith(undefined);
- expect(onDrop).toBeCalledWith({ id: '2', humanData: { label: 'label1' } }, 'field_add');
+ expect(onDrop).toBeCalledWith({ id: '2', humanData: { label: 'Label1' } }, 'field_add');
});
test('drop function is not called on dropType undefined', async () => {
@@ -138,7 +138,7 @@ describe('DragDrop', () => {
const component = mount(
@@ -195,7 +195,7 @@ describe('DragDrop', () => {
});
test('additional styles are reflected in the className until drop', () => {
- let dragging: { id: '1'; humanData: { label: 'label1' } } | undefined;
+ let dragging: { id: '1'; humanData: { label: 'Label1' } } | undefined;
const getAdditionalClassesOnEnter = jest.fn().mockReturnValue('additional');
const getAdditionalClassesOnDroppable = jest.fn().mockReturnValue('droppable');
const setA11yMessage = jest.fn();
@@ -206,7 +206,7 @@ describe('DragDrop', () => {
dragging={dragging}
setA11yMessage={setA11yMessage}
setDragging={() => {
- dragging = { id: '1', humanData: { label: 'label1' } };
+ dragging = { id: '1', humanData: { label: 'Label1' } };
}}
>
{
});
test('additional enter styles are reflected in the className until dragleave', () => {
- let dragging: { id: '1'; humanData: { label: 'label1' } } | undefined;
+ let dragging: { id: '1'; humanData: { label: 'Label1' } } | undefined;
const getAdditionalClasses = jest.fn().mockReturnValue('additional');
const getAdditionalClassesOnDroppable = jest.fn().mockReturnValue('droppable');
const setActiveDropTarget = jest.fn();
@@ -252,7 +252,7 @@ describe('DragDrop', () => {
setA11yMessage={jest.fn()}
dragging={dragging}
setDragging={() => {
- dragging = { id: '1', humanData: { label: 'label1' } };
+ dragging = { id: '1', humanData: { label: 'Label1' } };
}}
setActiveDropTarget={setActiveDropTarget}
activeDropTarget={
@@ -303,7 +303,7 @@ describe('DragDrop', () => {
draggable: true,
value: {
id: '1',
- humanData: { label: 'label1', position: 1 },
+ humanData: { label: 'Label1', position: 1 },
},
children: '1',
order: [2, 0, 0, 0],
@@ -326,7 +326,7 @@ describe('DragDrop', () => {
dragType: 'move' as 'copy' | 'move',
value: {
id: '3',
- humanData: { label: 'label3', position: 1 },
+ humanData: { label: 'label3', position: 1, groupLabel: 'Y' },
},
onDrop,
dropType: 'replace_compatible' as DropType,
@@ -337,7 +337,7 @@ describe('DragDrop', () => {
dragType: 'move' as 'copy' | 'move',
value: {
id: '4',
- humanData: { label: 'label4', position: 2 },
+ humanData: { label: 'label4', position: 2, groupLabel: 'Y' },
},
order: [2, 0, 2, 1],
},
@@ -380,11 +380,11 @@ describe('DragDrop', () => {
});
keyboardHandler.simulate('keydown', { key: 'Enter' });
expect(setA11yMessage).toBeCalledWith(
- 'Selected label3 in group at position 1. Press space or enter to replace label3 with label1.'
+ 'Replace label3 in Y group at position 1 with Label1. Press space or enter to replace'
);
expect(setActiveDropTarget).toBeCalledWith(undefined);
expect(onDrop).toBeCalledWith(
- { humanData: { label: 'label1', position: 1 }, id: '1' },
+ { humanData: { label: 'Label1', position: 1 }, id: '1' },
'move_compatible'
);
});
@@ -437,7 +437,7 @@ describe('DragDrop', () => {
draggable: true,
value: {
id: '1',
- humanData: { label: 'label1', position: 1 },
+ humanData: { label: 'Label1', position: 1 },
},
children: '1',
order: [2, 0, 0, 0],
@@ -488,19 +488,19 @@ describe('DragDrop', () => {
const items = [
{
id: '1',
- humanData: { label: 'label1', position: 1 },
+ humanData: { label: 'Label1', position: 1, groupLabel: 'X' },
onDrop,
dropType: 'reorder' as DropType,
},
{
id: '2',
- humanData: { label: 'label2', position: 2 },
+ humanData: { label: 'label2', position: 2, groupLabel: 'X' },
onDrop,
dropType: 'reorder' as DropType,
},
{
id: '3',
- humanData: { label: 'label3', position: 3 },
+ humanData: { label: 'label3', position: 3, groupLabel: 'X' },
onDrop,
dropType: 'reorder' as DropType,
},
@@ -583,7 +583,7 @@ describe('DragDrop', () => {
});
expect(setDragging).toBeCalledWith({ ...items[0] });
- expect(setA11yMessage).toBeCalledWith('Lifted label1');
+ expect(setA11yMessage).toBeCalledWith('Lifted Label1');
expect(
component
.find('[data-test-subj="lnsDragDrop-reorderableGroup"]')
@@ -652,7 +652,7 @@ describe('DragDrop', () => {
jest.runAllTimers();
expect(setA11yMessage).toBeCalledWith(
- 'You have dropped the item label1. You have moved the item from position 1 to positon 3'
+ 'Reordered Label1 in X group from position 1 to positon 3'
);
expect(preventDefault).toBeCalled();
expect(stopPropagation).toBeCalled();
@@ -687,7 +687,7 @@ describe('DragDrop', () => {
expect(setActiveDropTarget).toBeCalledWith(items[1]);
expect(setA11yMessage).toBeCalledWith(
- 'You have moved the item label1 from position 1 to position 2'
+ 'Reorder Label1 in X group from position 1 to position 2. Press space or enter to reorder'
);
});
test(`Keyboard navigation: user can drop element to an activeDropTarget`, () => {
@@ -729,13 +729,17 @@ describe('DragDrop', () => {
jest.runAllTimers();
expect(onDropHandler).not.toHaveBeenCalled();
- expect(setA11yMessage).toBeCalledWith('Movement cancelled');
+ expect(setA11yMessage).toBeCalledWith(
+ 'Movement cancelled. Label1 returned to X group at position 1'
+ );
keyboardHandler.simulate('keydown', { key: 'Space' });
keyboardHandler.simulate('keydown', { key: 'ArrowDown' });
keyboardHandler.simulate('blur');
expect(onDropHandler).not.toHaveBeenCalled();
- expect(setA11yMessage).toBeCalledWith('Movement cancelled');
+ expect(setA11yMessage).toBeCalledWith(
+ 'Movement cancelled. Label1 returned to X group at position 1'
+ );
});
test(`Keyboard Navigation: Reordered elements get extra styles to show the reorder effect`, () => {
@@ -772,7 +776,7 @@ describe('DragDrop', () => {
component.find('[data-test-subj="lnsDragDrop-translatableDrop"]').at(1).prop('style')
).toEqual(undefined);
expect(setA11yMessage).toBeCalledWith(
- 'You have moved the item label1 from position 1 to position 2'
+ 'Reorder Label1 in X group from position 1 to position 2. Press space or enter to reorder'
);
component
@@ -837,7 +841,7 @@ describe('DragDrop', () => {
keyboardHandler.simulate('keydown', { key: 'Space' });
keyboardHandler.simulate('keydown', { key: 'ArrowUp' });
expect(setActiveDropTarget).toBeCalledWith(undefined);
- expect(setA11yMessage).toBeCalledWith('You have moved the item label1 back to position 1');
+ expect(setA11yMessage).toBeCalledWith('Label1 returned to its initial position 1');
});
});
});
diff --git a/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx b/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx
index 4b2506432032..6c6a65ab421b 100644
--- a/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx
+++ b/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx
@@ -267,7 +267,7 @@ const DragInner = memo(function DragInner({
setDragging(undefined);
setActiveDropTarget(undefined);
setKeyboardMode(false);
- setA11yMessage(announce.cancelled());
+ setA11yMessage(announce.cancelled(value.humanData));
if (onDragEnd) {
onDragEnd();
}
diff --git a/x-pack/plugins/lens/public/drag_drop/providers.tsx b/x-pack/plugins/lens/public/drag_drop/providers.tsx
index 1a056902a474..deb9bf6cb17a 100644
--- a/x-pack/plugins/lens/public/drag_drop/providers.tsx
+++ b/x-pack/plugins/lens/public/drag_drop/providers.tsx
@@ -204,12 +204,12 @@ export function RootDragDropProvider({ children }: { children: React.ReactNode }
{i18n.translate('xpack.lens.dragDrop.keyboardInstructionsReorder', {
- defaultMessage: `Press enter or space to dragging. When dragging, use the up/down arrow keys to reorder items in the group and left/right arrow keys to choose drop targets outside of the group. Press enter or space again to finish.`,
+ defaultMessage: `Press space or enter to start dragging. When dragging, use the up/down arrow keys to reorder items in the group and left/right arrow keys to choose drop targets outside of the group. Press space or enter again to finish.`,
})}
{i18n.translate('xpack.lens.dragDrop.keyboardInstructions', {
- defaultMessage: `Press enter or space to start dragging. When dragging, use the left/right arrow keys to move between drop targets. Press enter or space again to finish.`,
+ defaultMessage: `Press space or enter to start dragging. When dragging, use the left/right arrow keys to move between drop targets. Press space or enter again to finish.`,
})}
diff --git a/x-pack/plugins/lens/public/drag_drop/readme.md b/x-pack/plugins/lens/public/drag_drop/readme.md
index 55a9e3157c24..01cc4c7bc85a 100644
--- a/x-pack/plugins/lens/public/drag_drop/readme.md
+++ b/x-pack/plugins/lens/public/drag_drop/readme.md
@@ -56,7 +56,7 @@ const { dragging } = useContext(DragContext);
return (
onChange([...items, item])}
>
{items.map((x) => (
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx
index f493000aa587..1cbd41fff2a8 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/draggable_dimension_button.tsx
@@ -64,13 +64,16 @@ export function DraggableDimensionButton({
columnId: string;
registerNewButtonRef: (id: string, instance: HTMLDivElement | null) => void;
}) {
- const dropType = layerDatasource.getDropTypes({
+ const dropProps = layerDatasource.getDropProps({
...layerDatasourceDropProps,
columnId,
filterOperations: group.filterOperations,
groupId: group.groupId,
});
+ const dropType = dropProps?.dropType;
+ const nextLabel = dropProps?.nextLabel;
+
const value = useMemo(
() => ({
columnId,
@@ -82,9 +85,10 @@ export function DraggableDimensionButton({
label,
groupLabel: group.groupLabel,
position: accessorIndex + 1,
+ nextLabel: nextLabel || '',
},
}),
- [columnId, group.groupId, accessorIndex, layerId, dropType, label, group.groupLabel]
+ [columnId, group.groupId, accessorIndex, layerId, dropType, label, group.groupLabel, nextLabel]
);
// todo: simplify by id and use drop targets?
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx
index a83d4bde0383..c9d0a7b00287 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/empty_dimension_button.tsx
@@ -54,13 +54,16 @@ export function EmptyDimensionButton({
setNewColumnId(generateId());
}, [itemIndex]);
- const dropType = layerDatasource.getDropTypes({
+ const dropProps = layerDatasource.getDropProps({
...layerDatasourceDropProps,
columnId: newColumnId,
filterOperations: group.filterOperations,
groupId: group.groupId,
});
+ const dropType = dropProps?.dropType;
+ const nextLabel = dropProps?.nextLabel;
+
const value = useMemo(
() => ({
columnId: newColumnId,
@@ -72,9 +75,10 @@ export function EmptyDimensionButton({
label,
groupLabel: group.groupLabel,
position: itemIndex + 1,
+ nextLabel: nextLabel || '',
},
}),
- [dropType, newColumnId, group.groupId, layerId, group.groupLabel, itemIndex]
+ [dropType, newColumnId, group.groupId, layerId, group.groupLabel, itemIndex, nextLabel]
);
return (
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
index 1f97399fdd29..619147987cdd 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx
@@ -440,7 +440,10 @@ describe('LayerPanel', () => {
],
});
- mockDatasource.getDropTypes.mockReturnValue('field_add');
+ mockDatasource.getDropProps.mockReturnValue({
+ dropType: 'field_add',
+ nextLabel: '',
+ });
const draggingField = {
field: { name: 'dragged' },
@@ -459,7 +462,7 @@ describe('LayerPanel', () => {
);
- expect(mockDatasource.getDropTypes).toHaveBeenCalledWith(
+ expect(mockDatasource.getDropProps).toHaveBeenCalledWith(
expect.objectContaining({
dragDropContext: expect.objectContaining({
dragging: draggingField,
@@ -492,8 +495,8 @@ describe('LayerPanel', () => {
],
});
- mockDatasource.getDropTypes.mockImplementation(({ columnId }) =>
- columnId !== 'a' ? 'field_replace' : undefined
+ mockDatasource.getDropProps.mockImplementation(({ columnId }) =>
+ columnId !== 'a' ? { dropType: 'field_replace', nextLabel: '' } : undefined
);
const draggingField = {
@@ -513,7 +516,7 @@ describe('LayerPanel', () => {
);
- expect(mockDatasource.getDropTypes).toHaveBeenCalledWith(
+ expect(mockDatasource.getDropProps).toHaveBeenCalledWith(
expect.objectContaining({ columnId: 'a' })
);
@@ -554,7 +557,10 @@ describe('LayerPanel', () => {
],
});
- mockDatasource.getDropTypes.mockReturnValue('replace_compatible');
+ mockDatasource.getDropProps.mockReturnValue({
+ dropType: 'replace_compatible',
+ nextLabel: '',
+ });
const draggingOperation = {
layerId: 'first',
@@ -574,7 +580,7 @@ describe('LayerPanel', () => {
);
- expect(mockDatasource.getDropTypes).toHaveBeenCalledWith(
+ expect(mockDatasource.getDropProps).toHaveBeenCalledWith(
expect.objectContaining({
dragDropContext: expect.objectContaining({
dragging: draggingOperation,
diff --git a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx
index 36c2d7128460..db3b29bb74d3 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx
@@ -88,7 +88,7 @@ export function createMockDatasource(id: string): DatasourceMock {
uniqueLabels: jest.fn((_state) => ({})),
renderDimensionTrigger: jest.fn(),
renderDimensionEditor: jest.fn(),
- getDropTypes: jest.fn(),
+ getDropProps: jest.fn(),
onDrop: jest.fn(),
// this is an additional property which doesn't exist on real datasources
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts
index 1f0381d92ce6..17f069b8831e 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.test.ts
@@ -6,7 +6,7 @@
*/
import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public';
import { IndexPatternDimensionEditorProps } from './dimension_panel';
-import { onDrop, getDropTypes } from './droppable';
+import { onDrop, getDropProps } from './droppable';
import { DragContextState } from '../../drag_drop';
import { createMockedDragDropContext } from '../mocks';
import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup, CoreSetup } from 'kibana/public';
@@ -91,7 +91,7 @@ const draggingField = {
* - Dimension trigger: Not tested here
* - Dimension editor component: First half of the tests
*
- * - getDropTypes: Returns drop types that are possible for the current dragging field or other dimension
+ * - getDropProps: Returns drop types that are possible for the current dragging field or other dimension
* - onDrop: Correct application of drop logic
*/
describe('IndexPatternDimensionEditorPanel', () => {
@@ -174,14 +174,14 @@ describe('IndexPatternDimensionEditorPanel', () => {
});
const groupId = 'a';
- describe('getDropTypes', () => {
+ describe('getDropProps', () => {
it('returns undefined if no drag is happening', () => {
- expect(getDropTypes({ ...defaultProps, groupId, dragDropContext })).toBe(undefined);
+ expect(getDropProps({ ...defaultProps, groupId, dragDropContext })).toBe(undefined);
});
it('returns undefined if the dragged item has no field', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -198,7 +198,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
it('returns undefined if field is not supported by filterOperations', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -217,7 +217,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
it('returns remove_add if the field is supported by filterOperations and the dropTarget is an existing column', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -226,12 +226,12 @@ describe('IndexPatternDimensionEditorPanel', () => {
},
filterOperations: (op: OperationMetadata) => op.dataType === 'number',
})
- ).toBe('field_replace');
+ ).toEqual({ dropType: 'field_replace', nextLabel: 'Intervals' });
});
it('returns undefined if the field belongs to another index pattern', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -250,7 +250,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
it('returns undefined if the dragged field is already in use by this operation', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -275,7 +275,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
it('returns move if the dragged column is compatible', () => {
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -290,7 +290,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
},
columnId: 'col2',
})
- ).toBe('move_compatible');
+ ).toEqual({ dropType: 'move_compatible' });
});
it('returns undefined if the dragged column from different group uses the same field as the dropTarget', () => {
@@ -318,7 +318,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
};
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -357,7 +357,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
};
expect(
- getDropTypes({
+ getDropProps({
...defaultProps,
groupId,
dragDropContext: {
@@ -373,7 +373,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
columnId: 'col2',
filterOperations: (op: OperationMetadata) => op.isBucketed === false,
})
- ).toEqual('replace_incompatible');
+ ).toEqual({ dropType: 'replace_incompatible', nextLabel: 'Unique count' });
});
});
describe('onDrop', () => {
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts
index 69c7e8c3c2ae..be791b3c7f7c 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable.ts
@@ -10,21 +10,29 @@ import {
DatasourceDimensionDropHandlerProps,
isDraggedOperation,
DraggedOperation,
+ DropType,
} from '../../types';
import { IndexPatternColumn } from '../indexpattern';
-import { insertOrReplaceColumn, deleteColumn, getOperationTypesForField } from '../operations';
+import {
+ insertOrReplaceColumn,
+ deleteColumn,
+ getOperationTypesForField,
+ getOperationDisplay,
+} from '../operations';
import { mergeLayer } from '../state_helpers';
import { hasField, isDraggedField } from '../utils';
-import { IndexPatternPrivateState, IndexPatternField, DraggedField } from '../types';
+import { IndexPatternPrivateState, DraggedField } from '../types';
import { trackUiEvent } from '../../lens_ui_telemetry';
type DropHandlerProps = DatasourceDimensionDropHandlerProps & {
droppedItem: T;
};
-export function getDropTypes(
+const operationLabels = getOperationDisplay();
+
+export function getDropProps(
props: DatasourceDimensionDropProps & { groupId: string }
-) {
+): { dropType: DropType; nextLabel?: string } | undefined {
const { dragging } = props.dragDropContext;
if (!dragging) {
return;
@@ -32,23 +40,27 @@ export function getDropTypes(
const layerIndexPatternId = props.state.layers[props.layerId].indexPatternId;
- function hasOperationForField(field: IndexPatternField) {
- const operationsForNewField = getOperationTypesForField(field, props.filterOperations);
- return !!operationsForNewField.length;
- }
-
const currentColumn = props.state.layers[props.layerId].columns[props.columnId];
if (isDraggedField(dragging)) {
- if (
- !!(layerIndexPatternId === dragging.indexPatternId && hasOperationForField(dragging.field))
- ) {
+ const operationsForNewField = getOperationTypesForField(dragging.field, props.filterOperations);
+
+ if (!!(layerIndexPatternId === dragging.indexPatternId && operationsForNewField.length)) {
+ const highestPriorityOperationLabel = operationLabels[operationsForNewField[0]].displayName;
if (!currentColumn) {
- return 'field_add';
+ return { dropType: 'field_add', nextLabel: highestPriorityOperationLabel };
} else if (
(hasField(currentColumn) && currentColumn.sourceField !== dragging.field.name) ||
!hasField(currentColumn)
) {
- return 'field_replace';
+ const persistingOperationLabel =
+ currentColumn &&
+ operationsForNewField.includes(currentColumn.operationType) &&
+ operationLabels[currentColumn.operationType].displayName;
+
+ return {
+ dropType: 'field_replace',
+ nextLabel: persistingOperationLabel || highestPriorityOperationLabel,
+ };
}
}
return;
@@ -62,9 +74,9 @@ export function getDropTypes(
// same group
if (props.groupId === dragging.groupId) {
if (currentColumn) {
- return 'reorder';
+ return { dropType: 'reorder' };
}
- return 'duplicate_in_group';
+ return { dropType: 'duplicate_in_group' };
}
// compatible group
@@ -80,20 +92,34 @@ export function getDropTypes(
}
if (props.filterOperations(op)) {
if (currentColumn) {
- return 'replace_compatible'; // in the future also 'swap_compatible' and 'duplicate_compatible'
+ return { dropType: 'replace_compatible' }; // in the future also 'swap_compatible' and 'duplicate_compatible'
} else {
- return 'move_compatible'; // in the future also 'duplicate_compatible'
+ return { dropType: 'move_compatible' }; // in the future also 'duplicate_compatible'
}
}
// suggest
const field =
hasField(op) && props.state.indexPatterns[layerIndexPatternId].getFieldByName(op.sourceField);
- if (field && hasOperationForField(field)) {
+ const operationsForNewField = field && getOperationTypesForField(field, props.filterOperations);
+
+ if (operationsForNewField && operationsForNewField?.length) {
+ const highestPriorityOperationLabel = operationLabels[operationsForNewField[0]].displayName;
+
if (currentColumn) {
- return 'replace_incompatible'; // in the future also 'swap_incompatible', 'duplicate_incompatible'
+ const persistingOperationLabel =
+ currentColumn &&
+ operationsForNewField.includes(currentColumn.operationType) &&
+ operationLabels[currentColumn.operationType].displayName;
+ return {
+ dropType: 'replace_incompatible',
+ nextLabel: persistingOperationLabel || highestPriorityOperationLabel,
+ }; // in the future also 'swap_incompatible', 'duplicate_incompatible'
} else {
- return 'move_incompatible'; // in the future also 'duplicate_incompatible'
+ return {
+ dropType: 'move_incompatible',
+ nextLabel: highestPriorityOperationLabel,
+ }; // in the future also 'duplicate_incompatible'
}
}
}
@@ -178,6 +204,12 @@ function onMoveDropToNonCompatibleGroup(props: DropHandlerProps {
renderDimensionTrigger: (domElement: Element, props: DatasourceDimensionTriggerProps) => void;
renderDimensionEditor: (domElement: Element, props: DatasourceDimensionEditorProps) => void;
renderLayerPanel: (domElement: Element, props: DatasourceLayerPanelProps) => void;
- getDropTypes: (
+ getDropProps: (
props: DatasourceDimensionDropProps & { groupId: string }
- ) => DropType | undefined;
+ ) => { dropType: DropType; nextLabel?: string } | undefined;
onDrop: (props: DatasourceDimensionDropHandlerProps) => false | true | { deleted: string };
updateStateOnCloseDimension?: (props: {
layerId: string;