mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Lens] Implement drag and drop between layers (#132018)
* added dnd between layers * Andrew's comments addressed Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
This commit is contained in:
parent
bf65d4c261
commit
d11c0be465
50 changed files with 5519 additions and 3586 deletions
|
@ -41,7 +41,14 @@ describe('DragDrop', () => {
|
|||
|
||||
const value = {
|
||||
id: '1',
|
||||
humanData: { label: 'hello', groupLabel: 'X', position: 1, canSwap: true, canDuplicate: true },
|
||||
humanData: {
|
||||
label: 'hello',
|
||||
groupLabel: 'X',
|
||||
position: 1,
|
||||
canSwap: true,
|
||||
canDuplicate: true,
|
||||
layerNumber: 0,
|
||||
},
|
||||
};
|
||||
|
||||
test('renders if nothing is being dragged', () => {
|
||||
|
@ -205,7 +212,7 @@ describe('DragDrop', () => {
|
|||
order={[2, 0, 1, 0]}
|
||||
onDrop={(x: unknown) => {}}
|
||||
dropTypes={undefined}
|
||||
value={{ id: '2', humanData: { label: 'label2' } }}
|
||||
value={{ id: '2', humanData: { label: 'label2', layerNumber: 0 } }}
|
||||
>
|
||||
<button>Hello!</button>
|
||||
</DragDrop>
|
||||
|
@ -231,7 +238,7 @@ describe('DragDrop', () => {
|
|||
}}
|
||||
>
|
||||
<DragDrop
|
||||
value={{ id: '3', humanData: { label: 'ignored' } }}
|
||||
value={{ id: '3', humanData: { label: 'ignored', layerNumber: 0 } }}
|
||||
draggable={true}
|
||||
order={[2, 0, 1, 0]}
|
||||
>
|
||||
|
@ -286,7 +293,7 @@ describe('DragDrop', () => {
|
|||
registerDropTarget={jest.fn()}
|
||||
>
|
||||
<DragDrop
|
||||
value={{ id: '3', humanData: { label: 'ignored' } }}
|
||||
value={{ id: '3', humanData: { label: 'ignored', layerNumber: 0 } }}
|
||||
draggable={true}
|
||||
order={[2, 0, 1, 0]}
|
||||
>
|
||||
|
@ -329,7 +336,7 @@ describe('DragDrop', () => {
|
|||
draggable: true,
|
||||
value: {
|
||||
id: '1',
|
||||
humanData: { label: 'Label1', position: 1 },
|
||||
humanData: { label: 'Label1', position: 1, layerNumber: 0 },
|
||||
},
|
||||
children: '1',
|
||||
order: [2, 0, 0, 0],
|
||||
|
@ -341,7 +348,7 @@ describe('DragDrop', () => {
|
|||
value: {
|
||||
id: '2',
|
||||
|
||||
humanData: { label: 'label2', position: 1 },
|
||||
humanData: { label: 'label2', position: 1, layerNumber: 0 },
|
||||
},
|
||||
onDrop,
|
||||
dropTypes: ['move_compatible'] as DropType[],
|
||||
|
@ -358,6 +365,7 @@ describe('DragDrop', () => {
|
|||
groupLabel: 'Y',
|
||||
canSwap: true,
|
||||
canDuplicate: true,
|
||||
layerNumber: 0,
|
||||
},
|
||||
},
|
||||
onDrop,
|
||||
|
@ -373,7 +381,7 @@ describe('DragDrop', () => {
|
|||
dragType: 'move' as 'copy' | 'move',
|
||||
value: {
|
||||
id: '4',
|
||||
humanData: { label: 'label4', position: 2, groupLabel: 'Y' },
|
||||
humanData: { label: 'label4', position: 2, groupLabel: 'Y', layerNumber: 0 },
|
||||
},
|
||||
order: [2, 0, 2, 1],
|
||||
},
|
||||
|
@ -415,11 +423,11 @@ describe('DragDrop', () => {
|
|||
});
|
||||
keyboardHandler.simulate('keydown', { key: 'Enter' });
|
||||
expect(setA11yMessage).toBeCalledWith(
|
||||
`You're dragging Label1 from at position 1 over label3 from Y group at position 1. Press space or enter to replace label3 with Label1. Hold alt or option to duplicate. Hold shift to swap.`
|
||||
`You're dragging Label1 from at position 1 in layer 0 over label3 from Y group at position 1 in layer 0. Press space or enter to replace label3 with Label1. Hold alt or option to duplicate. Hold shift to swap.`
|
||||
);
|
||||
expect(setActiveDropTarget).toBeCalledWith(undefined);
|
||||
expect(onDrop).toBeCalledWith(
|
||||
{ humanData: { label: 'Label1', position: 1 }, id: '1' },
|
||||
{ humanData: { label: 'Label1', position: 1, layerNumber: 0 }, id: '1' },
|
||||
'move_compatible'
|
||||
);
|
||||
});
|
||||
|
@ -474,7 +482,7 @@ describe('DragDrop', () => {
|
|||
draggable: true,
|
||||
value: {
|
||||
id: '1',
|
||||
humanData: { label: 'Label1', position: 1 },
|
||||
humanData: { label: 'Label1', position: 1, layerNumber: 0 },
|
||||
},
|
||||
children: '1',
|
||||
order: [2, 0, 0, 0],
|
||||
|
@ -486,7 +494,7 @@ describe('DragDrop', () => {
|
|||
value: {
|
||||
id: '2',
|
||||
|
||||
humanData: { label: 'label2', position: 1 },
|
||||
humanData: { label: 'label2', position: 1, layerNumber: 0 },
|
||||
},
|
||||
onDrop,
|
||||
dropTypes: ['move_compatible'] as DropType[],
|
||||
|
@ -533,7 +541,7 @@ describe('DragDrop', () => {
|
|||
component = mount(
|
||||
<ChildDragDropProvider
|
||||
setA11yMessage={jest.fn()}
|
||||
dragging={{ id: '1', humanData: { label: 'Label1' } }}
|
||||
dragging={{ id: '1', humanData: { label: 'Label1', layerNumber: 0 } }}
|
||||
setDragging={jest.fn()}
|
||||
setActiveDropTarget={setActiveDropTarget}
|
||||
activeDropTarget={activeDropTarget}
|
||||
|
@ -543,7 +551,7 @@ describe('DragDrop', () => {
|
|||
registerDropTarget={jest.fn()}
|
||||
>
|
||||
<DragDrop
|
||||
value={{ id: '3', humanData: { label: 'ignored' } }}
|
||||
value={{ id: '3', humanData: { label: 'ignored', layerNumber: 0 } }}
|
||||
draggable={true}
|
||||
order={[2, 0, 1, 0]}
|
||||
>
|
||||
|
@ -629,18 +637,24 @@ describe('DragDrop', () => {
|
|||
|
||||
component.find('SingleDropInner').at(0).simulate('dragover');
|
||||
component.find('SingleDropInner').at(0).simulate('drop');
|
||||
expect(onDrop).toBeCalledWith({ humanData: { label: 'Label1' }, id: '1' }, 'move_compatible');
|
||||
expect(onDrop).toBeCalledWith(
|
||||
{ humanData: { label: 'Label1', layerNumber: 0 }, id: '1' },
|
||||
'move_compatible'
|
||||
);
|
||||
|
||||
component.find('SingleDropInner').at(1).simulate('dragover');
|
||||
component.find('SingleDropInner').at(1).simulate('drop');
|
||||
expect(onDrop).toBeCalledWith(
|
||||
{ humanData: { label: 'Label1' }, id: '1' },
|
||||
{ humanData: { label: 'Label1', layerNumber: 0 }, id: '1' },
|
||||
'duplicate_compatible'
|
||||
);
|
||||
|
||||
component.find('SingleDropInner').at(2).simulate('dragover');
|
||||
component.find('SingleDropInner').at(2).simulate('drop');
|
||||
expect(onDrop).toBeCalledWith({ humanData: { label: 'Label1' }, id: '1' }, 'swap_compatible');
|
||||
expect(onDrop).toBeCalledWith(
|
||||
{ humanData: { label: 'Label1', layerNumber: 0 }, id: '1' },
|
||||
'swap_compatible'
|
||||
);
|
||||
});
|
||||
|
||||
test('pressing Alt or Shift when dragging over the main drop target sets extra drop target as active', () => {
|
||||
|
@ -693,7 +707,7 @@ describe('DragDrop', () => {
|
|||
draggable: true,
|
||||
value: {
|
||||
id: '1',
|
||||
humanData: { label: 'Label1', position: 1 },
|
||||
humanData: { label: 'Label1', position: 1, layerNumber: 0 },
|
||||
},
|
||||
children: '1',
|
||||
order: [2, 0, 0, 0],
|
||||
|
@ -705,7 +719,7 @@ describe('DragDrop', () => {
|
|||
value: {
|
||||
id: '2',
|
||||
|
||||
humanData: { label: 'label2', position: 1 },
|
||||
humanData: { label: 'label2', position: 1, layerNumber: 0 },
|
||||
},
|
||||
onDrop,
|
||||
dropTypes: ['move_compatible', 'duplicate_compatible', 'swap_compatible'] as DropType[],
|
||||
|
@ -716,7 +730,7 @@ describe('DragDrop', () => {
|
|||
dragType: 'move' as const,
|
||||
value: {
|
||||
id: '3',
|
||||
humanData: { label: 'label3', position: 1, groupLabel: 'Y' },
|
||||
humanData: { label: 'label3', position: 1, groupLabel: 'Y', layerNumber: 0 },
|
||||
},
|
||||
onDrop,
|
||||
dropTypes: ['replace_compatible'] as DropType[],
|
||||
|
@ -734,6 +748,7 @@ describe('DragDrop', () => {
|
|||
humanData: {
|
||||
label: 'label2',
|
||||
position: 1,
|
||||
layerNumber: 0,
|
||||
},
|
||||
id: '2',
|
||||
onDrop,
|
||||
|
@ -743,6 +758,7 @@ describe('DragDrop', () => {
|
|||
humanData: {
|
||||
label: 'label2',
|
||||
position: 1,
|
||||
layerNumber: 0,
|
||||
},
|
||||
id: '2',
|
||||
onDrop,
|
||||
|
@ -753,6 +769,7 @@ describe('DragDrop', () => {
|
|||
groupLabel: 'Y',
|
||||
label: 'label3',
|
||||
position: 1,
|
||||
layerNumber: 0,
|
||||
},
|
||||
id: '3',
|
||||
onDrop,
|
||||
|
@ -942,18 +959,18 @@ describe('DragDrop', () => {
|
|||
const items = [
|
||||
{
|
||||
id: '1',
|
||||
humanData: { label: 'Label1', position: 1, groupLabel: 'X' },
|
||||
humanData: { label: 'Label1', position: 1, groupLabel: 'X', layerNumber: 0 },
|
||||
onDrop,
|
||||
draggable: true,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
humanData: { label: 'label2', position: 2, groupLabel: 'X' },
|
||||
humanData: { label: 'label2', position: 2, groupLabel: 'X', layerNumber: 0 },
|
||||
onDrop,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
humanData: { label: 'label3', position: 3, groupLabel: 'X' },
|
||||
humanData: { label: 'label3', position: 3, groupLabel: 'X', layerNumber: 0 },
|
||||
onDrop,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -22,19 +22,21 @@ interface CustomAnnouncementsType {
|
|||
|
||||
const replaceAnnouncement = {
|
||||
selectedTarget: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
canSwap,
|
||||
canDuplicate,
|
||||
canCombine,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData,
|
||||
announceModifierKeys?: boolean
|
||||
) => {
|
||||
if (announceModifierKeys && (canSwap || canDuplicate)) {
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.replaceMain', {
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} over {dropLabel} from {dropGroupLabel} group at position {dropPosition}. Press space or enter to replace {dropLabel} with {label}.{duplicateCopy}{swapCopy}`,
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} in layer {layerNumber} over {dropLabel} from {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Press space or enter to replace {dropLabel} with {label}.{duplicateCopy}{swapCopy}{combineCopy}`,
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -44,63 +46,76 @@ const replaceAnnouncement = {
|
|||
dropPosition,
|
||||
duplicateCopy: canDuplicate ? DUPLICATE_SHORT : '',
|
||||
swapCopy: canSwap ? SWAP_SHORT : '',
|
||||
combineCopy: canCombine ? COMBINE_SHORT : '',
|
||||
layerNumber,
|
||||
dropLayerNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.replace', {
|
||||
defaultMessage: `Replace {dropLabel} in {dropGroupLabel} group at position {dropPosition} with {label}. Press space or enter to replace.`,
|
||||
defaultMessage: `Replace {dropLabel} in {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber} with {label}. Press space or enter to replace.`,
|
||||
values: {
|
||||
label,
|
||||
dropLabel,
|
||||
dropGroupLabel,
|
||||
dropPosition,
|
||||
dropLayerNumber,
|
||||
},
|
||||
});
|
||||
},
|
||||
dropped: ({ label }: HumanData, { label: dropLabel, groupLabel, position }: HumanData) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.duplicated.replace', {
|
||||
defaultMessage: 'Replaced {dropLabel} with {label} in {groupLabel} at position {position}',
|
||||
dropped: (
|
||||
{ label }: HumanData,
|
||||
{ label: dropLabel, groupLabel, position, layerNumber: dropLayerNumber }: HumanData
|
||||
) => {
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.duplicated.replace', {
|
||||
defaultMessage:
|
||||
'Replaced {dropLabel} with {label} in {groupLabel} at position {position} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
dropLabel,
|
||||
groupLabel,
|
||||
position,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const duplicateAnnouncement = {
|
||||
selectedTarget: (
|
||||
{ label, groupLabel }: HumanData,
|
||||
{ label, groupLabel, layerNumber }: HumanData,
|
||||
{ groupLabel: dropGroupLabel, position }: HumanData
|
||||
) => {
|
||||
if (groupLabel !== dropGroupLabel) {
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.duplicated', {
|
||||
defaultMessage: `Duplicate {label} to {dropGroupLabel} group at position {position}. Hold Alt or Option and press space or enter to duplicate`,
|
||||
defaultMessage: `Duplicate {label} to {dropGroupLabel} group at position {position} in layer {layerNumber}. Hold Alt or Option and press space or enter to duplicate`,
|
||||
values: {
|
||||
label,
|
||||
dropGroupLabel,
|
||||
position,
|
||||
layerNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.duplicatedInGroup', {
|
||||
defaultMessage: `Duplicate {label} to {dropGroupLabel} group at position {position}. Press space or enter to duplicate`,
|
||||
defaultMessage: `Duplicate {label} to {dropGroupLabel} group at position {position} in layer {layerNumber}. Press space or enter to duplicate`,
|
||||
values: {
|
||||
label,
|
||||
dropGroupLabel,
|
||||
position,
|
||||
layerNumber,
|
||||
},
|
||||
});
|
||||
},
|
||||
dropped: ({ label }: HumanData, { groupLabel, position }: HumanData) =>
|
||||
dropped: ({ label }: HumanData, { groupLabel, position, layerNumber }: HumanData) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.duplicated', {
|
||||
defaultMessage: 'Duplicated {label} in {groupLabel} group at position {position}',
|
||||
defaultMessage:
|
||||
'Duplicated {label} in {groupLabel} group at position {position} in layer {layerNumber}',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
position,
|
||||
layerNumber,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
@ -109,8 +124,8 @@ const reorderAnnouncement = {
|
|||
selectedTarget: (
|
||||
{ label, groupLabel, position: prevPosition }: HumanData,
|
||||
{ position }: HumanData
|
||||
) =>
|
||||
prevPosition === position
|
||||
) => {
|
||||
return prevPosition === position
|
||||
? i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.reorderedBack', {
|
||||
defaultMessage: `{label} returned to its initial position {prevPosition}`,
|
||||
values: {
|
||||
|
@ -121,12 +136,13 @@ const reorderAnnouncement = {
|
|||
: i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.reordered', {
|
||||
defaultMessage: `Reorder {label} in {groupLabel} group from position {prevPosition} to position {position}. Press space or enter to reorder`,
|
||||
values: {
|
||||
groupLabel,
|
||||
label,
|
||||
groupLabel,
|
||||
position,
|
||||
prevPosition,
|
||||
},
|
||||
}),
|
||||
});
|
||||
},
|
||||
dropped: ({ label, groupLabel, position: prevPosition }: HumanData, { position }: HumanData) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.reordered', {
|
||||
defaultMessage:
|
||||
|
@ -142,7 +158,7 @@ const reorderAnnouncement = {
|
|||
|
||||
const combineAnnouncement = {
|
||||
selectedTarget: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
|
@ -150,12 +166,13 @@ const combineAnnouncement = {
|
|||
canSwap,
|
||||
canDuplicate,
|
||||
canCombine,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData,
|
||||
announceModifierKeys?: boolean
|
||||
) => {
|
||||
if (announceModifierKeys && (canSwap || canDuplicate || canCombine)) {
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.combineMain', {
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} over {dropLabel} from {dropGroupLabel} group at position {dropPosition}. Press space or enter to combine {dropLabel} with {label}.{duplicateCopy}{swapCopy}{combineCopy}`,
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} in layer {layerNumber} over {dropLabel} from {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Press space or enter to combine {dropLabel} with {label}.{duplicateCopy}{swapCopy}{combineCopy}`,
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -166,28 +183,35 @@ const combineAnnouncement = {
|
|||
duplicateCopy: canDuplicate ? DUPLICATE_SHORT : '',
|
||||
swapCopy: canSwap ? SWAP_SHORT : '',
|
||||
combineCopy: canCombine ? COMBINE_SHORT : '',
|
||||
layerNumber,
|
||||
dropLayerNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.combine', {
|
||||
defaultMessage: `Combine {dropLabel} in {dropGroupLabel} group at position {dropPosition} with {label}. Press space or enter to combine.`,
|
||||
defaultMessage: `Combine {dropLabel} in {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber} with {label}. Press space or enter to combine.`,
|
||||
values: {
|
||||
label,
|
||||
dropLabel,
|
||||
dropGroupLabel,
|
||||
dropPosition,
|
||||
dropLayerNumber,
|
||||
},
|
||||
});
|
||||
},
|
||||
dropped: ({ label }: HumanData, { label: dropLabel, groupLabel, position }: HumanData) =>
|
||||
dropped: (
|
||||
{ label }: HumanData,
|
||||
{ label: dropLabel, groupLabel, position, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.duplicated.combine', {
|
||||
defaultMessage: 'Combine {dropLabel} with {label} in {groupLabel} at position {position}',
|
||||
defaultMessage:
|
||||
'Combine {dropLabel} with {label} in {groupLabel} at position {position} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
dropLabel,
|
||||
groupLabel,
|
||||
position,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
@ -212,7 +236,7 @@ export const announcements: CustomAnnouncementsType = {
|
|||
field_combine: combineAnnouncement.selectedTarget,
|
||||
replace_compatible: replaceAnnouncement.selectedTarget,
|
||||
replace_incompatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
|
@ -220,14 +244,16 @@ export const announcements: CustomAnnouncementsType = {
|
|||
nextLabel,
|
||||
canSwap,
|
||||
canDuplicate,
|
||||
canCombine,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData,
|
||||
announceModifierKeys?: boolean
|
||||
) => {
|
||||
if (announceModifierKeys && (canSwap || canDuplicate)) {
|
||||
if (announceModifierKeys && (canSwap || canDuplicate || canCombine)) {
|
||||
return i18n.translate(
|
||||
'xpack.lens.dragDrop.announce.selectedTarget.replaceIncompatibleMain',
|
||||
{
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} over {dropLabel} from {dropGroupLabel} group at position {dropPosition}. Press space or enter to convert {label} to {nextLabel} and replace {dropLabel}.{duplicateCopy}{swapCopy}`,
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} in layer {layerNumber} over {dropLabel} from {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Press space or enter to convert {label} to {nextLabel} and replace {dropLabel}.{duplicateCopy}{swapCopy}{combineCopy}`,
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -238,35 +264,40 @@ export const announcements: CustomAnnouncementsType = {
|
|||
nextLabel,
|
||||
duplicateCopy: canDuplicate ? DUPLICATE_SHORT : '',
|
||||
swapCopy: canSwap ? SWAP_SHORT : '',
|
||||
combineCopy: canCombine ? COMBINE_SHORT : '',
|
||||
layerNumber,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.replaceIncompatible', {
|
||||
defaultMessage: `Convert {label} to {nextLabel} and replace {dropLabel} in {dropGroupLabel} group at position {dropPosition}. Press space or enter to replace`,
|
||||
defaultMessage: `Convert {label} to {nextLabel} and replace {dropLabel} in {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Press space or enter to replace`,
|
||||
values: {
|
||||
label,
|
||||
nextLabel,
|
||||
dropLabel,
|
||||
dropGroupLabel,
|
||||
dropPosition,
|
||||
nextLabel,
|
||||
dropLayerNumber,
|
||||
},
|
||||
});
|
||||
},
|
||||
move_incompatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
nextLabel,
|
||||
canSwap,
|
||||
canDuplicate,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData,
|
||||
announceModifierKeys?: boolean
|
||||
) => {
|
||||
if (announceModifierKeys && (canSwap || canDuplicate)) {
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.moveIncompatibleMain', {
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} over position {dropPosition} in {dropGroupLabel} group. Press space or enter to convert {label} to {nextLabel} and move.{duplicateCopy}{swapCopy}`,
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} in layer {layerNumber} over position {dropPosition} in {dropGroupLabel} group in layer {dropLayerNumber}. Press space or enter to convert {label} to {nextLabel} and move.{duplicateCopy}`,
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -275,29 +306,37 @@ export const announcements: CustomAnnouncementsType = {
|
|||
dropPosition,
|
||||
nextLabel,
|
||||
duplicateCopy: canDuplicate ? DUPLICATE_SHORT : '',
|
||||
swapCopy: canSwap ? SWAP_SHORT : '',
|
||||
layerNumber,
|
||||
dropLayerNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.moveIncompatible', {
|
||||
defaultMessage: `Convert {label} to {nextLabel} and move to {dropGroupLabel} group at position {dropPosition}. Press space or enter to move`,
|
||||
defaultMessage: `Convert {label} to {nextLabel} and move to {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Press space or enter to move`,
|
||||
values: {
|
||||
label,
|
||||
nextLabel,
|
||||
dropGroupLabel,
|
||||
dropPosition,
|
||||
nextLabel,
|
||||
dropLayerNumber,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
move_compatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ groupLabel: dropGroupLabel, position: dropPosition, canSwap, canDuplicate }: HumanData,
|
||||
{
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
canSwap,
|
||||
canDuplicate,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData,
|
||||
announceModifierKeys?: boolean
|
||||
) => {
|
||||
if (announceModifierKeys && (canSwap || canDuplicate)) {
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.moveCompatibleMain', {
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} over position {dropPosition} in {dropGroupLabel} group. Press space or enter to move.{duplicateCopy}{swapCopy}`,
|
||||
defaultMessage: `You're dragging {label} from {groupLabel} at position {position} over position {dropPosition} in {dropGroupLabel} group in layer {dropLayerNumber}. Press space or enter to move.{duplicateCopy}`,
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -305,69 +344,78 @@ export const announcements: CustomAnnouncementsType = {
|
|||
dropGroupLabel,
|
||||
dropPosition,
|
||||
duplicateCopy: canDuplicate ? DUPLICATE_SHORT : '',
|
||||
swapCopy: canSwap ? SWAP_SHORT : '',
|
||||
dropLayerNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
return i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.moveCompatible', {
|
||||
defaultMessage: `Move {label} to {dropGroupLabel} group at position {dropPosition}. Press space or enter to move`,
|
||||
defaultMessage: `Move {label} to {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Press space or enter to move`,
|
||||
values: {
|
||||
label,
|
||||
dropGroupLabel,
|
||||
dropPosition,
|
||||
dropLayerNumber,
|
||||
},
|
||||
});
|
||||
},
|
||||
duplicate_incompatible: (
|
||||
{ label }: HumanData,
|
||||
{ groupLabel, position, nextLabel }: HumanData
|
||||
{ groupLabel, position, nextLabel, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.duplicateIncompatible', {
|
||||
defaultMessage:
|
||||
'Convert copy of {label} to {nextLabel} and add to {groupLabel} group at position {position}. Hold Alt or Option and press space or enter to duplicate',
|
||||
'Convert copy of {label} to {nextLabel} and add to {groupLabel} group at position {position} in layer {dropLayerNumber}. Hold Alt or Option and press space or enter to duplicate',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
position,
|
||||
nextLabel,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
replace_duplicate_incompatible: (
|
||||
{ label }: HumanData,
|
||||
{ label: dropLabel, groupLabel, position, nextLabel }: HumanData
|
||||
{ label: dropLabel, groupLabel, position, nextLabel, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.replaceDuplicateIncompatible', {
|
||||
defaultMessage:
|
||||
'Convert copy of {label} to {nextLabel} and replace {dropLabel} in {groupLabel} group at position {position}. Hold Alt or Option and press space or enter to duplicate and replace',
|
||||
'Convert copy of {label} to {nextLabel} and replace {dropLabel} in {groupLabel} group at position {position} in layer {dropLayerNumber}. Hold Alt or Option and press space or enter to duplicate and replace',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
position,
|
||||
dropLabel,
|
||||
nextLabel,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
replace_duplicate_compatible: (
|
||||
{ label }: HumanData,
|
||||
{ label: dropLabel, groupLabel, position }: HumanData
|
||||
{ label: dropLabel, groupLabel, position, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.replaceDuplicateCompatible', {
|
||||
defaultMessage:
|
||||
'Duplicate {label} and replace {dropLabel} in {groupLabel} at position {position}. Hold Alt or Option and press space or enter to duplicate and replace',
|
||||
'Duplicate {label} and replace {dropLabel} in {groupLabel} at position {position} in layer {dropLayerNumber}. Hold Alt or Option and press space or enter to duplicate and replace',
|
||||
values: {
|
||||
label,
|
||||
dropLabel,
|
||||
groupLabel,
|
||||
position,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
swap_compatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label: dropLabel, groupLabel: dropGroupLabel, position: dropPosition }: HumanData
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.swapCompatible', {
|
||||
defaultMessage:
|
||||
'Swap {label} in {groupLabel} group at position {position} with {dropLabel} in {dropGroupLabel} group at position {dropPosition}. Hold Shift and press space or enter to swap',
|
||||
'Swap {label} in {groupLabel} group at position {position} in layer {layerNumber} with {dropLabel} in {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Hold Shift and press space or enter to swap',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -375,15 +423,23 @@ export const announcements: CustomAnnouncementsType = {
|
|||
dropLabel,
|
||||
dropGroupLabel,
|
||||
dropPosition,
|
||||
layerNumber,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
swap_incompatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label: dropLabel, groupLabel: dropGroupLabel, position: dropPosition, nextLabel }: HumanData
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
nextLabel,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.swapIncompatible', {
|
||||
defaultMessage:
|
||||
'Convert {label} to {nextLabel} in {groupLabel} group at position {position} and swap with {dropLabel} in {dropGroupLabel} group at position {dropPosition}. Hold Shift and press space or enter to swap',
|
||||
'Convert {label} to {nextLabel} in {groupLabel} group at position {position} in layer {layerNumber} and swap with {dropLabel} in {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Hold Shift and press space or enter to swap',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -392,15 +448,22 @@ export const announcements: CustomAnnouncementsType = {
|
|||
dropGroupLabel,
|
||||
dropPosition,
|
||||
nextLabel,
|
||||
layerNumber,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
combine_compatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label: dropLabel, groupLabel: dropGroupLabel, position: dropPosition, nextLabel }: HumanData
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.combineCompatible', {
|
||||
defaultMessage:
|
||||
'Combine {label} in {groupLabel} group at position {position} with {dropLabel} in {dropGroupLabel} group at position {dropPosition}. Hold Control and press space or enter to combine',
|
||||
'Combine {label} in {groupLabel} group at position {position} in layer {layerNumber} with {dropLabel} in {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Hold Control and press space or enter to combine',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -408,15 +471,23 @@ export const announcements: CustomAnnouncementsType = {
|
|||
dropLabel,
|
||||
dropGroupLabel,
|
||||
dropPosition,
|
||||
layerNumber,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
combine_incompatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label: dropLabel, groupLabel: dropGroupLabel, position: dropPosition, nextLabel }: HumanData
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
nextLabel,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.combineIncompatible', {
|
||||
defaultMessage:
|
||||
'Convert {label} to {nextLabel} in {groupLabel} group at position {position} and combine with {dropLabel} in {dropGroupLabel} group at position {dropPosition}. Hold Control and press space or enter to combine',
|
||||
'Convert {label} to {nextLabel} in {groupLabel} group at position {position} in layer {layerNumber} and combine with {dropLabel} in {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}. Hold Control and press space or enter to combine',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -425,6 +496,8 @@ export const announcements: CustomAnnouncementsType = {
|
|||
dropGroupLabel,
|
||||
dropPosition,
|
||||
nextLabel,
|
||||
dropLayerNumber,
|
||||
layerNumber,
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -436,92 +509,110 @@ export const announcements: CustomAnnouncementsType = {
|
|||
replace_compatible: replaceAnnouncement.dropped,
|
||||
replace_incompatible: (
|
||||
{ label }: HumanData,
|
||||
{ label: dropLabel, groupLabel, position, nextLabel }: HumanData
|
||||
{ label: dropLabel, groupLabel, position, nextLabel, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.replaceIncompatible', {
|
||||
defaultMessage:
|
||||
'Converted {label} to {nextLabel} and replaced {dropLabel} in {groupLabel} group at position {position}',
|
||||
'Converted {label} to {nextLabel} and replaced {dropLabel} in {groupLabel} group at position {position} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
nextLabel,
|
||||
dropLabel,
|
||||
groupLabel,
|
||||
position,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
move_incompatible: ({ label }: HumanData, { groupLabel, position, nextLabel }: HumanData) =>
|
||||
move_incompatible: (
|
||||
{ label }: HumanData,
|
||||
{ groupLabel, position, nextLabel, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.moveIncompatible', {
|
||||
defaultMessage:
|
||||
'Converted {label} to {nextLabel} and moved to {groupLabel} group at position {position}',
|
||||
'Converted {label} to {nextLabel} and moved to {groupLabel} group at position {position} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
nextLabel,
|
||||
groupLabel,
|
||||
position,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
|
||||
move_compatible: ({ label }: HumanData, { groupLabel, position }: HumanData) =>
|
||||
move_compatible: (
|
||||
{ label }: HumanData,
|
||||
{ groupLabel, position, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.moveCompatible', {
|
||||
defaultMessage: 'Moved {label} to {groupLabel} group at position {position}',
|
||||
defaultMessage:
|
||||
'Moved {label} to {groupLabel} group at position {position} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
position,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
|
||||
duplicate_incompatible: (
|
||||
{ label }: HumanData,
|
||||
{ groupLabel, position, nextLabel }: HumanData
|
||||
{ groupLabel, position, nextLabel, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.duplicateIncompatible', {
|
||||
defaultMessage:
|
||||
'Converted copy of {label} to {nextLabel} and added to {groupLabel} group at position {position}',
|
||||
'Converted copy of {label} to {nextLabel} and added to {groupLabel} group at position {position} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
position,
|
||||
nextLabel,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
|
||||
replace_duplicate_incompatible: (
|
||||
{ label }: HumanData,
|
||||
{ label: dropLabel, groupLabel, position, nextLabel }: HumanData
|
||||
{ label: dropLabel, groupLabel, position, nextLabel, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.replaceDuplicateIncompatible', {
|
||||
defaultMessage:
|
||||
'Converted copy of {label} to {nextLabel} and replaced {dropLabel} in {groupLabel} group at position {position}',
|
||||
'Converted copy of {label} to {nextLabel} and replaced {dropLabel} in {groupLabel} group at position {position} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
dropLabel,
|
||||
groupLabel,
|
||||
position,
|
||||
nextLabel,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
replace_duplicate_compatible: (
|
||||
{ label }: HumanData,
|
||||
{ label: dropLabel, groupLabel, position }: HumanData
|
||||
{ label: dropLabel, groupLabel, position, layerNumber: dropLayerNumber }: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.duplicated.replaceDuplicateCompatible', {
|
||||
defaultMessage:
|
||||
'Replaced {dropLabel} with a copy of {label} in {groupLabel} at position {position}',
|
||||
'Replaced {dropLabel} with a copy of {label} in {groupLabel} at position {position} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
dropLabel,
|
||||
groupLabel,
|
||||
position,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
swap_compatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label: dropLabel, groupLabel: dropGroupLabel, position: dropPosition }: HumanData
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.swapCompatible', {
|
||||
defaultMessage:
|
||||
'Moved {label} to {dropGroupLabel} at position {dropPosition} and {dropLabel} to {groupLabel} group at position {position}',
|
||||
'Moved {label} to {dropGroupLabel} at position {dropPosition} in layer {dropLayerNumber} and {dropLabel} to {groupLabel} group at position {position} in layer {layerNumber}',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -529,15 +620,23 @@ export const announcements: CustomAnnouncementsType = {
|
|||
dropLabel,
|
||||
dropGroupLabel,
|
||||
dropPosition,
|
||||
layerNumber,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
swap_incompatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label: dropLabel, groupLabel: dropGroupLabel, position: dropPosition, nextLabel }: HumanData
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
nextLabel,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.swapIncompatible', {
|
||||
defaultMessage:
|
||||
'Converted {label} to {nextLabel} in {groupLabel} group at position {position} and swapped with {dropLabel} in {dropGroupLabel} group at position {dropPosition}',
|
||||
'Converted {label} to {nextLabel} in {groupLabel} group at position {position} in layer {layerNumber} and swapped with {dropLabel} in {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -546,31 +645,44 @@ export const announcements: CustomAnnouncementsType = {
|
|||
dropLabel,
|
||||
dropPosition,
|
||||
nextLabel,
|
||||
dropLayerNumber,
|
||||
layerNumber,
|
||||
},
|
||||
}),
|
||||
combine_compatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label: dropLabel, groupLabel: dropGroupLabel, position: dropPosition }: HumanData
|
||||
{ label, groupLabel }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.combineCompatible', {
|
||||
defaultMessage:
|
||||
'Combined {label} to {dropGroupLabel} at position {dropPosition} and {dropLabel} to {groupLabel} group at position {position}',
|
||||
'Combined {label} in group {groupLabel} to {dropLabel} in group {dropGroupLabel} at position {dropPosition} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
position,
|
||||
dropLabel,
|
||||
dropGroupLabel,
|
||||
dropPosition,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
combine_incompatible: (
|
||||
{ label, groupLabel, position }: HumanData,
|
||||
{ label: dropLabel, groupLabel: dropGroupLabel, position: dropPosition, nextLabel }: HumanData
|
||||
{ label, groupLabel, position, layerNumber }: HumanData,
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position: dropPosition,
|
||||
nextLabel,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) =>
|
||||
i18n.translate('xpack.lens.dragDrop.announce.dropped.combineIncompatible', {
|
||||
defaultMessage:
|
||||
'Converted {label} to {nextLabel} in {groupLabel} group at position {position} and combined with {dropLabel} in {dropGroupLabel} group at position {dropPosition}',
|
||||
'Converted {label} to {nextLabel} in {groupLabel} group at position {position} and combined with {dropLabel} in {dropGroupLabel} group at position {dropPosition} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
groupLabel,
|
||||
|
@ -579,6 +691,7 @@ export const announcements: CustomAnnouncementsType = {
|
|||
dropLabel,
|
||||
dropPosition,
|
||||
nextLabel,
|
||||
dropLayerNumber,
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -620,15 +733,22 @@ const defaultAnnouncements = {
|
|||
|
||||
dropped: (
|
||||
{ label }: HumanData,
|
||||
{ groupLabel: dropGroupLabel, position, label: dropLabel }: HumanData
|
||||
{
|
||||
groupLabel: dropGroupLabel,
|
||||
position,
|
||||
label: dropLabel,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) =>
|
||||
dropGroupLabel && position
|
||||
? i18n.translate('xpack.lens.dragDrop.announce.droppedDefault', {
|
||||
defaultMessage: 'Added {label} in {dropGroupLabel} group at position {position}',
|
||||
defaultMessage:
|
||||
'Added {label} in {dropGroupLabel} group at position {position} in layer {dropLayerNumber}',
|
||||
values: {
|
||||
label,
|
||||
dropGroupLabel,
|
||||
position,
|
||||
dropLayerNumber,
|
||||
},
|
||||
})
|
||||
: i18n.translate('xpack.lens.dragDrop.announce.droppedNoPosition', {
|
||||
|
@ -640,15 +760,21 @@ const defaultAnnouncements = {
|
|||
}),
|
||||
selectedTarget: (
|
||||
{ label }: HumanData,
|
||||
{ label: dropLabel, groupLabel: dropGroupLabel, position }: HumanData
|
||||
{
|
||||
label: dropLabel,
|
||||
groupLabel: dropGroupLabel,
|
||||
position,
|
||||
layerNumber: dropLayerNumber,
|
||||
}: HumanData
|
||||
) => {
|
||||
return dropGroupLabel && position
|
||||
? i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.default', {
|
||||
defaultMessage: `Add {label} to {dropGroupLabel} group at position {position}. Press space or enter to add`,
|
||||
defaultMessage: `Add {label} to {dropGroupLabel} group at position {position} in layer {dropLayerNumber}. Press space or enter to add`,
|
||||
values: {
|
||||
label,
|
||||
dropGroupLabel,
|
||||
position,
|
||||
dropLayerNumber,
|
||||
},
|
||||
})
|
||||
: i18n.translate('xpack.lens.dragDrop.announce.selectedTarget.defaultNoPosition', {
|
||||
|
@ -671,8 +797,15 @@ export const announce = {
|
|||
dropElement: HumanData,
|
||||
type?: DropType,
|
||||
announceModifierKeys?: boolean
|
||||
) =>
|
||||
(type &&
|
||||
announcements.selectedTarget?.[type]?.(draggedElement, dropElement, announceModifierKeys)) ||
|
||||
defaultAnnouncements.selectedTarget(draggedElement, dropElement),
|
||||
) => {
|
||||
return (
|
||||
(type &&
|
||||
announcements.selectedTarget?.[type]?.(
|
||||
draggedElement,
|
||||
dropElement,
|
||||
announceModifierKeys
|
||||
)) ||
|
||||
defaultAnnouncements.selectedTarget(draggedElement, dropElement)
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ import { DropType } from '../../types';
|
|||
export interface HumanData {
|
||||
label: string;
|
||||
groupLabel?: string;
|
||||
layerNumber?: number;
|
||||
position?: number;
|
||||
nextLabel?: string;
|
||||
canSwap?: boolean;
|
||||
|
|
|
@ -10,10 +10,10 @@ import { DragDrop, DragDropIdentifier, DragContext } from '../../../../drag_drop
|
|||
import {
|
||||
Datasource,
|
||||
VisualizationDimensionGroupConfig,
|
||||
isDraggedOperation,
|
||||
isOperation,
|
||||
DropType,
|
||||
DatasourceLayers,
|
||||
} from '../../../../types';
|
||||
import { LayerDatasourceDropProps } from '../types';
|
||||
import {
|
||||
getCustomDropTarget,
|
||||
getAdditionalClassesOnDroppable,
|
||||
|
@ -29,45 +29,53 @@ export function DraggableDimensionButton({
|
|||
layerIndex,
|
||||
columnId,
|
||||
group,
|
||||
groups,
|
||||
onDrop,
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
children,
|
||||
layerDatasourceDropProps,
|
||||
state,
|
||||
layerDatasource,
|
||||
datasourceLayers,
|
||||
registerNewButtonRef,
|
||||
}: {
|
||||
layerId: string;
|
||||
groupIndex: number;
|
||||
layerIndex: number;
|
||||
onDrop: (
|
||||
droppedItem: DragDropIdentifier,
|
||||
dropTarget: DragDropIdentifier,
|
||||
dropType?: DropType
|
||||
) => void;
|
||||
onDrop: (source: DragDropIdentifier, dropTarget: DragDropIdentifier, dropType?: DropType) => void;
|
||||
onDragStart: () => void;
|
||||
onDragEnd: () => void;
|
||||
group: VisualizationDimensionGroupConfig;
|
||||
groups: VisualizationDimensionGroupConfig[];
|
||||
label: string;
|
||||
children: ReactElement;
|
||||
layerDatasource: Datasource<unknown, unknown>;
|
||||
layerDatasourceDropProps: LayerDatasourceDropProps;
|
||||
datasourceLayers: DatasourceLayers;
|
||||
state: unknown;
|
||||
accessorIndex: number;
|
||||
columnId: string;
|
||||
registerNewButtonRef: (id: string, instance: HTMLDivElement | null) => void;
|
||||
}) {
|
||||
const { dragging } = useContext(DragContext);
|
||||
|
||||
const dropProps = getDropProps(layerDatasource, {
|
||||
...(layerDatasourceDropProps || {}),
|
||||
dragging,
|
||||
columnId,
|
||||
filterOperations: group.filterOperations,
|
||||
groupId: group.groupId,
|
||||
dimensionGroups: groups,
|
||||
});
|
||||
const sharedDatasource =
|
||||
!isOperation(dragging) ||
|
||||
datasourceLayers?.[dragging.layerId]?.datasourceId === datasourceLayers?.[layerId]?.datasourceId
|
||||
? layerDatasource
|
||||
: undefined;
|
||||
|
||||
const dropProps = getDropProps(
|
||||
{
|
||||
state,
|
||||
source: dragging,
|
||||
target: {
|
||||
layerId,
|
||||
columnId,
|
||||
groupId: group.groupId,
|
||||
filterOperations: group.filterOperations,
|
||||
prioritizedOperation: group.prioritizedOperation,
|
||||
},
|
||||
},
|
||||
sharedDatasource
|
||||
);
|
||||
|
||||
const dropTypes = dropProps?.dropTypes;
|
||||
const nextLabel = dropProps?.nextLabel;
|
||||
|
@ -104,6 +112,7 @@ export function DraggableDimensionButton({
|
|||
groupLabel: group.groupLabel,
|
||||
position: accessorIndex + 1,
|
||||
nextLabel: nextLabel || '',
|
||||
layerNumber: layerIndex + 1,
|
||||
},
|
||||
}),
|
||||
[
|
||||
|
@ -118,10 +127,10 @@ export function DraggableDimensionButton({
|
|||
canDuplicate,
|
||||
canSwap,
|
||||
canCombine,
|
||||
layerIndex,
|
||||
]
|
||||
);
|
||||
|
||||
// todo: simplify by id and use drop targets?
|
||||
const reorderableGroup = useMemo(
|
||||
() =>
|
||||
group.accessors.map((g) => ({
|
||||
|
@ -136,7 +145,7 @@ export function DraggableDimensionButton({
|
|||
);
|
||||
|
||||
const handleOnDrop = useCallback(
|
||||
(droppedItem, selectedDropType) => onDrop(droppedItem, value, selectedDropType),
|
||||
(source, selectedDropType) => onDrop(source, value, selectedDropType),
|
||||
[value, onDrop]
|
||||
);
|
||||
return (
|
||||
|
@ -151,7 +160,7 @@ export function DraggableDimensionButton({
|
|||
getAdditionalClassesOnDroppable={getAdditionalClassesOnDroppable}
|
||||
order={[2, layerIndex, groupIndex, accessorIndex]}
|
||||
draggable
|
||||
dragType={isDraggedOperation(dragging) ? 'move' : 'copy'}
|
||||
dragType={isOperation(dragging) ? 'move' : 'copy'}
|
||||
dropTypes={dropTypes}
|
||||
reorderableGroup={reorderableGroup.length > 1 ? reorderableGroup : undefined}
|
||||
value={value}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getDropProps } from './drop_targets_utils';
|
||||
import { createMockDatasource } from '../../../../mocks';
|
||||
|
||||
describe('getDropProps', () => {
|
||||
it('should run datasource getDropProps if exists', () => {
|
||||
const mockDatasource = createMockDatasource('testDatasource');
|
||||
getDropProps(
|
||||
{
|
||||
state: 'datasourceState',
|
||||
target: {
|
||||
columnId: 'col1',
|
||||
groupId: 'x',
|
||||
layerId: 'first',
|
||||
filterOperations: () => true,
|
||||
},
|
||||
source: {
|
||||
columnId: 'col1',
|
||||
groupId: 'x',
|
||||
layerId: 'first',
|
||||
id: 'annotationColumn2',
|
||||
humanData: { label: 'Event' },
|
||||
},
|
||||
},
|
||||
mockDatasource
|
||||
);
|
||||
expect(mockDatasource.getDropProps).toHaveBeenCalled();
|
||||
});
|
||||
describe('no datasource', () => {
|
||||
it('returns reorder for the same group existing columns', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
state: 'datasourceState',
|
||||
target: {
|
||||
columnId: 'annotationColumn',
|
||||
groupId: 'xAnnotations',
|
||||
layerId: 'second',
|
||||
filterOperations: () => true,
|
||||
},
|
||||
source: {
|
||||
columnId: 'annotationColumn2',
|
||||
groupId: 'xAnnotations',
|
||||
layerId: 'second',
|
||||
id: 'annotationColumn2',
|
||||
humanData: { label: 'Event' },
|
||||
},
|
||||
})
|
||||
).toEqual({ dropTypes: ['reorder'] });
|
||||
});
|
||||
it('returns duplicate for the same group existing column and not existing column', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
state: 'datasourceState',
|
||||
target: {
|
||||
columnId: 'annotationColumn',
|
||||
groupId: 'xAnnotations',
|
||||
layerId: 'second',
|
||||
isNewColumn: true,
|
||||
filterOperations: () => true,
|
||||
},
|
||||
source: {
|
||||
columnId: 'annotationColumn2',
|
||||
groupId: 'xAnnotations',
|
||||
layerId: 'second',
|
||||
id: 'annotationColumn2',
|
||||
humanData: { label: 'Event' },
|
||||
},
|
||||
})
|
||||
).toEqual({ dropTypes: ['duplicate_compatible'] });
|
||||
});
|
||||
it('returns replace_duplicate and replace for replacing to different layer', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
state: 'datasourceState',
|
||||
target: {
|
||||
columnId: 'annotationColumn',
|
||||
groupId: 'xAnnotations',
|
||||
layerId: 'first',
|
||||
filterOperations: () => true,
|
||||
},
|
||||
source: {
|
||||
columnId: 'annotationColumn2',
|
||||
groupId: 'xAnnotations',
|
||||
layerId: 'second',
|
||||
id: 'annotationColumn2',
|
||||
humanData: { label: 'Event' },
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['replace_compatible', 'replace_duplicate_compatible', 'swap_compatible'],
|
||||
});
|
||||
});
|
||||
it('returns duplicate and move for replacing to different layer for empty column', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
state: 'datasourceState',
|
||||
target: {
|
||||
columnId: 'annotationColumn',
|
||||
groupId: 'xAnnotations',
|
||||
layerId: 'first',
|
||||
isNewColumn: true,
|
||||
filterOperations: () => true,
|
||||
},
|
||||
source: {
|
||||
columnId: 'annotationColumn2',
|
||||
groupId: 'xAnnotations',
|
||||
layerId: 'second',
|
||||
id: 'annotationColumn2',
|
||||
humanData: { label: 'Event' },
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['move_compatible', 'duplicate_compatible'],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,8 +9,17 @@ import React from 'react';
|
|||
import classNames from 'classnames';
|
||||
import { EuiIcon, EuiFlexItem, EuiFlexGroup, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DraggingIdentifier } from '../../../../drag_drop';
|
||||
import { Datasource, DropType, GetDropProps } from '../../../../types';
|
||||
import { DragDropIdentifier, DraggingIdentifier } from '../../../../drag_drop';
|
||||
import {
|
||||
Datasource,
|
||||
DropType,
|
||||
FramePublicAPI,
|
||||
GetDropPropsArgs,
|
||||
isOperation,
|
||||
Visualization,
|
||||
DragDropOperation,
|
||||
VisualizationDimensionGroupConfig,
|
||||
} from '../../../../types';
|
||||
|
||||
function getPropsForDropType(type: 'swap' | 'duplicate' | 'combine') {
|
||||
switch (type) {
|
||||
|
@ -131,35 +140,97 @@ export const getAdditionalClassesOnDroppable = (dropType?: string) => {
|
|||
}
|
||||
};
|
||||
|
||||
const isOperationFromTheSameGroup = (
|
||||
op1?: DraggingIdentifier,
|
||||
op2?: { layerId: string; groupId: string; columnId: string }
|
||||
) => {
|
||||
const isOperationFromCompatibleGroup = (op1?: DraggingIdentifier, op2?: DragDropOperation) => {
|
||||
return (
|
||||
op1 &&
|
||||
op2 &&
|
||||
'columnId' in op1 &&
|
||||
isOperation(op1) &&
|
||||
isOperation(op2) &&
|
||||
op1.columnId !== op2.columnId &&
|
||||
op1.groupId === op2.groupId &&
|
||||
op1.layerId !== op2.layerId
|
||||
);
|
||||
};
|
||||
|
||||
export const isOperationFromTheSameGroup = (op1?: DraggingIdentifier, op2?: DragDropOperation) => {
|
||||
return (
|
||||
isOperation(op1) &&
|
||||
isOperation(op2) &&
|
||||
op1.columnId !== op2.columnId &&
|
||||
'groupId' in op1 &&
|
||||
op1.groupId === op2.groupId &&
|
||||
'layerId' in op1 &&
|
||||
op1.layerId === op2.layerId
|
||||
);
|
||||
};
|
||||
|
||||
export function getDropPropsForSameGroup(
|
||||
isNewColumn?: boolean
|
||||
): { dropTypes: DropType[]; nextLabel?: string } | undefined {
|
||||
return !isNewColumn ? { dropTypes: ['reorder'] } : { dropTypes: ['duplicate_compatible'] };
|
||||
}
|
||||
|
||||
export const getDropProps = (
|
||||
layerDatasource: Datasource<unknown, unknown>,
|
||||
dropProps: GetDropProps,
|
||||
isNew?: boolean
|
||||
dropProps: GetDropPropsArgs,
|
||||
sharedDatasource?: Datasource<unknown, unknown>
|
||||
): { dropTypes: DropType[]; nextLabel?: string } | undefined => {
|
||||
if (layerDatasource) {
|
||||
return layerDatasource.getDropProps(dropProps);
|
||||
if (sharedDatasource) {
|
||||
return sharedDatasource?.getDropProps(dropProps);
|
||||
} else {
|
||||
// TODO: refactor & test this - it's too annotations specific
|
||||
// TODO: allow moving operations between layers for annotations
|
||||
if (isOperationFromTheSameGroup(dropProps.dragging, dropProps)) {
|
||||
return { dropTypes: [isNew ? 'duplicate_compatible' : 'reorder'], nextLabel: '' };
|
||||
if (isOperationFromTheSameGroup(dropProps.source, dropProps.target)) {
|
||||
return getDropPropsForSameGroup(dropProps.target.isNewColumn);
|
||||
}
|
||||
if (isOperationFromCompatibleGroup(dropProps.source, dropProps.target)) {
|
||||
return {
|
||||
dropTypes: dropProps.target.isNewColumn
|
||||
? ['move_compatible', 'duplicate_compatible']
|
||||
: ['replace_compatible', 'replace_duplicate_compatible', 'swap_compatible'],
|
||||
};
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
export interface OnVisDropProps<T> {
|
||||
prevState: T;
|
||||
target: DragDropOperation;
|
||||
source: DragDropIdentifier;
|
||||
frame: FramePublicAPI;
|
||||
dropType: DropType;
|
||||
group?: VisualizationDimensionGroupConfig;
|
||||
}
|
||||
|
||||
export function onDropForVisualization<T>(
|
||||
props: OnVisDropProps<T>,
|
||||
activeVisualization: Visualization<T>
|
||||
) {
|
||||
const { prevState, target, frame, dropType, source, group } = props;
|
||||
const { layerId, columnId, groupId } = target;
|
||||
|
||||
const previousColumn =
|
||||
isOperation(source) && group?.requiresPreviousColumnOnDuplicate ? source.columnId : undefined;
|
||||
|
||||
const newVisState = activeVisualization.setDimension({
|
||||
columnId,
|
||||
groupId,
|
||||
layerId,
|
||||
prevState,
|
||||
previousColumn,
|
||||
frame,
|
||||
});
|
||||
|
||||
// remove source
|
||||
if (
|
||||
isOperation(source) &&
|
||||
(dropType === 'move_compatible' ||
|
||||
dropType === 'move_incompatible' ||
|
||||
dropType === 'combine_incompatible' ||
|
||||
dropType === 'combine_compatible' ||
|
||||
dropType === 'replace_compatible' ||
|
||||
dropType === 'replace_incompatible')
|
||||
) {
|
||||
return activeVisualization.removeDimension({
|
||||
columnId: source?.columnId,
|
||||
layerId: source?.layerId,
|
||||
prevState: newVisState,
|
||||
frame,
|
||||
});
|
||||
}
|
||||
return newVisState;
|
||||
}
|
||||
|
|
|
@ -12,8 +12,13 @@ import { i18n } from '@kbn/i18n';
|
|||
import { generateId } from '../../../../id_generator';
|
||||
import { DragDrop, DragDropIdentifier, DragContext } from '../../../../drag_drop';
|
||||
|
||||
import { Datasource, VisualizationDimensionGroupConfig, DropType } from '../../../../types';
|
||||
import { LayerDatasourceDropProps } from '../types';
|
||||
import {
|
||||
Datasource,
|
||||
VisualizationDimensionGroupConfig,
|
||||
DropType,
|
||||
DatasourceLayers,
|
||||
isOperation,
|
||||
} from '../../../../types';
|
||||
import {
|
||||
getCustomDropTarget,
|
||||
getAdditionalClassesOnDroppable,
|
||||
|
@ -98,31 +103,31 @@ const SuggestedValueButton = ({ columnId, group, onClick }: EmptyButtonProps) =>
|
|||
|
||||
export function EmptyDimensionButton({
|
||||
group,
|
||||
groups,
|
||||
layerDatasource,
|
||||
layerDatasourceDropProps,
|
||||
state,
|
||||
layerId,
|
||||
groupIndex,
|
||||
layerIndex,
|
||||
onClick,
|
||||
onDrop,
|
||||
datasourceLayers,
|
||||
}: {
|
||||
layerId: string;
|
||||
groupIndex: number;
|
||||
layerIndex: number;
|
||||
onDrop: (
|
||||
droppedItem: DragDropIdentifier,
|
||||
dropTarget: DragDropIdentifier,
|
||||
dropType?: DropType
|
||||
) => void;
|
||||
onDrop: (source: DragDropIdentifier, dropTarget: DragDropIdentifier, dropType?: DropType) => void;
|
||||
onClick: (id: string) => void;
|
||||
group: VisualizationDimensionGroupConfig;
|
||||
groups: VisualizationDimensionGroupConfig[];
|
||||
|
||||
layerDatasource: Datasource<unknown, unknown>;
|
||||
layerDatasourceDropProps: LayerDatasourceDropProps;
|
||||
datasourceLayers: DatasourceLayers;
|
||||
state: unknown;
|
||||
}) {
|
||||
const { dragging } = useContext(DragContext);
|
||||
const sharedDatasource =
|
||||
!isOperation(dragging) ||
|
||||
datasourceLayers?.[dragging.layerId]?.datasourceId === datasourceLayers?.[layerId]?.datasourceId
|
||||
? layerDatasource
|
||||
: undefined;
|
||||
|
||||
const itemIndex = group.accessors.length;
|
||||
|
||||
|
@ -132,16 +137,19 @@ export function EmptyDimensionButton({
|
|||
}, [itemIndex]);
|
||||
|
||||
const dropProps = getDropProps(
|
||||
layerDatasource,
|
||||
{
|
||||
...(layerDatasourceDropProps || {}),
|
||||
dragging,
|
||||
columnId: newColumnId,
|
||||
filterOperations: group.filterOperations,
|
||||
groupId: group.groupId,
|
||||
dimensionGroups: groups,
|
||||
state,
|
||||
source: dragging,
|
||||
target: {
|
||||
layerId,
|
||||
columnId: newColumnId,
|
||||
groupId: group.groupId,
|
||||
filterOperations: group.filterOperations,
|
||||
prioritizedOperation: group.prioritizedOperation,
|
||||
isNewColumn: true,
|
||||
},
|
||||
},
|
||||
true
|
||||
sharedDatasource
|
||||
);
|
||||
|
||||
const dropTypes = dropProps?.dropTypes;
|
||||
|
@ -157,6 +165,7 @@ export function EmptyDimensionButton({
|
|||
columnId: newColumnId,
|
||||
groupId: group.groupId,
|
||||
layerId,
|
||||
filterOperations: group.filterOperations,
|
||||
id: newColumnId,
|
||||
humanData: {
|
||||
label,
|
||||
|
@ -164,13 +173,24 @@ export function EmptyDimensionButton({
|
|||
position: itemIndex + 1,
|
||||
nextLabel: nextLabel || '',
|
||||
canDuplicate,
|
||||
layerNumber: layerIndex + 1,
|
||||
},
|
||||
}),
|
||||
[newColumnId, group.groupId, layerId, group.groupLabel, itemIndex, nextLabel, canDuplicate]
|
||||
[
|
||||
newColumnId,
|
||||
group.groupId,
|
||||
layerId,
|
||||
group.groupLabel,
|
||||
group.filterOperations,
|
||||
itemIndex,
|
||||
nextLabel,
|
||||
canDuplicate,
|
||||
layerIndex,
|
||||
]
|
||||
);
|
||||
|
||||
const handleOnDrop = React.useCallback(
|
||||
(droppedItem, selectedDropType) => onDrop(droppedItem, value, selectedDropType),
|
||||
(source, selectedDropType) => onDrop(source, value, selectedDropType),
|
||||
[value, onDrop]
|
||||
);
|
||||
|
||||
|
|
|
@ -631,7 +631,7 @@ describe('LayerPanel', () => {
|
|||
|
||||
expect(mockDatasource.getDropProps).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
dragging: draggingField,
|
||||
source: draggingField,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -644,7 +644,7 @@ describe('LayerPanel', () => {
|
|||
|
||||
expect(mockDatasource.onDrop).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
droppedItem: draggingField,
|
||||
source: draggingField,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -663,8 +663,8 @@ describe('LayerPanel', () => {
|
|||
],
|
||||
});
|
||||
|
||||
mockDatasource.getDropProps.mockImplementation(({ columnId }) =>
|
||||
columnId !== 'a' ? { dropTypes: ['field_replace'], nextLabel: '' } : undefined
|
||||
mockDatasource.getDropProps.mockImplementation(({ target }) =>
|
||||
target.columnId !== 'a' ? { dropTypes: ['field_replace'], nextLabel: '' } : undefined
|
||||
);
|
||||
|
||||
const { instance } = await mountWithProvider(
|
||||
|
@ -674,7 +674,9 @@ describe('LayerPanel', () => {
|
|||
);
|
||||
|
||||
expect(mockDatasource.getDropProps).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ columnId: 'a' })
|
||||
expect.objectContaining({
|
||||
target: expect.objectContaining({ columnId: 'a', groupId: 'a', layerId: 'first' }),
|
||||
})
|
||||
);
|
||||
|
||||
expect(
|
||||
|
@ -741,7 +743,7 @@ describe('LayerPanel', () => {
|
|||
|
||||
expect(mockDatasource.getDropProps).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
dragging: draggingOperation,
|
||||
source: draggingOperation,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -755,8 +757,8 @@ describe('LayerPanel', () => {
|
|||
|
||||
expect(mockDatasource.onDrop).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
columnId: 'b',
|
||||
droppedItem: draggingOperation,
|
||||
target: expect.objectContaining({ columnId: 'b' }),
|
||||
source: draggingOperation,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -771,8 +773,8 @@ describe('LayerPanel', () => {
|
|||
|
||||
expect(mockDatasource.onDrop).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
columnId: 'newid',
|
||||
droppedItem: draggingOperation,
|
||||
target: expect.objectContaining({ columnId: 'newid' }),
|
||||
source: draggingOperation,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -816,7 +818,7 @@ describe('LayerPanel', () => {
|
|||
expect(mockDatasource.onDrop).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
dropType: 'reorder',
|
||||
droppedItem: draggingOperation,
|
||||
source: draggingOperation,
|
||||
})
|
||||
);
|
||||
const secondButton = instance
|
||||
|
@ -865,9 +867,9 @@ describe('LayerPanel', () => {
|
|||
});
|
||||
expect(mockDatasource.onDrop).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
columnId: 'newid',
|
||||
target: expect.objectContaining({ columnId: 'newid' }),
|
||||
dropType: 'duplicate_compatible',
|
||||
droppedItem: draggingOperation,
|
||||
source: draggingOperation,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -907,7 +909,7 @@ describe('LayerPanel', () => {
|
|||
humanData: { label: 'Label' },
|
||||
};
|
||||
|
||||
mockDatasource.onDrop.mockReturnValue({ deleted: 'a' });
|
||||
mockDatasource.onDrop.mockReturnValue(true);
|
||||
const updateVisualization = jest.fn();
|
||||
|
||||
const { instance } = await mountWithProvider(
|
||||
|
@ -925,9 +927,10 @@ describe('LayerPanel', () => {
|
|||
expect(mockDatasource.onDrop).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
dropType: 'replace_compatible',
|
||||
droppedItem: draggingOperation,
|
||||
source: draggingOperation,
|
||||
})
|
||||
);
|
||||
// testing default onDropForVisualization path
|
||||
expect(mockVis.setDimension).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
columnId: 'c',
|
||||
|
@ -945,6 +948,85 @@ describe('LayerPanel', () => {
|
|||
);
|
||||
expect(updateVisualization).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('should call onDrop and update visualization when replacing between compatible groups2', async () => {
|
||||
const mockVis = {
|
||||
...mockVisualization,
|
||||
removeDimension: jest.fn(),
|
||||
setDimension: jest.fn(() => 'modifiedState'),
|
||||
onDrop: jest.fn(() => 'modifiedState'),
|
||||
};
|
||||
jest.spyOn(mockVis.onDrop, 'bind').mockImplementation((thisVal, ...args) => mockVis.onDrop);
|
||||
|
||||
mockVis.getConfiguration.mockReturnValue({
|
||||
groups: [
|
||||
{
|
||||
groupLabel: 'A',
|
||||
groupId: 'a',
|
||||
accessors: [{ columnId: 'a' }, { columnId: 'b' }],
|
||||
filterOperations: () => true,
|
||||
supportsMoreColumns: true,
|
||||
dataTestSubj: 'lnsGroup',
|
||||
},
|
||||
{
|
||||
groupLabel: 'B',
|
||||
groupId: 'b',
|
||||
accessors: [{ columnId: 'c' }],
|
||||
filterOperations: () => true,
|
||||
supportsMoreColumns: true,
|
||||
dataTestSubj: 'lnsGroup2',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const draggingOperation = {
|
||||
layerId: 'first',
|
||||
columnId: 'a',
|
||||
groupId: 'a',
|
||||
id: 'a',
|
||||
humanData: { label: 'Label' },
|
||||
};
|
||||
|
||||
mockDatasource.onDrop.mockReturnValue(true);
|
||||
const updateVisualization = jest.fn();
|
||||
|
||||
const { instance } = await mountWithProvider(
|
||||
<ChildDragDropProvider {...defaultContext} dragging={draggingOperation}>
|
||||
<LayerPanel
|
||||
{...getDefaultProps()}
|
||||
updateVisualization={updateVisualization}
|
||||
activeVisualization={mockVis}
|
||||
/>
|
||||
</ChildDragDropProvider>
|
||||
);
|
||||
act(() => {
|
||||
instance.find(DragDrop).at(3).prop('onDrop')!(draggingOperation, 'replace_compatible');
|
||||
});
|
||||
|
||||
expect(mockDatasource.onDrop).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
dropType: 'replace_compatible',
|
||||
source: draggingOperation,
|
||||
})
|
||||
);
|
||||
|
||||
expect(mockVis.onDrop).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
dropType: 'replace_compatible',
|
||||
prevState: 'state',
|
||||
source: draggingOperation,
|
||||
target: expect.objectContaining({
|
||||
columnId: 'c',
|
||||
groupId: 'b',
|
||||
id: 'c',
|
||||
layerId: 'first',
|
||||
}),
|
||||
}),
|
||||
mockVis
|
||||
);
|
||||
expect(mockVis.setDimension).not.toHaveBeenCalled();
|
||||
expect(mockVis.removeDimension).not.toHaveBeenCalled();
|
||||
expect(updateVisualization).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('add a new dimension', () => {
|
||||
|
|
|
@ -19,7 +19,13 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { NativeRenderer } from '../../../native_renderer';
|
||||
import { StateSetter, Visualization, DraggedOperation, DropType } from '../../../types';
|
||||
import {
|
||||
StateSetter,
|
||||
Visualization,
|
||||
DragDropOperation,
|
||||
DropType,
|
||||
isOperation,
|
||||
} from '../../../types';
|
||||
import { DragDropIdentifier, ReorderProvider } from '../../../drag_drop';
|
||||
import { LayerSettings } from './layer_settings';
|
||||
import { trackUiEvent } from '../../../lens_ui_telemetry';
|
||||
|
@ -36,6 +42,7 @@ import {
|
|||
selectResolvedDateRange,
|
||||
selectDatasourceStates,
|
||||
} from '../../../state_management';
|
||||
import { onDropForVisualization } from './buttons/drop_targets_utils';
|
||||
|
||||
const initialActiveDimensionState = {
|
||||
isNew: false,
|
||||
|
@ -109,19 +116,12 @@ export function LayerPanel(
|
|||
const layerDatasourceState = datasourceStates?.[datasourceId]?.state;
|
||||
const layerDatasource = props.datasourceMap[datasourceId];
|
||||
|
||||
const layerDatasourceDropProps = useMemo(
|
||||
() => ({
|
||||
layerId,
|
||||
state: layerDatasourceState,
|
||||
setState: (newState: unknown) => {
|
||||
updateDatasource(datasourceId, newState);
|
||||
},
|
||||
}),
|
||||
[layerId, layerDatasourceState, datasourceId, updateDatasource]
|
||||
);
|
||||
|
||||
const layerDatasourceConfigProps = {
|
||||
...layerDatasourceDropProps,
|
||||
state: layerDatasourceState,
|
||||
setState: (newState: unknown) => {
|
||||
updateDatasource(datasourceId, newState);
|
||||
},
|
||||
layerId,
|
||||
frame: props.framePublicAPI,
|
||||
dateRange,
|
||||
};
|
||||
|
@ -155,105 +155,70 @@ export function LayerPanel(
|
|||
registerNewRef: registerNewButtonRef,
|
||||
} = useFocusUpdate(allAccessors);
|
||||
|
||||
const layerDatasourceOnDrop = layerDatasource?.onDrop;
|
||||
|
||||
const onDrop = useMemo(() => {
|
||||
return (
|
||||
droppedItem: DragDropIdentifier,
|
||||
targetItem: DragDropIdentifier,
|
||||
dropType?: DropType
|
||||
) => {
|
||||
return (source: DragDropIdentifier, target: DragDropIdentifier, dropType?: DropType) => {
|
||||
if (!dropType) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
columnId,
|
||||
groupId,
|
||||
layerId: targetLayerId,
|
||||
} = targetItem as unknown as DraggedOperation;
|
||||
if (dropType === 'reorder' || dropType === 'field_replace' || dropType === 'field_add') {
|
||||
setNextFocusedButtonId(droppedItem.id);
|
||||
} else {
|
||||
setNextFocusedButtonId(columnId);
|
||||
if (!isOperation(target)) {
|
||||
throw new Error('Drop target should be an operation');
|
||||
}
|
||||
|
||||
if (layerDatasource) {
|
||||
const group = groups.find(({ groupId: gId }) => gId === groupId);
|
||||
const filterOperations = group?.filterOperations || (() => false);
|
||||
const dropResult = layerDatasourceOnDrop({
|
||||
...layerDatasourceDropProps,
|
||||
droppedItem,
|
||||
columnId,
|
||||
layerId: targetLayerId,
|
||||
filterOperations,
|
||||
dimensionGroups: groups,
|
||||
groupId,
|
||||
dropType,
|
||||
});
|
||||
if (dropResult) {
|
||||
let previousColumn =
|
||||
typeof droppedItem.column === 'string' ? droppedItem.column : undefined;
|
||||
|
||||
// make it inherit only for moving and duplicate
|
||||
if (!previousColumn) {
|
||||
// when duplicating check if the previous column is required
|
||||
if (
|
||||
dropType === 'duplicate_compatible' &&
|
||||
typeof droppedItem.columnId === 'string' &&
|
||||
group?.requiresPreviousColumnOnDuplicate
|
||||
) {
|
||||
previousColumn = droppedItem.columnId;
|
||||
} else {
|
||||
previousColumn = typeof dropResult === 'object' ? dropResult.deleted : undefined;
|
||||
}
|
||||
}
|
||||
const newVisState = activeVisualization.setDimension({
|
||||
columnId,
|
||||
groupId,
|
||||
layerId: targetLayerId,
|
||||
prevState: props.visualizationState,
|
||||
previousColumn,
|
||||
frame: framePublicAPI,
|
||||
});
|
||||
|
||||
if (typeof dropResult === 'object') {
|
||||
// When a column is moved, we delete the reference to the old
|
||||
updateVisualization(
|
||||
activeVisualization.removeDimension({
|
||||
columnId: dropResult.deleted,
|
||||
layerId: targetLayerId,
|
||||
prevState: newVisState,
|
||||
frame: framePublicAPI,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
updateVisualization(newVisState);
|
||||
}
|
||||
}
|
||||
if (dropType === 'reorder' || dropType === 'field_replace' || dropType === 'field_add') {
|
||||
setNextFocusedButtonId(source.id);
|
||||
} else {
|
||||
if (dropType === 'duplicate_compatible' || dropType === 'reorder') {
|
||||
const newVisState = activeVisualization.setDimension({
|
||||
columnId,
|
||||
groupId,
|
||||
layerId: targetLayerId,
|
||||
prevState: props.visualizationState,
|
||||
previousColumn: droppedItem.id,
|
||||
frame: framePublicAPI,
|
||||
});
|
||||
updateVisualization(newVisState);
|
||||
}
|
||||
setNextFocusedButtonId(target.columnId);
|
||||
}
|
||||
|
||||
let hasDropSucceeded = true;
|
||||
if (layerDatasource) {
|
||||
hasDropSucceeded = Boolean(
|
||||
layerDatasource?.onDrop({
|
||||
state: layerDatasourceState,
|
||||
setState: (newState: unknown) => {
|
||||
updateDatasource(datasourceId, newState);
|
||||
},
|
||||
source,
|
||||
target: {
|
||||
...(target as unknown as DragDropOperation),
|
||||
filterOperations:
|
||||
groups.find(({ groupId: gId }) => gId === target.groupId)?.filterOperations ||
|
||||
Boolean,
|
||||
},
|
||||
dimensionGroups: groups,
|
||||
dropType,
|
||||
})
|
||||
);
|
||||
}
|
||||
if (hasDropSucceeded) {
|
||||
activeVisualization.onDrop = activeVisualization.onDrop?.bind(activeVisualization);
|
||||
|
||||
updateVisualization(
|
||||
(activeVisualization.onDrop || onDropForVisualization)?.(
|
||||
{
|
||||
prevState: props.visualizationState,
|
||||
frame: framePublicAPI,
|
||||
target,
|
||||
source,
|
||||
dropType,
|
||||
group: groups.find(({ groupId: gId }) => gId === target.groupId),
|
||||
},
|
||||
activeVisualization
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
}, [
|
||||
layerDatasource,
|
||||
layerDatasourceState,
|
||||
setNextFocusedButtonId,
|
||||
groups,
|
||||
layerDatasourceOnDrop,
|
||||
layerDatasourceDropProps,
|
||||
activeVisualization,
|
||||
props.visualizationState,
|
||||
framePublicAPI,
|
||||
updateVisualization,
|
||||
datasourceId,
|
||||
updateDatasource,
|
||||
]);
|
||||
|
||||
const isDimensionPanelOpen = Boolean(activeId);
|
||||
|
@ -462,15 +427,15 @@ export function LayerPanel(
|
|||
return (
|
||||
<DraggableDimensionButton
|
||||
registerNewButtonRef={registerNewButtonRef}
|
||||
accessorIndex={accessorIndex}
|
||||
columnId={columnId}
|
||||
group={group}
|
||||
groups={groups}
|
||||
accessorIndex={accessorIndex}
|
||||
groupIndex={groupIndex}
|
||||
key={columnId}
|
||||
layerDatasourceDropProps={layerDatasourceDropProps}
|
||||
state={layerDatasourceState}
|
||||
label={columnLabelMap?.[columnId]}
|
||||
layerDatasource={layerDatasource}
|
||||
datasourceLayers={framePublicAPI.datasourceLayers}
|
||||
layerIndex={layerIndex}
|
||||
layerId={layerId}
|
||||
onDragStart={() => setHideTooltip(true)}
|
||||
|
@ -562,12 +527,12 @@ export function LayerPanel(
|
|||
{group.supportsMoreColumns ? (
|
||||
<EmptyDimensionButton
|
||||
group={group}
|
||||
groupIndex={groupIndex}
|
||||
groups={groups}
|
||||
layerId={layerId}
|
||||
groupIndex={groupIndex}
|
||||
layerIndex={layerIndex}
|
||||
layerDatasource={layerDatasource}
|
||||
layerDatasourceDropProps={layerDatasourceDropProps}
|
||||
state={layerDatasourceState}
|
||||
datasourceLayers={framePublicAPI.datasourceLayers}
|
||||
onClick={(id) => {
|
||||
props.onEmptyDimensionAdd(id, group);
|
||||
setActiveDimension({
|
||||
|
|
|
@ -29,7 +29,6 @@ export interface LayerPanelProps {
|
|||
}
|
||||
|
||||
export interface LayerDatasourceDropProps {
|
||||
layerId: string;
|
||||
state: unknown;
|
||||
setState: (newState: unknown) => void;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,767 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DragDropOperation, OperationMetadata } from '../../../types';
|
||||
import { TermsIndexPatternColumn } from '../../operations';
|
||||
import { getDropProps } from './get_drop_props';
|
||||
import {
|
||||
mockDataViews,
|
||||
mockedLayers,
|
||||
mockedDraggedField,
|
||||
mockedDndOperations,
|
||||
mockedColumns,
|
||||
} from './mocks';
|
||||
import { generateId } from '../../../id_generator';
|
||||
|
||||
const getDefaultProps = () => ({
|
||||
state: {
|
||||
indexPatternRefs: [],
|
||||
indexPatterns: mockDataViews(),
|
||||
currentIndexPatternId: 'first',
|
||||
isFirstExistenceFetch: false,
|
||||
existingFields: {
|
||||
first: {
|
||||
timestamp: true,
|
||||
bytes: true,
|
||||
memory: true,
|
||||
source: true,
|
||||
},
|
||||
},
|
||||
layers: { first: mockedLayers.doubleColumnLayer(), second: mockedLayers.emptyLayer() },
|
||||
},
|
||||
target: mockedDndOperations.notFiltering,
|
||||
source: mockedDndOperations.bucket,
|
||||
});
|
||||
|
||||
describe('IndexPatternDimensionEditorPanel#getDropProps', () => {
|
||||
describe('not dragging', () => {
|
||||
it('returns undefined if no drag is happening', () => {
|
||||
expect(getDropProps({ ...getDefaultProps(), source: undefined })).toBe(undefined);
|
||||
});
|
||||
|
||||
it('returns undefined if the dragged item has no field', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
...getDefaultProps(),
|
||||
source: { name: 'bar', id: 'bar', humanData: { label: 'Label' } },
|
||||
})
|
||||
).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dragging a field', () => {
|
||||
it('returns undefined if field is not supported by filterOperations', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
...getDefaultProps(),
|
||||
source: mockedDraggedField,
|
||||
target: mockedDndOperations.staticValue,
|
||||
})
|
||||
).toBe(undefined);
|
||||
});
|
||||
|
||||
it('returns field_replace if the field is supported by filterOperations and the dropTarget is an existing column', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
...getDefaultProps(),
|
||||
target: mockedDndOperations.numericalOnly,
|
||||
source: mockedDraggedField,
|
||||
})
|
||||
).toEqual({ dropTypes: ['field_replace'], nextLabel: 'Intervals' });
|
||||
});
|
||||
|
||||
it('returns field_add if the field is supported by filterOperations and the dropTarget is an empty column', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
...getDefaultProps(),
|
||||
target: {
|
||||
...mockedDndOperations.numericalOnly,
|
||||
columnId: 'newId',
|
||||
},
|
||||
source: mockedDraggedField,
|
||||
})
|
||||
).toEqual({ dropTypes: ['field_add'], nextLabel: 'Intervals' });
|
||||
});
|
||||
|
||||
it('returns undefined if the field belongs to another data view', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
...getDefaultProps(),
|
||||
source: {
|
||||
...mockedDraggedField,
|
||||
indexPatternId: 'first2',
|
||||
},
|
||||
})
|
||||
).toBe(undefined);
|
||||
});
|
||||
|
||||
it('returns undefined if the dragged field is already in use by this operation', () => {
|
||||
expect(
|
||||
getDropProps({
|
||||
...getDefaultProps(),
|
||||
source: {
|
||||
...mockedDraggedField,
|
||||
field: {
|
||||
name: 'timestamp',
|
||||
displayName: 'timestampLabel',
|
||||
type: 'date',
|
||||
aggregatable: true,
|
||||
searchable: true,
|
||||
exists: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
).toBe(undefined);
|
||||
});
|
||||
|
||||
it('returns also field_combine if the field is supported by filterOperations and the dropTarget is an existing column that supports multiple fields', () => {
|
||||
// replace the state with a top values column to enable the multi fields behaviour
|
||||
const props = getDefaultProps();
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: mockedDraggedField,
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'col2',
|
||||
filterOperations: (op: OperationMetadata) => op.dataType !== 'date',
|
||||
},
|
||||
})
|
||||
).toEqual({ dropTypes: ['field_replace', 'field_combine'] });
|
||||
});
|
||||
});
|
||||
|
||||
describe('dragging a column', () => {
|
||||
it('allows replacing and replace-duplicating when two columns from compatible groups use the same field', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns.col2 = mockedColumns.dateHistogramCopy;
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'col2',
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
groupId: 'c',
|
||||
},
|
||||
})
|
||||
).toEqual({ dropTypes: ['replace_compatible', 'replace_duplicate_compatible'] });
|
||||
});
|
||||
|
||||
it('returns correct dropTypes if the dragged column from different group uses the same fields as the dropTarget', () => {
|
||||
const props = getDefaultProps();
|
||||
const sourceMultiFieldColumn = {
|
||||
...props.state.layers.first.columns.col1,
|
||||
sourceField: 'bytes',
|
||||
params: {
|
||||
...(props.state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
|
||||
secondaryFields: ['dest'],
|
||||
},
|
||||
} as TermsIndexPatternColumn;
|
||||
// invert the fields
|
||||
const targetMultiFieldColumn = {
|
||||
...props.state.layers.first.columns.col1,
|
||||
sourceField: 'dest',
|
||||
params: {
|
||||
...(props.state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
|
||||
secondaryFields: ['bytes'],
|
||||
},
|
||||
} as TermsIndexPatternColumn;
|
||||
props.state.layers.first.columns = {
|
||||
col1: sourceMultiFieldColumn,
|
||||
col2: targetMultiFieldColumn,
|
||||
};
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'col2',
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
groupId: 'c',
|
||||
},
|
||||
})
|
||||
).toEqual({ dropTypes: ['replace_compatible', 'replace_duplicate_compatible'] });
|
||||
});
|
||||
|
||||
it('returns duplicate and replace if the dragged column from different group uses the same field as the dropTarget, but this last one is multifield, and can be swappable', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns.col2 = {
|
||||
...props.state.layers.first.columns.col1,
|
||||
sourceField: 'bytes',
|
||||
params: {
|
||||
...(props.state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
|
||||
secondaryFields: ['dest'],
|
||||
},
|
||||
} as TermsIndexPatternColumn;
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'col2',
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
groupId: 'c',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['replace_compatible', 'replace_duplicate_compatible'],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns swap, duplicate and replace if the dragged column from different group uses the same field as the dropTarget, but this last one is multifield', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns.col2 = {
|
||||
...props.state.layers.first.columns.col1,
|
||||
sourceField: 'bytes',
|
||||
params: {
|
||||
...(props.state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
|
||||
secondaryFields: ['dest'],
|
||||
},
|
||||
} as TermsIndexPatternColumn;
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
...props,
|
||||
// make it swappable
|
||||
target: {
|
||||
...props.target,
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed,
|
||||
groupId: 'a',
|
||||
columnId: 'col2',
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed,
|
||||
groupId: 'c',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['replace_compatible', 'replace_duplicate_compatible', 'swap_compatible'],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns reorder if drop target and source columns are from the same group and both are existing', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns.col2 = mockedColumns.sum;
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: { ...mockedDndOperations.metric, groupId: 'a' },
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'col2',
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed === false,
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['reorder'],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns duplicate_compatible if drop target and source columns are from the same group and drop target id is a new column', () => {
|
||||
const props = getDefaultProps();
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
groupId: 'a',
|
||||
columnId: 'newId',
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
groupId: 'a',
|
||||
},
|
||||
})
|
||||
).toEqual({ dropTypes: ['duplicate_compatible'] });
|
||||
});
|
||||
|
||||
it('returns compatible drop types if the dragged column is compatible', () => {
|
||||
const props = getDefaultProps();
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
groupId: 'a',
|
||||
columnId: 'col3',
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
groupId: 'c',
|
||||
},
|
||||
})
|
||||
).toEqual({ dropTypes: ['move_compatible', 'duplicate_compatible'] });
|
||||
});
|
||||
|
||||
it('returns incompatible drop target types if dropping column to existing incompatible column', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns = {
|
||||
col1: mockedColumns.dateHistogram,
|
||||
col2: mockedColumns.sum,
|
||||
};
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'col2',
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed === false,
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
groupId: 'c',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['replace_incompatible', 'replace_duplicate_incompatible', 'swap_incompatible'],
|
||||
nextLabel: 'Minimum',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not return swap_incompatible if current dropTarget column cannot be swapped to the group of dragging column', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns = {
|
||||
col1: mockedColumns.dateHistogram,
|
||||
col2: mockedColumns.count,
|
||||
};
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'col2',
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed === false,
|
||||
},
|
||||
source: {
|
||||
columnId: 'col1',
|
||||
groupId: 'b',
|
||||
layerId: 'first',
|
||||
id: 'col1',
|
||||
humanData: { label: 'Label' },
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed === true,
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['replace_incompatible', 'replace_duplicate_incompatible'],
|
||||
nextLabel: 'Minimum',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns combine_compatible drop type if the dragged column is compatible and the target one support multiple fields', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns = {
|
||||
col1: mockedColumns.terms,
|
||||
col2: {
|
||||
...mockedColumns.terms,
|
||||
sourceField: 'bytes',
|
||||
},
|
||||
};
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'col2',
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
groupId: 'c',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['replace_compatible', 'replace_duplicate_compatible', 'combine_compatible'],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns no combine_compatible drop type if the target column uses rarity ordering', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns = {
|
||||
col1: mockedColumns.terms,
|
||||
col2: {
|
||||
...mockedColumns.terms,
|
||||
sourceField: 'bytes',
|
||||
params: {
|
||||
...(props.state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
|
||||
orderBy: { type: 'rare' },
|
||||
},
|
||||
} as TermsIndexPatternColumn,
|
||||
};
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
groupId: 'a',
|
||||
columnId: 'col2',
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
groupId: 'c',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['replace_compatible', 'replace_duplicate_compatible'],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns no combine drop type if the dragged column is compatible, the target one supports multiple fields but there are too many fields', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns.col2 = {
|
||||
...props.state.layers.first.columns.col1,
|
||||
sourceField: 'source',
|
||||
params: {
|
||||
...(props.state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
|
||||
secondaryFields: ['memory', 'bytes', 'geo.src'], // too many fields here
|
||||
},
|
||||
} as TermsIndexPatternColumn;
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
groupId: 'a',
|
||||
columnId: 'col2',
|
||||
},
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
groupId: 'c',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['replace_compatible', 'replace_duplicate_compatible'],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns combine_incompatible drop target types if dropping column to existing incompatible column which supports multiple fields', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.first.columns = {
|
||||
col1: mockedColumns.terms,
|
||||
col2: mockedColumns.sum,
|
||||
};
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
target: {
|
||||
...props.target,
|
||||
groupId: 'a',
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed,
|
||||
},
|
||||
// drag the sum over the top values
|
||||
source: {
|
||||
...mockedDndOperations.bucket,
|
||||
groupId: 'c',
|
||||
filterOperation: undefined,
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: [
|
||||
'replace_incompatible',
|
||||
'replace_duplicate_incompatible',
|
||||
'swap_incompatible',
|
||||
'combine_incompatible',
|
||||
],
|
||||
nextLabel: 'Top values',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDropProps between layers', () => {
|
||||
it('allows dropping to the same group', () => {
|
||||
const props = getDefaultProps();
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
columnId: 'col1',
|
||||
layerId: 'first',
|
||||
groupId: 'c',
|
||||
},
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'newId',
|
||||
groupId: 'c',
|
||||
layerId: 'second',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['move_compatible', 'duplicate_compatible'],
|
||||
});
|
||||
});
|
||||
it('allows dropping to compatible groups', () => {
|
||||
const props = getDefaultProps();
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
columnId: 'col1',
|
||||
layerId: 'first',
|
||||
groupId: 'a',
|
||||
},
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'newId',
|
||||
groupId: 'c',
|
||||
layerId: 'second',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
dropTypes: ['move_compatible', 'duplicate_compatible'],
|
||||
});
|
||||
});
|
||||
it('allows incompatible drop', () => {
|
||||
const props = getDefaultProps();
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
columnId: 'col1',
|
||||
layerId: 'first',
|
||||
groupId: 'c',
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed,
|
||||
},
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'newId',
|
||||
groupId: 'c',
|
||||
layerId: 'second',
|
||||
filterOperations: (op: OperationMetadata) => !op.isBucketed,
|
||||
},
|
||||
})?.dropTypes
|
||||
).toEqual(['move_incompatible', 'duplicate_incompatible']);
|
||||
});
|
||||
it('allows dropping references', () => {
|
||||
const props = getDefaultProps();
|
||||
const referenceDragging = {
|
||||
columnId: 'col1',
|
||||
groupId: 'a',
|
||||
layerId: 'first',
|
||||
id: 'col1',
|
||||
humanData: { label: 'Label' },
|
||||
};
|
||||
|
||||
(generateId as jest.Mock).mockReturnValue(`ref1Copy`);
|
||||
props.state = {
|
||||
...props.state,
|
||||
layers: {
|
||||
...props.state.layers,
|
||||
first: {
|
||||
indexPatternId: 'first',
|
||||
columnOrder: ['col1', 'ref1'],
|
||||
columns: {
|
||||
col1: {
|
||||
label: 'Test reference',
|
||||
dataType: 'number',
|
||||
isBucketed: false,
|
||||
operationType: 'cumulative_sum',
|
||||
references: ['ref1'],
|
||||
},
|
||||
ref1: {
|
||||
label: 'Count of records',
|
||||
dataType: 'number',
|
||||
isBucketed: false,
|
||||
sourceField: '___records___',
|
||||
operationType: 'count',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: referenceDragging,
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'newColumnId',
|
||||
groupId: 'c',
|
||||
layerId: 'second',
|
||||
filterOperations: (op: OperationMetadata) => !op.isBucketed,
|
||||
},
|
||||
})?.dropTypes
|
||||
).toEqual(['move_compatible', 'duplicate_compatible']);
|
||||
});
|
||||
it('doesnt allow dropping for different index patterns', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers.second.indexPatternId = 'different index';
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: {
|
||||
...mockedDndOperations.metric,
|
||||
columnId: 'col1',
|
||||
layerId: 'first',
|
||||
groupId: 'c',
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed,
|
||||
},
|
||||
target: {
|
||||
...props.target,
|
||||
columnId: 'newId',
|
||||
groupId: 'c',
|
||||
layerId: 'second',
|
||||
filterOperations: (op: OperationMetadata) => !op.isBucketed,
|
||||
},
|
||||
})?.dropTypes
|
||||
).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('does not allow static value to be moved when not allowed', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers = {
|
||||
first: {
|
||||
indexPatternId: 'first',
|
||||
columns: {
|
||||
col1: mockedColumns.dateHistogram,
|
||||
colMetric: mockedColumns.count,
|
||||
},
|
||||
columnOrder: ['col1', 'colMetric'],
|
||||
incompleteColumns: {},
|
||||
},
|
||||
second: {
|
||||
indexPatternId: 'first',
|
||||
columns: {
|
||||
staticValue: mockedColumns.staticValue,
|
||||
},
|
||||
columnOrder: ['staticValue'],
|
||||
incompleteColumns: {},
|
||||
},
|
||||
};
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: {
|
||||
columnId: 'staticValue',
|
||||
groupId: 'yReferenceLineLeft',
|
||||
layerId: 'second',
|
||||
id: 'staticValue',
|
||||
humanData: { label: 'Label' },
|
||||
},
|
||||
target: {
|
||||
layerId: 'first',
|
||||
columnId: 'col1',
|
||||
groupId: 'x',
|
||||
} as DragDropOperation,
|
||||
})?.dropTypes
|
||||
).toEqual(undefined);
|
||||
});
|
||||
it('allow multiple drop types from terms to terms', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers = {
|
||||
first: {
|
||||
indexPatternId: 'first',
|
||||
columns: {
|
||||
terms: mockedColumns.terms,
|
||||
metric: mockedColumns.count,
|
||||
},
|
||||
columnOrder: ['terms', 'metric'],
|
||||
incompleteColumns: {},
|
||||
},
|
||||
second: {
|
||||
indexPatternId: 'first',
|
||||
columns: {
|
||||
terms2: mockedColumns.terms2,
|
||||
metric2: mockedColumns.count,
|
||||
},
|
||||
columnOrder: ['terms2', 'metric2'],
|
||||
incompleteColumns: {},
|
||||
},
|
||||
};
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: {
|
||||
columnId: 'terms',
|
||||
groupId: 'x',
|
||||
layerId: 'first',
|
||||
id: 'terms',
|
||||
humanData: { label: 'Label' },
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed,
|
||||
},
|
||||
target: {
|
||||
columnId: 'terms2',
|
||||
groupId: 'x',
|
||||
layerId: 'second',
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed,
|
||||
} as DragDropOperation,
|
||||
})?.dropTypes
|
||||
).toEqual([
|
||||
'replace_compatible',
|
||||
'replace_duplicate_compatible',
|
||||
'swap_compatible',
|
||||
'combine_compatible',
|
||||
]);
|
||||
});
|
||||
it('allow multiple drop types from metric on field to terms', () => {
|
||||
const props = getDefaultProps();
|
||||
props.state.layers = {
|
||||
first: {
|
||||
indexPatternId: 'first',
|
||||
columns: {
|
||||
sum: mockedColumns.sum,
|
||||
metric: mockedColumns.count,
|
||||
},
|
||||
columnOrder: ['sum', 'metric'],
|
||||
incompleteColumns: {},
|
||||
},
|
||||
second: {
|
||||
indexPatternId: 'first',
|
||||
columns: {
|
||||
terms2: mockedColumns.terms2,
|
||||
metric2: mockedColumns.count,
|
||||
},
|
||||
columnOrder: ['terms2', 'metric2'],
|
||||
incompleteColumns: {},
|
||||
},
|
||||
};
|
||||
expect(
|
||||
getDropProps({
|
||||
...props,
|
||||
source: {
|
||||
columnId: 'sum',
|
||||
groupId: 'x',
|
||||
layerId: 'first',
|
||||
id: 'sum',
|
||||
humanData: { label: 'Label' },
|
||||
filterOperations: (op: OperationMetadata) => !op.isBucketed,
|
||||
},
|
||||
target: {
|
||||
columnId: 'terms2',
|
||||
groupId: 'x',
|
||||
layerId: 'second',
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed,
|
||||
} as DragDropOperation,
|
||||
})?.dropTypes
|
||||
).toEqual([
|
||||
'replace_incompatible',
|
||||
'replace_duplicate_incompatible',
|
||||
'swap_incompatible',
|
||||
'combine_incompatible',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,13 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
DatasourceDimensionDropProps,
|
||||
isDraggedOperation,
|
||||
DraggedOperation,
|
||||
DropType,
|
||||
VisualizationDimensionGroupConfig,
|
||||
} from '../../../types';
|
||||
import { isOperation, DropType, DragDropOperation } from '../../../types';
|
||||
import {
|
||||
getCurrentFieldsForOperation,
|
||||
getOperationDisplay,
|
||||
|
@ -27,12 +21,18 @@ import {
|
|||
IndexPattern,
|
||||
IndexPatternField,
|
||||
DraggedField,
|
||||
DataViewDragDropOperation,
|
||||
} from '../../types';
|
||||
import {
|
||||
getDropPropsForSameGroup,
|
||||
isOperationFromTheSameGroup,
|
||||
} from '../../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils';
|
||||
|
||||
type GetDropProps = DatasourceDimensionDropProps<IndexPatternPrivateState> & {
|
||||
dragging?: DragContextState['dragging'];
|
||||
groupId: string;
|
||||
};
|
||||
interface GetDropPropsArgs {
|
||||
state: IndexPatternPrivateState;
|
||||
source?: DragContextState['dragging'];
|
||||
target: DragDropOperation;
|
||||
}
|
||||
|
||||
type DropProps = { dropTypes: DropType[]; nextLabel?: string } | undefined;
|
||||
|
||||
|
@ -41,7 +41,7 @@ const operationLabels = getOperationDisplay();
|
|||
export function getNewOperation(
|
||||
field: IndexPatternField | undefined | false,
|
||||
filterOperations: (meta: OperationMetadata) => boolean,
|
||||
targetColumn: GenericIndexPatternColumn,
|
||||
targetColumn?: GenericIndexPatternColumn,
|
||||
prioritizedOperation?: GenericIndexPatternColumn['operationType']
|
||||
) {
|
||||
if (!field) {
|
||||
|
@ -61,52 +61,50 @@ export function getNewOperation(
|
|||
return existsPrioritizedOperation ? prioritizedOperation : newOperations[0];
|
||||
}
|
||||
|
||||
export function getField(
|
||||
column: GenericIndexPatternColumn | undefined,
|
||||
indexPattern: IndexPattern
|
||||
) {
|
||||
export function getField(column: GenericIndexPatternColumn | undefined, dataView: IndexPattern) {
|
||||
if (!column) {
|
||||
return;
|
||||
}
|
||||
const field = (hasField(column) && indexPattern.getFieldByName(column.sourceField)) || undefined;
|
||||
const field = (hasField(column) && dataView.getFieldByName(column.sourceField)) || undefined;
|
||||
return field;
|
||||
}
|
||||
|
||||
export function getDropProps(props: GetDropProps) {
|
||||
const { state, columnId, layerId, dragging, groupId, filterOperations } = props;
|
||||
if (!dragging) {
|
||||
export function getDropProps(props: GetDropPropsArgs) {
|
||||
const { state, source, target } = props;
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
const targetProps: DataViewDragDropOperation = {
|
||||
...target,
|
||||
column: state.layers[target.layerId].columns[target.columnId],
|
||||
dataView: state.indexPatterns[state.layers[target.layerId].indexPatternId],
|
||||
};
|
||||
|
||||
if (isDraggedField(dragging)) {
|
||||
return getDropPropsForField({ ...props, dragging });
|
||||
if (isDraggedField(source)) {
|
||||
return getDropPropsForField({ ...props, source, target: targetProps });
|
||||
}
|
||||
|
||||
if (
|
||||
isDraggedOperation(dragging) &&
|
||||
dragging.layerId === layerId &&
|
||||
columnId !== dragging.columnId
|
||||
) {
|
||||
const sourceColumn = state.layers[dragging.layerId].columns[dragging.columnId];
|
||||
const targetColumn = state.layers[layerId].columns[columnId];
|
||||
const isSameGroup = groupId === dragging.groupId;
|
||||
if (isSameGroup) {
|
||||
return getDropPropsForSameGroup(!targetColumn);
|
||||
}
|
||||
const layerIndexPattern = state.indexPatterns[state.layers[layerId].indexPatternId];
|
||||
|
||||
if (filterOperations(sourceColumn)) {
|
||||
return getDropPropsForCompatibleGroup(
|
||||
props.dimensionGroups,
|
||||
dragging.columnId,
|
||||
sourceColumn,
|
||||
targetColumn,
|
||||
layerIndexPattern
|
||||
);
|
||||
} else if (hasTheSameField(sourceColumn, targetColumn)) {
|
||||
if (isOperation(source)) {
|
||||
const sourceProps: DataViewDragDropOperation = {
|
||||
...source,
|
||||
column: state.layers[source.layerId]?.columns[source.columnId],
|
||||
dataView: state.indexPatterns[state.layers[source.layerId]?.indexPatternId],
|
||||
};
|
||||
if (!sourceProps.column) {
|
||||
return;
|
||||
} else {
|
||||
return getDropPropsFromIncompatibleGroup({ ...props, dragging });
|
||||
}
|
||||
if (target.columnId !== source.columnId && targetProps.dataView === sourceProps.dataView) {
|
||||
if (isOperationFromTheSameGroup(source, target)) {
|
||||
return getDropPropsForSameGroup(!targetProps.column);
|
||||
}
|
||||
|
||||
if (targetProps.filterOperations?.(sourceProps?.column)) {
|
||||
return getDropPropsForCompatibleGroup(sourceProps, targetProps);
|
||||
} else if (hasTheSameField(sourceProps.column, targetProps.column)) {
|
||||
return;
|
||||
} else {
|
||||
return getDropPropsFromIncompatibleGroup(sourceProps, targetProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,14 +124,13 @@ function hasTheSameField(
|
|||
|
||||
function getDropPropsForField({
|
||||
state,
|
||||
columnId,
|
||||
layerId,
|
||||
dragging,
|
||||
filterOperations,
|
||||
}: GetDropProps & { dragging: DraggedField }): DropProps {
|
||||
const targetColumn = state.layers[layerId].columns[columnId];
|
||||
const isTheSameIndexPattern = state.layers[layerId].indexPatternId === dragging.indexPatternId;
|
||||
const newOperation = getNewOperation(dragging.field, filterOperations, targetColumn);
|
||||
source,
|
||||
target,
|
||||
}: GetDropPropsArgs & { source: DraggedField }): DropProps {
|
||||
const targetColumn = state.layers[target.layerId].columns[target.columnId];
|
||||
const isTheSameIndexPattern =
|
||||
state.layers[target.layerId].indexPatternId === source.indexPatternId;
|
||||
const newOperation = getNewOperation(source.field, target.filterOperations, targetColumn);
|
||||
|
||||
if (isTheSameIndexPattern && newOperation) {
|
||||
const nextLabel = operationLabels[newOperation].displayName;
|
||||
|
@ -141,18 +138,13 @@ function getDropPropsForField({
|
|||
if (!targetColumn) {
|
||||
return { dropTypes: ['field_add'], nextLabel };
|
||||
} else if (
|
||||
(hasField(targetColumn) && targetColumn.sourceField !== dragging.field.name) ||
|
||||
(hasField(targetColumn) && targetColumn.sourceField !== source.field.name) ||
|
||||
!hasField(targetColumn)
|
||||
) {
|
||||
const layerIndexPattern = state.indexPatterns[state.layers[layerId].indexPatternId];
|
||||
const layerDataView = state.indexPatterns[state.layers[target.layerId].indexPatternId];
|
||||
return hasField(targetColumn) &&
|
||||
layerIndexPattern &&
|
||||
hasOperationSupportForMultipleFields(
|
||||
layerIndexPattern,
|
||||
targetColumn,
|
||||
undefined,
|
||||
dragging.field
|
||||
)
|
||||
layerDataView &&
|
||||
hasOperationSupportForMultipleFields(layerDataView, targetColumn, undefined, source.field)
|
||||
? {
|
||||
dropTypes: ['field_replace', 'field_combine'],
|
||||
}
|
||||
|
@ -165,82 +157,68 @@ function getDropPropsForField({
|
|||
return;
|
||||
}
|
||||
|
||||
function getDropPropsForSameGroup(isNew?: boolean): DropProps {
|
||||
return !isNew ? { dropTypes: ['reorder'] } : { dropTypes: ['duplicate_compatible'] };
|
||||
}
|
||||
|
||||
function getDropPropsForCompatibleGroup(
|
||||
dimensionGroups: VisualizationDimensionGroupConfig[],
|
||||
sourceId: string,
|
||||
sourceColumn?: GenericIndexPatternColumn,
|
||||
targetColumn?: GenericIndexPatternColumn,
|
||||
indexPattern?: IndexPattern
|
||||
sourceProps: DataViewDragDropOperation,
|
||||
targetProps: DataViewDragDropOperation
|
||||
): DropProps {
|
||||
const hasSameField = sourceColumn && hasTheSameField(sourceColumn, targetColumn);
|
||||
|
||||
const canSwap =
|
||||
targetColumn &&
|
||||
!hasSameField &&
|
||||
dimensionGroups
|
||||
.find((group) => group.accessors.some((accessor) => accessor.columnId === sourceId))
|
||||
?.filterOperations(targetColumn);
|
||||
|
||||
if (!targetProps.column) {
|
||||
return { dropTypes: ['move_compatible', 'duplicate_compatible'] };
|
||||
}
|
||||
const canSwap = sourceProps.filterOperations?.(targetProps.column);
|
||||
const swapType: DropType[] = canSwap ? ['swap_compatible'] : [];
|
||||
|
||||
if (!targetColumn) {
|
||||
return { dropTypes: ['move_compatible', 'duplicate_compatible', ...swapType] };
|
||||
const dropTypes: DropType[] = ['replace_compatible', 'replace_duplicate_compatible', ...swapType];
|
||||
if (!targetProps.dataView || !hasField(targetProps.column)) {
|
||||
return { dropTypes };
|
||||
}
|
||||
if (!indexPattern || !hasField(targetColumn)) {
|
||||
return { dropTypes: ['replace_compatible', 'replace_duplicate_compatible', ...swapType] };
|
||||
}
|
||||
// With multi fields operations there are more combination of drops now
|
||||
const dropTypes: DropType[] = [];
|
||||
if (!hasSameField) {
|
||||
dropTypes.push('replace_compatible', 'replace_duplicate_compatible');
|
||||
}
|
||||
if (canSwap) {
|
||||
dropTypes.push('swap_compatible');
|
||||
}
|
||||
if (hasOperationSupportForMultipleFields(indexPattern, targetColumn, sourceColumn)) {
|
||||
|
||||
if (
|
||||
hasOperationSupportForMultipleFields(
|
||||
targetProps.dataView,
|
||||
targetProps.column,
|
||||
sourceProps.column
|
||||
)
|
||||
) {
|
||||
dropTypes.push('combine_compatible');
|
||||
}
|
||||
// return undefined if no drop action is available
|
||||
if (!dropTypes.length) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
dropTypes,
|
||||
};
|
||||
}
|
||||
|
||||
function getDropPropsFromIncompatibleGroup({
|
||||
state,
|
||||
columnId,
|
||||
layerId,
|
||||
dragging,
|
||||
filterOperations,
|
||||
}: GetDropProps & { dragging: DraggedOperation }): DropProps {
|
||||
const targetColumn = state.layers[layerId].columns[columnId];
|
||||
const sourceColumn = state.layers[dragging.layerId].columns[dragging.columnId];
|
||||
|
||||
const layerIndexPattern = state.indexPatterns[state.layers[layerId].indexPatternId];
|
||||
if (!layerIndexPattern) {
|
||||
function getDropPropsFromIncompatibleGroup(
|
||||
sourceProps: DataViewDragDropOperation,
|
||||
targetProps: DataViewDragDropOperation
|
||||
): DropProps {
|
||||
if (!targetProps.dataView || !sourceProps.column) {
|
||||
return;
|
||||
}
|
||||
const sourceField = getField(sourceColumn, layerIndexPattern);
|
||||
const newOperationForSource = getNewOperation(sourceField, filterOperations, targetColumn);
|
||||
const sourceField = getField(sourceProps.column, sourceProps.dataView);
|
||||
const newOperationForSource = getNewOperation(
|
||||
sourceField,
|
||||
targetProps.filterOperations,
|
||||
targetProps.column
|
||||
);
|
||||
|
||||
if (newOperationForSource) {
|
||||
const targetField = getField(targetColumn, layerIndexPattern);
|
||||
const canSwap = Boolean(getNewOperation(targetField, dragging.filterOperations, sourceColumn));
|
||||
const targetField = getField(targetProps.column, targetProps.dataView);
|
||||
const canSwap = Boolean(
|
||||
getNewOperation(targetField, sourceProps.filterOperations, sourceProps.column)
|
||||
);
|
||||
|
||||
const dropTypes: DropType[] = [];
|
||||
if (targetColumn) {
|
||||
if (targetProps.column) {
|
||||
dropTypes.push('replace_incompatible', 'replace_duplicate_incompatible');
|
||||
if (canSwap) {
|
||||
dropTypes.push('swap_incompatible');
|
||||
}
|
||||
if (hasOperationSupportForMultipleFields(layerIndexPattern, targetColumn, sourceColumn)) {
|
||||
if (
|
||||
hasOperationSupportForMultipleFields(
|
||||
targetProps.dataView,
|
||||
targetProps.column,
|
||||
sourceProps.column
|
||||
)
|
||||
) {
|
||||
dropTypes.push('combine_incompatible');
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IndexPattern, IndexPatternLayer } from '../../types';
|
||||
import { documentField } from '../../document_field';
|
||||
import { OperationMetadata } from '../../../types';
|
||||
import {
|
||||
DateHistogramIndexPatternColumn,
|
||||
GenericIndexPatternColumn,
|
||||
StaticValueIndexPatternColumn,
|
||||
TermsIndexPatternColumn,
|
||||
} from '../../operations';
|
||||
import { getFieldByNameFactory } from '../../pure_helpers';
|
||||
jest.mock('../../../id_generator');
|
||||
|
||||
export const mockDataViews = (): Record<string, IndexPattern> => {
|
||||
const fields = [
|
||||
{
|
||||
name: 'timestamp',
|
||||
displayName: 'timestampLabel',
|
||||
type: 'date',
|
||||
aggregatable: true,
|
||||
searchable: true,
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: 'bytes',
|
||||
displayName: 'bytes',
|
||||
type: 'number',
|
||||
aggregatable: true,
|
||||
searchable: true,
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: 'memory',
|
||||
displayName: 'memory',
|
||||
type: 'number',
|
||||
aggregatable: true,
|
||||
searchable: true,
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: 'source',
|
||||
displayName: 'source',
|
||||
type: 'string',
|
||||
aggregatable: true,
|
||||
searchable: true,
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: 'src',
|
||||
displayName: 'src',
|
||||
type: 'string',
|
||||
aggregatable: true,
|
||||
searchable: true,
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: 'dest',
|
||||
displayName: 'dest',
|
||||
type: 'string',
|
||||
aggregatable: true,
|
||||
searchable: true,
|
||||
exists: true,
|
||||
},
|
||||
documentField,
|
||||
];
|
||||
return {
|
||||
first: {
|
||||
id: 'first',
|
||||
title: 'first',
|
||||
timeFieldName: 'timestamp',
|
||||
hasRestrictions: false,
|
||||
fields,
|
||||
getFieldByName: getFieldByNameFactory(fields),
|
||||
},
|
||||
second: {
|
||||
id: 'second',
|
||||
title: 'my-fake-restricted-pattern',
|
||||
hasRestrictions: true,
|
||||
timeFieldName: 'timestamp',
|
||||
fields: [fields[0]],
|
||||
getFieldByName: getFieldByNameFactory([fields[0]]),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const mockedColumns: Record<string, GenericIndexPatternColumn> = {
|
||||
count: {
|
||||
label: 'Count of records',
|
||||
dataType: 'number',
|
||||
isBucketed: false,
|
||||
sourceField: '___records___',
|
||||
operationType: 'count',
|
||||
},
|
||||
staticValue: {
|
||||
label: 'Static value: 0.75',
|
||||
dataType: 'number',
|
||||
operationType: 'static_value',
|
||||
isStaticValue: true,
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
params: {
|
||||
value: '0.75',
|
||||
},
|
||||
references: [],
|
||||
} as StaticValueIndexPatternColumn,
|
||||
dateHistogram: {
|
||||
label: 'Date histogram of timestamp',
|
||||
customLabel: true,
|
||||
dataType: 'date',
|
||||
isBucketed: true,
|
||||
|
||||
// Private
|
||||
operationType: 'date_histogram',
|
||||
params: {
|
||||
interval: '1d',
|
||||
},
|
||||
sourceField: 'timestamp',
|
||||
} as DateHistogramIndexPatternColumn,
|
||||
dateHistogramCopy: {
|
||||
label: 'Date histogram of timestamp (1)',
|
||||
customLabel: true,
|
||||
dataType: 'date',
|
||||
isBucketed: true,
|
||||
|
||||
// Private
|
||||
operationType: 'date_histogram',
|
||||
params: {
|
||||
interval: '1d',
|
||||
},
|
||||
sourceField: 'timestamp',
|
||||
} as DateHistogramIndexPatternColumn,
|
||||
terms: {
|
||||
label: 'Top 10 values of src',
|
||||
dataType: 'string',
|
||||
isBucketed: true,
|
||||
// Private
|
||||
operationType: 'terms',
|
||||
params: {
|
||||
orderBy: { type: 'alphabetical' },
|
||||
orderDirection: 'desc',
|
||||
size: 10,
|
||||
},
|
||||
sourceField: 'src',
|
||||
} as TermsIndexPatternColumn,
|
||||
terms2: {
|
||||
label: 'Top 10 values of dest',
|
||||
dataType: 'string',
|
||||
isBucketed: true,
|
||||
|
||||
// Private
|
||||
operationType: 'terms',
|
||||
params: {
|
||||
orderBy: { type: 'alphabetical' },
|
||||
orderDirection: 'desc',
|
||||
size: 10,
|
||||
},
|
||||
sourceField: 'dest',
|
||||
} as TermsIndexPatternColumn,
|
||||
sum: {
|
||||
label: 'Sum of bytes',
|
||||
dataType: 'number',
|
||||
isBucketed: false,
|
||||
operationType: 'sum',
|
||||
sourceField: 'bytes',
|
||||
} as GenericIndexPatternColumn,
|
||||
median: {
|
||||
label: 'Median of bytes',
|
||||
dataType: 'number',
|
||||
isBucketed: false,
|
||||
|
||||
// Private
|
||||
operationType: 'median',
|
||||
sourceField: 'bytes',
|
||||
} as GenericIndexPatternColumn,
|
||||
uniqueCount: {
|
||||
label: 'Unique count of bytes',
|
||||
dataType: 'number',
|
||||
isBucketed: false,
|
||||
sourceField: 'bytes',
|
||||
operationType: 'unique_count',
|
||||
} as GenericIndexPatternColumn,
|
||||
};
|
||||
|
||||
export const mockedLayers: Record<string, (...args: string[]) => IndexPatternLayer> = {
|
||||
singleColumnLayer: (id = 'col1') => ({
|
||||
indexPatternId: 'first',
|
||||
columnOrder: [id],
|
||||
columns: {
|
||||
[id]: mockedColumns.dateHistogram,
|
||||
},
|
||||
incompleteColumns: {},
|
||||
}),
|
||||
doubleColumnLayer: (id1 = 'col1', id2 = 'col2') => ({
|
||||
indexPatternId: 'first',
|
||||
columnOrder: [id1, id2],
|
||||
columns: {
|
||||
[id1]: mockedColumns.dateHistogram,
|
||||
[id2]: mockedColumns.terms,
|
||||
},
|
||||
incompleteColumns: {},
|
||||
}),
|
||||
multipleColumnsLayer: (id1 = 'col1', id2 = 'col2', id3 = 'col3', id4 = 'col4') => ({
|
||||
indexPatternId: 'first',
|
||||
columnOrder: [id1, id2, id3, id4],
|
||||
columns: {
|
||||
[id1]: mockedColumns.dateHistogram,
|
||||
[id2]: mockedColumns.terms,
|
||||
[id3]: mockedColumns.terms2,
|
||||
[id4]: mockedColumns.median,
|
||||
},
|
||||
}),
|
||||
emptyLayer: () => ({
|
||||
indexPatternId: 'first',
|
||||
columnOrder: [],
|
||||
columns: {},
|
||||
}),
|
||||
};
|
||||
|
||||
export const mockedDraggedField = {
|
||||
field: { type: 'number', name: 'bytes', aggregatable: true },
|
||||
indexPatternId: 'first',
|
||||
id: 'bar',
|
||||
humanData: { label: 'Label' },
|
||||
};
|
||||
|
||||
export const mockedDndOperations = {
|
||||
notFiltering: {
|
||||
layerId: 'first',
|
||||
groupId: 'a',
|
||||
filterOperations: () => true,
|
||||
columnId: 'col1',
|
||||
id: 'col1',
|
||||
humanData: { label: 'Column 1' },
|
||||
},
|
||||
metric: {
|
||||
layerId: 'first',
|
||||
groupId: 'a',
|
||||
columnId: 'col1',
|
||||
filterOperations: (op: OperationMetadata) => !op.isBucketed,
|
||||
id: 'col1',
|
||||
humanData: { label: 'Column 1' },
|
||||
},
|
||||
numericalOnly: {
|
||||
layerId: 'first',
|
||||
groupId: 'a',
|
||||
columnId: 'col1',
|
||||
filterOperations: (op: OperationMetadata) => op.dataType === 'number',
|
||||
id: 'col1',
|
||||
humanData: { label: 'Column 1' },
|
||||
},
|
||||
bucket: {
|
||||
columnId: 'col2',
|
||||
groupId: 'b',
|
||||
layerId: 'first',
|
||||
id: 'col2',
|
||||
humanData: { label: 'Column 2' },
|
||||
filterOperations: (op: OperationMetadata) => op.isBucketed,
|
||||
},
|
||||
staticValue: {
|
||||
columnId: 'col1',
|
||||
groupId: 'b',
|
||||
layerId: 'first',
|
||||
id: 'col1',
|
||||
humanData: { label: 'Column 2' },
|
||||
filterOperations: (op: OperationMetadata) => !!op.isStaticValue,
|
||||
},
|
||||
bucket2: {
|
||||
columnId: 'col3',
|
||||
groupId: 'b',
|
||||
layerId: 'first',
|
||||
id: 'col3',
|
||||
humanData: {
|
||||
label: '',
|
||||
},
|
||||
},
|
||||
metricC: {
|
||||
columnId: 'col4',
|
||||
groupId: 'c',
|
||||
layerId: 'first',
|
||||
id: 'col4',
|
||||
humanData: {
|
||||
label: '',
|
||||
},
|
||||
filterOperations: (op: OperationMetadata) => !op.isBucketed,
|
||||
},
|
||||
};
|
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,14 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { DatasourceDimensionDropHandlerProps, DraggedOperation } from '../../../types';
|
||||
import {
|
||||
DatasourceDimensionDropHandlerProps,
|
||||
DragDropOperation,
|
||||
DropType,
|
||||
isOperation,
|
||||
StateSetter,
|
||||
VisualizationDimensionGroupConfig,
|
||||
} from '../../../types';
|
||||
import {
|
||||
insertOrReplaceColumn,
|
||||
deleteColumn,
|
||||
|
@ -14,75 +21,461 @@ import {
|
|||
hasOperationSupportForMultipleFields,
|
||||
getOperationHelperForMultipleFields,
|
||||
replaceColumn,
|
||||
deleteColumnInLayers,
|
||||
} from '../../operations';
|
||||
import { mergeLayer } from '../../state_helpers';
|
||||
import { mergeLayer, mergeLayers } from '../../state_helpers';
|
||||
import { isDraggedField } from '../../pure_utils';
|
||||
import { getNewOperation, getField } from './get_drop_props';
|
||||
import { IndexPatternPrivateState, DraggedField } from '../../types';
|
||||
import { IndexPatternPrivateState, DraggedField, DataViewDragDropOperation } from '../../types';
|
||||
import { trackUiEvent } from '../../../lens_ui_telemetry';
|
||||
|
||||
type DropHandlerProps<T> = DatasourceDimensionDropHandlerProps<IndexPatternPrivateState> & {
|
||||
droppedItem: T;
|
||||
};
|
||||
|
||||
export function onDrop(props: DatasourceDimensionDropHandlerProps<IndexPatternPrivateState>) {
|
||||
const { droppedItem, dropType } = props;
|
||||
|
||||
if (dropType === 'field_add' || dropType === 'field_replace' || dropType === 'field_combine') {
|
||||
return operationOnDropMap[dropType]({
|
||||
...props,
|
||||
droppedItem: droppedItem as DraggedField,
|
||||
});
|
||||
}
|
||||
return operationOnDropMap[dropType]({
|
||||
...props,
|
||||
droppedItem: droppedItem as DraggedOperation,
|
||||
});
|
||||
interface DropHandlerProps<T = DataViewDragDropOperation> {
|
||||
state: IndexPatternPrivateState;
|
||||
setState: StateSetter<
|
||||
IndexPatternPrivateState,
|
||||
{
|
||||
isDimensionComplete?: boolean;
|
||||
forceRender?: boolean;
|
||||
}
|
||||
>;
|
||||
dimensionGroups: VisualizationDimensionGroupConfig[];
|
||||
dropType?: DropType;
|
||||
source: T;
|
||||
target: DataViewDragDropOperation;
|
||||
}
|
||||
|
||||
const operationOnDropMap = {
|
||||
field_add: onFieldDrop,
|
||||
field_replace: onFieldDrop,
|
||||
field_combine: (props: DropHandlerProps<DraggedField>) => onFieldDrop(props, true),
|
||||
export function onDrop(props: DatasourceDimensionDropHandlerProps<IndexPatternPrivateState>) {
|
||||
const { target, source, dropType, state } = props;
|
||||
|
||||
reorder: onReorder,
|
||||
if (isDraggedField(source) && isFieldDropType(dropType)) {
|
||||
return onFieldDrop(
|
||||
{
|
||||
...props,
|
||||
target: {
|
||||
...target,
|
||||
dataView: state.indexPatterns[state.layers[target.layerId].indexPatternId],
|
||||
},
|
||||
source,
|
||||
},
|
||||
dropType === 'field_combine'
|
||||
);
|
||||
}
|
||||
|
||||
move_compatible: (props: DropHandlerProps<DraggedOperation>) => onMoveCompatible(props, true),
|
||||
replace_compatible: (props: DropHandlerProps<DraggedOperation>) => onMoveCompatible(props, true),
|
||||
duplicate_compatible: onMoveCompatible,
|
||||
replace_duplicate_compatible: onMoveCompatible,
|
||||
if (!isOperation(source)) {
|
||||
return false;
|
||||
}
|
||||
const sourceDataView = state.indexPatterns[state.layers[source.layerId].indexPatternId];
|
||||
const targetDataView = state.indexPatterns[state.layers[target.layerId].indexPatternId];
|
||||
if (sourceDataView !== targetDataView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
move_incompatible: (props: DropHandlerProps<DraggedOperation>) => onMoveIncompatible(props, true),
|
||||
replace_incompatible: (props: DropHandlerProps<DraggedOperation>) =>
|
||||
onMoveIncompatible(props, true),
|
||||
duplicate_incompatible: onMoveIncompatible,
|
||||
replace_duplicate_incompatible: onMoveIncompatible,
|
||||
const operationProps = {
|
||||
...props,
|
||||
target: {
|
||||
...target,
|
||||
dataView: targetDataView,
|
||||
},
|
||||
source: {
|
||||
...source,
|
||||
dataView: sourceDataView,
|
||||
},
|
||||
};
|
||||
if (dropType === 'reorder') {
|
||||
return onReorder(operationProps);
|
||||
}
|
||||
|
||||
swap_compatible: onSwapCompatible,
|
||||
swap_incompatible: onSwapIncompatible,
|
||||
combine_compatible: onCombineCompatible,
|
||||
combine_incompatible: onCombineCompatible,
|
||||
};
|
||||
if (['move_compatible', 'replace_compatible'].includes(dropType)) {
|
||||
return onMoveCompatible(operationProps, true);
|
||||
}
|
||||
if (['duplicate_compatible', 'replace_duplicate_compatible'].includes(dropType)) {
|
||||
return onMoveCompatible(operationProps);
|
||||
}
|
||||
if (['move_incompatible', 'replace_incompatible'].includes(dropType)) {
|
||||
return onMoveIncompatible(operationProps, true);
|
||||
}
|
||||
if (['duplicate_incompatible', 'replace_duplicate_incompatible'].includes(dropType)) {
|
||||
return onMoveIncompatible(operationProps);
|
||||
}
|
||||
if (dropType === 'swap_compatible') {
|
||||
return onSwapCompatible(operationProps);
|
||||
}
|
||||
if (dropType === 'swap_incompatible') {
|
||||
return onSwapIncompatible(operationProps);
|
||||
}
|
||||
if (['combine_incompatible', 'combine_compatible'].includes(dropType)) {
|
||||
return onCombine(operationProps);
|
||||
}
|
||||
}
|
||||
|
||||
function onCombineCompatible({
|
||||
columnId,
|
||||
const isFieldDropType = (dropType: DropType) =>
|
||||
['field_add', 'field_replace', 'field_combine'].includes(dropType);
|
||||
|
||||
function onFieldDrop(props: DropHandlerProps<DraggedField>, shouldAddField?: boolean) {
|
||||
const { setState, state, source, target, dimensionGroups } = props;
|
||||
|
||||
const prioritizedOperation = dimensionGroups.find(
|
||||
(g) => g.groupId === target.groupId
|
||||
)?.prioritizedOperation;
|
||||
|
||||
const layer = state.layers[target.layerId];
|
||||
const indexPattern = state.indexPatterns[layer.indexPatternId];
|
||||
const targetColumn = layer.columns[target.columnId];
|
||||
const newOperation = shouldAddField
|
||||
? targetColumn.operationType
|
||||
: getNewOperation(source.field, target.filterOperations, targetColumn, prioritizedOperation);
|
||||
|
||||
if (
|
||||
!isDraggedField(source) ||
|
||||
!newOperation ||
|
||||
(shouldAddField &&
|
||||
!hasOperationSupportForMultipleFields(indexPattern, targetColumn, undefined, source.field))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const field = shouldAddField ? getField(targetColumn, indexPattern) : source.field;
|
||||
const initialParams = shouldAddField
|
||||
? {
|
||||
params:
|
||||
getOperationHelperForMultipleFields(targetColumn.operationType)?.({
|
||||
targetColumn,
|
||||
field: source.field,
|
||||
indexPattern,
|
||||
}) || {},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const newLayer = insertOrReplaceColumn({
|
||||
layer,
|
||||
columnId: target.columnId,
|
||||
indexPattern,
|
||||
op: newOperation,
|
||||
field,
|
||||
visualizationGroups: dimensionGroups,
|
||||
targetGroup: target.groupId,
|
||||
shouldCombineField: shouldAddField,
|
||||
initialParams,
|
||||
});
|
||||
|
||||
trackUiEvent('drop_onto_dimension');
|
||||
const hasData = Object.values(state.layers).some(({ columns }) => columns.length);
|
||||
trackUiEvent(hasData ? 'drop_non_empty' : 'drop_empty');
|
||||
setState(mergeLayer({ state, layerId: target.layerId, newLayer }));
|
||||
return true;
|
||||
}
|
||||
|
||||
function onMoveCompatible(
|
||||
{ setState, state, source, target, dimensionGroups }: DropHandlerProps<DataViewDragDropOperation>,
|
||||
shouldDeleteSource?: boolean
|
||||
) {
|
||||
const modifiedLayers = copyColumn({
|
||||
layers: state.layers,
|
||||
target,
|
||||
source,
|
||||
shouldDeleteSource,
|
||||
});
|
||||
|
||||
if (target.layerId === source.layerId) {
|
||||
const updatedColumnOrder = reorderByGroups(
|
||||
dimensionGroups,
|
||||
getColumnOrder(modifiedLayers[target.layerId]),
|
||||
target.groupId,
|
||||
target.columnId
|
||||
);
|
||||
|
||||
const newLayer = {
|
||||
...modifiedLayers[target.layerId],
|
||||
columnOrder: updatedColumnOrder,
|
||||
columns: modifiedLayers[target.layerId].columns,
|
||||
};
|
||||
|
||||
// Time to replace
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId: target.layerId,
|
||||
newLayer,
|
||||
})
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
setState(mergeLayers({ state, newLayers: modifiedLayers }));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function onReorder({
|
||||
setState,
|
||||
state,
|
||||
layerId,
|
||||
droppedItem,
|
||||
dimensionGroups,
|
||||
groupId,
|
||||
}: DropHandlerProps<DraggedOperation>) {
|
||||
const layer = state.layers[layerId];
|
||||
const sourceId = droppedItem.columnId;
|
||||
const targetId = columnId;
|
||||
const indexPattern = state.indexPatterns[layer.indexPatternId];
|
||||
const sourceColumn = layer.columns[sourceId];
|
||||
const targetColumn = layer.columns[targetId];
|
||||
source,
|
||||
target,
|
||||
}: DropHandlerProps<DataViewDragDropOperation>) {
|
||||
function reorderElements(items: string[], targetId: string, sourceId: string) {
|
||||
const result = items.filter((c) => c !== sourceId);
|
||||
const targetIndex = items.findIndex((c) => c === sourceId);
|
||||
const sourceIndex = items.findIndex((c) => c === targetId);
|
||||
|
||||
const targetPosition = result.indexOf(targetId);
|
||||
result.splice(targetIndex < sourceIndex ? targetPosition + 1 : targetPosition, 0, sourceId);
|
||||
return result;
|
||||
}
|
||||
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId: target.layerId,
|
||||
newLayer: {
|
||||
columnOrder: reorderElements(
|
||||
state.layers[target.layerId].columnOrder,
|
||||
target.columnId,
|
||||
source.columnId
|
||||
),
|
||||
},
|
||||
})
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
function onMoveIncompatible(
|
||||
{ setState, state, source, dimensionGroups, target }: DropHandlerProps<DataViewDragDropOperation>,
|
||||
shouldDeleteSource?: boolean
|
||||
) {
|
||||
const targetLayer = state.layers[target.layerId];
|
||||
const targetColumn = targetLayer.columns[target.columnId] || null;
|
||||
const sourceLayer = state.layers[source.layerId];
|
||||
const indexPattern = state.indexPatterns[sourceLayer.indexPatternId];
|
||||
const sourceColumn = sourceLayer.columns[source.columnId];
|
||||
const sourceField = getField(sourceColumn, indexPattern);
|
||||
const newOperation = getNewOperation(sourceField, target.filterOperations, targetColumn);
|
||||
if (!newOperation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const outputSourceLayer = shouldDeleteSource
|
||||
? deleteColumn({
|
||||
layer: sourceLayer,
|
||||
columnId: source.columnId,
|
||||
indexPattern,
|
||||
})
|
||||
: sourceLayer;
|
||||
|
||||
if (target.layerId === source.layerId) {
|
||||
const newLayer = insertOrReplaceColumn({
|
||||
layer: outputSourceLayer,
|
||||
columnId: target.columnId,
|
||||
indexPattern,
|
||||
op: newOperation,
|
||||
field: sourceField,
|
||||
visualizationGroups: dimensionGroups,
|
||||
targetGroup: target.groupId,
|
||||
shouldResetLabel: true,
|
||||
});
|
||||
|
||||
trackUiEvent('drop_onto_dimension');
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId: target.layerId,
|
||||
newLayer,
|
||||
})
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
const outputTargetLayer = insertOrReplaceColumn({
|
||||
layer: targetLayer,
|
||||
columnId: target.columnId,
|
||||
indexPattern,
|
||||
op: newOperation,
|
||||
field: sourceField,
|
||||
visualizationGroups: dimensionGroups,
|
||||
targetGroup: target.groupId,
|
||||
shouldResetLabel: true,
|
||||
});
|
||||
|
||||
trackUiEvent('drop_onto_dimension');
|
||||
setState(
|
||||
mergeLayers({
|
||||
state,
|
||||
newLayers: {
|
||||
[source.layerId]: outputSourceLayer,
|
||||
[target.layerId]: outputTargetLayer,
|
||||
},
|
||||
})
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function onSwapIncompatible({
|
||||
setState,
|
||||
state,
|
||||
source,
|
||||
dimensionGroups,
|
||||
target,
|
||||
}: DropHandlerProps<DragDropOperation>) {
|
||||
const targetLayer = state.layers[target.layerId];
|
||||
const sourceLayer = state.layers[source.layerId];
|
||||
const indexPattern = state.indexPatterns[targetLayer.indexPatternId];
|
||||
const sourceColumn = sourceLayer.columns[source.columnId];
|
||||
const targetColumn = targetLayer.columns[target.columnId];
|
||||
|
||||
// extract the field from the source column
|
||||
const sourceField = getField(sourceColumn, indexPattern);
|
||||
const targetField = getField(targetColumn, indexPattern);
|
||||
|
||||
const newOperationForSource = getNewOperation(sourceField, target.filterOperations, targetColumn);
|
||||
const newOperationForTarget = getNewOperation(targetField, source.filterOperations, sourceColumn);
|
||||
|
||||
if (!newOperationForSource || !newOperationForTarget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const outputTargetLayer = insertOrReplaceColumn({
|
||||
layer: targetLayer,
|
||||
columnId: target.columnId,
|
||||
targetGroup: target.groupId,
|
||||
indexPattern,
|
||||
op: newOperationForSource,
|
||||
field: sourceField,
|
||||
visualizationGroups: dimensionGroups,
|
||||
shouldResetLabel: true,
|
||||
});
|
||||
|
||||
if (source.layerId === target.layerId) {
|
||||
const newLayer = insertOrReplaceColumn({
|
||||
layer: outputTargetLayer,
|
||||
columnId: source.columnId,
|
||||
indexPattern,
|
||||
op: newOperationForTarget,
|
||||
field: targetField,
|
||||
visualizationGroups: dimensionGroups,
|
||||
targetGroup: source.groupId,
|
||||
shouldResetLabel: true,
|
||||
});
|
||||
|
||||
trackUiEvent('drop_onto_dimension');
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId: target.layerId,
|
||||
newLayer,
|
||||
})
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
const outputSourceLayer = insertOrReplaceColumn({
|
||||
layer: sourceLayer,
|
||||
columnId: source.columnId,
|
||||
indexPattern,
|
||||
op: newOperationForTarget,
|
||||
field: targetField,
|
||||
visualizationGroups: dimensionGroups,
|
||||
targetGroup: source.groupId,
|
||||
shouldResetLabel: true,
|
||||
});
|
||||
|
||||
trackUiEvent('drop_onto_dimension');
|
||||
setState(
|
||||
mergeLayers({
|
||||
state,
|
||||
newLayers: { [source.layerId]: outputSourceLayer, [target.layerId]: outputTargetLayer },
|
||||
})
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const swapColumnOrder = (columnOrder: string[], sourceId: string, targetId: string) => {
|
||||
const sourceIndex = columnOrder.findIndex((c) => c === sourceId);
|
||||
const targetIndex = columnOrder.findIndex((c) => c === targetId);
|
||||
|
||||
const newColumnOrder = [...columnOrder];
|
||||
newColumnOrder[sourceIndex] = targetId;
|
||||
newColumnOrder[targetIndex] = sourceId;
|
||||
|
||||
return newColumnOrder;
|
||||
};
|
||||
|
||||
function onSwapCompatible({
|
||||
setState,
|
||||
state,
|
||||
source,
|
||||
dimensionGroups,
|
||||
target,
|
||||
}: DropHandlerProps<DataViewDragDropOperation>) {
|
||||
if (target.layerId === source.layerId) {
|
||||
const layer = state.layers[target.layerId];
|
||||
const newColumns = {
|
||||
...layer.columns,
|
||||
[target.columnId]: { ...layer.columns[source.columnId] },
|
||||
[source.columnId]: { ...layer.columns[target.columnId] },
|
||||
};
|
||||
|
||||
let updatedColumnOrder = swapColumnOrder(layer.columnOrder, source.columnId, target.columnId);
|
||||
updatedColumnOrder = reorderByGroups(
|
||||
dimensionGroups,
|
||||
updatedColumnOrder,
|
||||
target.groupId,
|
||||
target.columnId
|
||||
);
|
||||
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId: target.layerId,
|
||||
newLayer: {
|
||||
columnOrder: updatedColumnOrder,
|
||||
columns: newColumns,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
const newTargetLayer = copyColumn({
|
||||
layers: state.layers,
|
||||
target,
|
||||
source,
|
||||
shouldDeleteSource: true,
|
||||
})[target.layerId];
|
||||
|
||||
const newSourceLayer = copyColumn({
|
||||
layers: state.layers,
|
||||
target: source,
|
||||
source: target,
|
||||
shouldDeleteSource: true,
|
||||
})[source.layerId];
|
||||
|
||||
setState(
|
||||
mergeLayers({
|
||||
state,
|
||||
newLayers: {
|
||||
[source.layerId]: newSourceLayer,
|
||||
[target.layerId]: newTargetLayer,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function onCombine({
|
||||
state,
|
||||
setState,
|
||||
source,
|
||||
target,
|
||||
dimensionGroups,
|
||||
}: DropHandlerProps<DataViewDragDropOperation>) {
|
||||
const targetLayer = state.layers[target.layerId];
|
||||
const targetColumn = targetLayer.columns[target.columnId];
|
||||
const targetField = getField(targetColumn, target.dataView);
|
||||
const indexPattern = state.indexPatterns[targetLayer.indexPatternId];
|
||||
|
||||
const sourceLayer = state.layers[source.layerId];
|
||||
const sourceColumn = sourceLayer.columns[source.columnId];
|
||||
const sourceField = getField(sourceColumn, indexPattern);
|
||||
// extract the field from the source column
|
||||
if (!sourceField || !targetField) {
|
||||
return false;
|
||||
}
|
||||
|
@ -96,339 +489,22 @@ function onCombineCompatible({
|
|||
}) ?? {},
|
||||
};
|
||||
|
||||
const modifiedLayer = replaceColumn({
|
||||
layer,
|
||||
columnId,
|
||||
const outputTargetLayer = replaceColumn({
|
||||
layer: targetLayer,
|
||||
columnId: target.columnId,
|
||||
indexPattern,
|
||||
op: targetColumn.operationType,
|
||||
field: targetField,
|
||||
visualizationGroups: dimensionGroups,
|
||||
targetGroup: groupId,
|
||||
targetGroup: target.groupId,
|
||||
initialParams,
|
||||
shouldCombineField: true,
|
||||
});
|
||||
const newLayer = deleteColumn({
|
||||
layer: modifiedLayer,
|
||||
columnId: sourceId,
|
||||
indexPattern,
|
||||
|
||||
const newLayers = deleteColumnInLayers({
|
||||
layers: { ...state.layers, [target.layerId]: outputTargetLayer },
|
||||
source,
|
||||
});
|
||||
|
||||
// Time to replace
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId,
|
||||
newLayer,
|
||||
})
|
||||
);
|
||||
|
||||
return { deleted: sourceId };
|
||||
}
|
||||
|
||||
function onFieldDrop(props: DropHandlerProps<DraggedField>, shouldAddField?: boolean) {
|
||||
const {
|
||||
columnId,
|
||||
setState,
|
||||
state,
|
||||
layerId,
|
||||
droppedItem,
|
||||
filterOperations,
|
||||
groupId,
|
||||
dimensionGroups,
|
||||
} = props;
|
||||
|
||||
const prioritizedOperation = dimensionGroups.find(
|
||||
(g) => g.groupId === groupId
|
||||
)?.prioritizedOperation;
|
||||
|
||||
const layer = state.layers[layerId];
|
||||
const indexPattern = state.indexPatterns[layer.indexPatternId];
|
||||
const targetColumn = layer.columns[columnId];
|
||||
const newOperation = shouldAddField
|
||||
? targetColumn.operationType
|
||||
: getNewOperation(droppedItem.field, filterOperations, targetColumn, prioritizedOperation);
|
||||
|
||||
if (
|
||||
!isDraggedField(droppedItem) ||
|
||||
!newOperation ||
|
||||
(shouldAddField &&
|
||||
!hasOperationSupportForMultipleFields(
|
||||
indexPattern,
|
||||
targetColumn,
|
||||
undefined,
|
||||
droppedItem.field
|
||||
))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const field = shouldAddField ? getField(targetColumn, indexPattern) : droppedItem.field;
|
||||
const initialParams = shouldAddField
|
||||
? {
|
||||
params:
|
||||
getOperationHelperForMultipleFields(targetColumn.operationType)?.({
|
||||
targetColumn,
|
||||
field: droppedItem.field,
|
||||
indexPattern,
|
||||
}) || {},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const newLayer = insertOrReplaceColumn({
|
||||
layer,
|
||||
columnId,
|
||||
indexPattern,
|
||||
op: newOperation,
|
||||
field,
|
||||
visualizationGroups: dimensionGroups,
|
||||
targetGroup: groupId,
|
||||
shouldCombineField: shouldAddField,
|
||||
initialParams,
|
||||
});
|
||||
|
||||
trackUiEvent('drop_onto_dimension');
|
||||
const hasData = Object.values(state.layers).some(({ columns }) => columns.length);
|
||||
trackUiEvent(hasData ? 'drop_non_empty' : 'drop_empty');
|
||||
setState(mergeLayer({ state, layerId, newLayer }));
|
||||
return true;
|
||||
}
|
||||
|
||||
function onMoveCompatible(
|
||||
{
|
||||
columnId,
|
||||
setState,
|
||||
state,
|
||||
layerId,
|
||||
droppedItem,
|
||||
dimensionGroups,
|
||||
groupId,
|
||||
}: DropHandlerProps<DraggedOperation>,
|
||||
shouldDeleteSource?: boolean
|
||||
) {
|
||||
const layer = state.layers[layerId];
|
||||
const sourceColumn = layer.columns[droppedItem.columnId];
|
||||
const indexPattern = state.indexPatterns[layer.indexPatternId];
|
||||
|
||||
const modifiedLayer = copyColumn({
|
||||
layer,
|
||||
targetId: columnId,
|
||||
sourceColumnId: droppedItem.columnId,
|
||||
sourceColumn,
|
||||
shouldDeleteSource,
|
||||
indexPattern,
|
||||
});
|
||||
|
||||
const updatedColumnOrder = reorderByGroups(
|
||||
dimensionGroups,
|
||||
groupId,
|
||||
getColumnOrder(modifiedLayer),
|
||||
columnId
|
||||
);
|
||||
|
||||
// Time to replace
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId,
|
||||
newLayer: {
|
||||
columnOrder: updatedColumnOrder,
|
||||
columns: modifiedLayer.columns,
|
||||
},
|
||||
})
|
||||
);
|
||||
return shouldDeleteSource ? { deleted: droppedItem.columnId } : true;
|
||||
}
|
||||
|
||||
function onReorder({
|
||||
columnId,
|
||||
setState,
|
||||
state,
|
||||
layerId,
|
||||
droppedItem,
|
||||
}: DropHandlerProps<DraggedOperation>) {
|
||||
function reorderElements(items: string[], dest: string, src: string) {
|
||||
const result = items.filter((c) => c !== src);
|
||||
const targetIndex = items.findIndex((c) => c === src);
|
||||
const sourceIndex = items.findIndex((c) => c === dest);
|
||||
|
||||
const targetPosition = result.indexOf(dest);
|
||||
result.splice(targetIndex < sourceIndex ? targetPosition + 1 : targetPosition, 0, src);
|
||||
return result;
|
||||
}
|
||||
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId,
|
||||
newLayer: {
|
||||
columnOrder: reorderElements(
|
||||
state.layers[layerId].columnOrder,
|
||||
columnId,
|
||||
droppedItem.columnId
|
||||
),
|
||||
},
|
||||
})
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
function onMoveIncompatible(
|
||||
{
|
||||
columnId,
|
||||
setState,
|
||||
state,
|
||||
layerId,
|
||||
droppedItem,
|
||||
filterOperations,
|
||||
dimensionGroups,
|
||||
groupId,
|
||||
}: DropHandlerProps<DraggedOperation>,
|
||||
shouldDeleteSource?: boolean
|
||||
) {
|
||||
const layer = state.layers[layerId];
|
||||
const indexPattern = state.indexPatterns[layer.indexPatternId];
|
||||
const sourceColumn = layer.columns[droppedItem.columnId];
|
||||
const targetColumn = layer.columns[columnId] || null;
|
||||
|
||||
const sourceField = getField(sourceColumn, indexPattern);
|
||||
const newOperation = getNewOperation(sourceField, filterOperations, targetColumn);
|
||||
if (!newOperation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const modifiedLayer = shouldDeleteSource
|
||||
? deleteColumn({
|
||||
layer,
|
||||
columnId: droppedItem.columnId,
|
||||
indexPattern,
|
||||
})
|
||||
: layer;
|
||||
|
||||
const newLayer = insertOrReplaceColumn({
|
||||
layer: modifiedLayer,
|
||||
columnId,
|
||||
indexPattern,
|
||||
op: newOperation,
|
||||
field: sourceField,
|
||||
visualizationGroups: dimensionGroups,
|
||||
targetGroup: groupId,
|
||||
shouldResetLabel: true,
|
||||
});
|
||||
|
||||
trackUiEvent('drop_onto_dimension');
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId,
|
||||
newLayer,
|
||||
})
|
||||
);
|
||||
return shouldDeleteSource ? { deleted: droppedItem.columnId } : true;
|
||||
}
|
||||
|
||||
function onSwapIncompatible({
|
||||
columnId,
|
||||
setState,
|
||||
state,
|
||||
layerId,
|
||||
droppedItem,
|
||||
filterOperations,
|
||||
dimensionGroups,
|
||||
groupId,
|
||||
}: DropHandlerProps<DraggedOperation>) {
|
||||
const layer = state.layers[layerId];
|
||||
const indexPattern = state.indexPatterns[layer.indexPatternId];
|
||||
const sourceColumn = layer.columns[droppedItem.columnId];
|
||||
const targetColumn = layer.columns[columnId];
|
||||
|
||||
const sourceField = getField(sourceColumn, indexPattern);
|
||||
const targetField = getField(targetColumn, indexPattern);
|
||||
|
||||
const newOperationForSource = getNewOperation(sourceField, filterOperations, targetColumn);
|
||||
const newOperationForTarget = getNewOperation(
|
||||
targetField,
|
||||
droppedItem.filterOperations,
|
||||
sourceColumn
|
||||
);
|
||||
|
||||
if (!newOperationForSource || !newOperationForTarget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const newLayer = insertOrReplaceColumn({
|
||||
layer: insertOrReplaceColumn({
|
||||
layer,
|
||||
columnId,
|
||||
targetGroup: groupId,
|
||||
indexPattern,
|
||||
op: newOperationForSource,
|
||||
field: sourceField,
|
||||
visualizationGroups: dimensionGroups,
|
||||
shouldResetLabel: true,
|
||||
}),
|
||||
columnId: droppedItem.columnId,
|
||||
indexPattern,
|
||||
op: newOperationForTarget,
|
||||
field: targetField,
|
||||
visualizationGroups: dimensionGroups,
|
||||
targetGroup: droppedItem.groupId,
|
||||
shouldResetLabel: true,
|
||||
});
|
||||
|
||||
trackUiEvent('drop_onto_dimension');
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId,
|
||||
newLayer,
|
||||
})
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
const swapColumnOrder = (columnOrder: string[], sourceId: string, targetId: string) => {
|
||||
const newColumnOrder = [...columnOrder];
|
||||
const sourceIndex = newColumnOrder.findIndex((c) => c === sourceId);
|
||||
const targetIndex = newColumnOrder.findIndex((c) => c === targetId);
|
||||
|
||||
newColumnOrder[sourceIndex] = targetId;
|
||||
newColumnOrder[targetIndex] = sourceId;
|
||||
|
||||
return newColumnOrder;
|
||||
};
|
||||
|
||||
function onSwapCompatible({
|
||||
columnId,
|
||||
setState,
|
||||
state,
|
||||
layerId,
|
||||
droppedItem,
|
||||
dimensionGroups,
|
||||
groupId,
|
||||
}: DropHandlerProps<DraggedOperation>) {
|
||||
const layer = state.layers[layerId];
|
||||
const sourceId = droppedItem.columnId;
|
||||
const targetId = columnId;
|
||||
|
||||
const sourceColumn = { ...layer.columns[sourceId] };
|
||||
const targetColumn = { ...layer.columns[targetId] };
|
||||
const newColumns = { ...layer.columns };
|
||||
newColumns[targetId] = sourceColumn;
|
||||
newColumns[sourceId] = targetColumn;
|
||||
|
||||
let updatedColumnOrder = swapColumnOrder(layer.columnOrder, sourceId, targetId);
|
||||
updatedColumnOrder = reorderByGroups(dimensionGroups, groupId, updatedColumnOrder, columnId);
|
||||
|
||||
// Time to replace
|
||||
setState(
|
||||
mergeLayer({
|
||||
state,
|
||||
layerId,
|
||||
newLayer: {
|
||||
columnOrder: updatedColumnOrder,
|
||||
columns: newColumns,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
setState(mergeLayers({ state, newLayers }));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,9 @@ export interface OperationSupportMatrix {
|
|||
}
|
||||
|
||||
type Props = Pick<
|
||||
DatasourceDimensionDropProps<IndexPatternPrivateState>,
|
||||
'layerId' | 'columnId' | 'state' | 'filterOperations'
|
||||
>;
|
||||
DatasourceDimensionDropProps<IndexPatternPrivateState>['target'],
|
||||
'layerId' | 'columnId' | 'filterOperations'
|
||||
> & { state: IndexPatternPrivateState };
|
||||
|
||||
function computeOperationMatrix(
|
||||
operationsByMetadata: Array<{
|
||||
|
|
|
@ -150,12 +150,8 @@ export const countOperation: OperationDefinition<CountIndexPatternColumn, 'field
|
|||
},
|
||||
];
|
||||
},
|
||||
onOtherColumnChanged: (layer, thisColumnId, changedColumnId) =>
|
||||
adjustTimeScaleOnOtherColumnChange<CountIndexPatternColumn>(
|
||||
layer,
|
||||
thisColumnId,
|
||||
changedColumnId
|
||||
),
|
||||
onOtherColumnChanged: (layer, thisColumnId) =>
|
||||
adjustTimeScaleOnOtherColumnChange<CountIndexPatternColumn>(layer, thisColumnId),
|
||||
toEsAggsFn: (column, columnId) => {
|
||||
return buildExpressionFunction<AggFunctionsMapping['aggCount']>('aggCount', {
|
||||
id: columnId,
|
||||
|
|
|
@ -174,13 +174,23 @@ export const formulaOperation: OperationDefinition<FormulaIndexPatternColumn, 'm
|
|||
isTransferable: () => {
|
||||
return true;
|
||||
},
|
||||
createCopy(layer, sourceId, targetId, indexPattern, operationDefinitionMap) {
|
||||
const currentColumn = layer.columns[sourceId] as FormulaIndexPatternColumn;
|
||||
|
||||
return insertOrReplaceFormulaColumn(targetId, currentColumn, layer, {
|
||||
indexPattern,
|
||||
operations: operationDefinitionMap,
|
||||
}).layer;
|
||||
createCopy(layers, source, target, operationDefinitionMap) {
|
||||
const currentColumn = layers[source.layerId].columns[
|
||||
source.columnId
|
||||
] as FormulaIndexPatternColumn;
|
||||
const modifiedLayer = insertOrReplaceFormulaColumn(
|
||||
target.columnId,
|
||||
currentColumn,
|
||||
layers[target.layerId],
|
||||
{
|
||||
indexPattern: target.dataView,
|
||||
operations: operationDefinitionMap,
|
||||
}
|
||||
);
|
||||
return {
|
||||
...layers,
|
||||
[target.layerId]: modifiedLayer.layer,
|
||||
};
|
||||
},
|
||||
timeScalingMode: 'optional',
|
||||
paramEditor: WrappedFormulaEditor,
|
||||
|
|
|
@ -67,8 +67,8 @@ export const mathOperation: OperationDefinition<MathIndexPatternColumn, 'managed
|
|||
// TODO has to check all children
|
||||
return true;
|
||||
},
|
||||
createCopy: (layer) => {
|
||||
return { ...layer };
|
||||
createCopy: (layers) => {
|
||||
return { ...layers };
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -54,7 +54,12 @@ import type {
|
|||
GenericIndexPatternColumn,
|
||||
ReferenceBasedIndexPatternColumn,
|
||||
} from './column_types';
|
||||
import { IndexPattern, IndexPatternField, IndexPatternLayer } from '../../types';
|
||||
import {
|
||||
DataViewDragDropOperation,
|
||||
IndexPattern,
|
||||
IndexPatternField,
|
||||
IndexPatternLayer,
|
||||
} from '../../types';
|
||||
import { DateRange, LayerType } from '../../../../common';
|
||||
import { rangeOperation } from './ranges';
|
||||
import { IndexPatternDimensionEditorProps, OperationSupportMatrix } from '../../dimension_panel';
|
||||
|
@ -249,11 +254,7 @@ interface BaseOperationDefinitionProps<C extends BaseIndexPatternColumn, P = {}>
|
|||
* Based on the current column and the other updated columns, this function has to
|
||||
* return an updated column. If not implemented, the `id` function is used instead.
|
||||
*/
|
||||
onOtherColumnChanged?: (
|
||||
layer: IndexPatternLayer,
|
||||
thisColumnId: string,
|
||||
changedColumnId: string
|
||||
) => C;
|
||||
onOtherColumnChanged?: (layer: IndexPatternLayer, thisColumnId: string) => C;
|
||||
/**
|
||||
* React component for operation specific settings shown in the flyout editor
|
||||
*/
|
||||
|
@ -623,12 +624,11 @@ interface ManagedReferenceOperationDefinition<C extends BaseIndexPatternColumn>
|
|||
* root level
|
||||
*/
|
||||
createCopy: (
|
||||
layer: IndexPatternLayer,
|
||||
sourceColumnId: string,
|
||||
targetColumnId: string,
|
||||
indexPattern: IndexPattern,
|
||||
layers: Record<string, IndexPatternLayer>,
|
||||
source: DataViewDragDropOperation,
|
||||
target: DataViewDragDropOperation,
|
||||
operationDefinitionMap: Record<string, GenericOperationDefinition>
|
||||
) => IndexPatternLayer;
|
||||
) => Record<string, IndexPatternLayer>;
|
||||
}
|
||||
|
||||
interface OperationDefinitionMap<C extends BaseIndexPatternColumn, P = {}> {
|
||||
|
|
|
@ -108,9 +108,9 @@ function buildMetricOperation<T extends MetricColumn<string>>({
|
|||
(!newField.aggregationRestrictions || newField.aggregationRestrictions![type])
|
||||
);
|
||||
},
|
||||
onOtherColumnChanged: (layer, thisColumnId, changedColumnId) =>
|
||||
onOtherColumnChanged: (layer, thisColumnId) =>
|
||||
optionalTimeScaling
|
||||
? (adjustTimeScaleOnOtherColumnChange(layer, thisColumnId, changedColumnId) as T)
|
||||
? (adjustTimeScaleOnOtherColumnChange(layer, thisColumnId) as T)
|
||||
: (layer.columns[thisColumnId] as T),
|
||||
getDefaultLabel: (column, indexPattern, columns) =>
|
||||
labelLookup(getSafeName(column.sourceField, indexPattern), column),
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
import type { IndexPattern } from '../../types';
|
||||
import { useDebouncedValue } from '../../../shared_components';
|
||||
import { getFormatFromPreviousColumn, isValidNumber } from './helpers';
|
||||
import { getColumnOrder } from '../layer_helpers';
|
||||
|
||||
const defaultLabel = i18n.translate('xpack.lens.indexPattern.staticValueLabelDefault', {
|
||||
defaultMessage: 'Static value',
|
||||
|
@ -132,13 +133,21 @@ export const staticValueOperation: OperationDefinition<
|
|||
isTransferable: (column) => {
|
||||
return true;
|
||||
},
|
||||
createCopy(layer, sourceId, targetId, indexPattern, operationDefinitionMap) {
|
||||
const currentColumn = layer.columns[sourceId] as StaticValueIndexPatternColumn;
|
||||
createCopy(layers, source, target) {
|
||||
const currentColumn = layers[source.layerId].columns[
|
||||
source.columnId
|
||||
] as StaticValueIndexPatternColumn;
|
||||
const targetLayer = layers[target.layerId];
|
||||
const columns = {
|
||||
...targetLayer.columns,
|
||||
[target.columnId]: { ...currentColumn },
|
||||
};
|
||||
return {
|
||||
...layer,
|
||||
columns: {
|
||||
...layer.columns,
|
||||
[targetId]: { ...currentColumn },
|
||||
...layers,
|
||||
[target.layerId]: {
|
||||
...targetLayer,
|
||||
columns,
|
||||
columnOrder: getColumnOrder({ ...targetLayer, columns }),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
@ -325,7 +325,7 @@ export const termsOperation: OperationDefinition<TermsIndexPatternColumn, 'field
|
|||
params: newParams,
|
||||
};
|
||||
},
|
||||
onOtherColumnChanged: (layer, thisColumnId, changedColumnId) => {
|
||||
onOtherColumnChanged: (layer, thisColumnId) => {
|
||||
const columns = layer.columns;
|
||||
const currentColumn = columns[thisColumnId] as TermsIndexPatternColumn;
|
||||
if (
|
||||
|
|
|
@ -755,8 +755,7 @@ describe('terms', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
'col2',
|
||||
'col1'
|
||||
'col2'
|
||||
);
|
||||
|
||||
expect(updatedColumn).toBe(initialColumn);
|
||||
|
@ -796,8 +795,7 @@ describe('terms', () => {
|
|||
columnOrder: [],
|
||||
indexPatternId: '',
|
||||
},
|
||||
'col2',
|
||||
'col1'
|
||||
'col2'
|
||||
);
|
||||
expect(updatedColumn.params).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -843,8 +841,7 @@ describe('terms', () => {
|
|||
columnOrder: [],
|
||||
indexPatternId: '',
|
||||
},
|
||||
'col2',
|
||||
'col1'
|
||||
'col2'
|
||||
);
|
||||
expect(updatedColumn.params).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -875,8 +872,7 @@ describe('terms', () => {
|
|||
columnOrder: [],
|
||||
indexPatternId: '',
|
||||
},
|
||||
'col2',
|
||||
'col1'
|
||||
'col2'
|
||||
);
|
||||
expect(termsColumn.params).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -919,8 +915,7 @@ describe('terms', () => {
|
|||
columnOrder: [],
|
||||
indexPatternId: '',
|
||||
},
|
||||
'col2',
|
||||
'col1'
|
||||
'col2'
|
||||
);
|
||||
expect(termsColumn.params).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -951,8 +946,7 @@ describe('terms', () => {
|
|||
columnOrder: [],
|
||||
indexPatternId: '',
|
||||
},
|
||||
'col2',
|
||||
'col1'
|
||||
'col2'
|
||||
);
|
||||
expect(termsColumn.params).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -991,8 +985,7 @@ describe('terms', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
'col2',
|
||||
'col1'
|
||||
'col2'
|
||||
);
|
||||
|
||||
expect(updatedColumn.params).toEqual(
|
||||
|
|
|
@ -159,24 +159,38 @@ describe('state_helpers', () => {
|
|||
params: { window: 5 },
|
||||
references: ['formulaX0'],
|
||||
};
|
||||
|
||||
expect(
|
||||
copyColumn({
|
||||
layer: {
|
||||
indexPatternId: '',
|
||||
columnOrder: [],
|
||||
columns: {
|
||||
source,
|
||||
formulaX0: sum,
|
||||
formulaX1: movingAvg,
|
||||
formulaX2: math,
|
||||
layers: {
|
||||
layer: {
|
||||
indexPatternId: '',
|
||||
columnOrder: [],
|
||||
columns: {
|
||||
source,
|
||||
formulaX0: sum,
|
||||
formulaX1: movingAvg,
|
||||
formulaX2: math,
|
||||
},
|
||||
},
|
||||
},
|
||||
targetId: 'copy',
|
||||
sourceColumn: source,
|
||||
source: {
|
||||
column: source,
|
||||
groupId: 'one',
|
||||
columnId: 'source',
|
||||
layerId: 'layer',
|
||||
dataView: indexPattern,
|
||||
filterOperations: () => true,
|
||||
},
|
||||
target: {
|
||||
columnId: 'copy',
|
||||
groupId: 'one',
|
||||
dataView: indexPattern,
|
||||
layerId: 'layer',
|
||||
filterOperations: () => true,
|
||||
},
|
||||
shouldDeleteSource: false,
|
||||
indexPattern,
|
||||
sourceColumnId: 'source',
|
||||
})
|
||||
}).layer
|
||||
).toEqual({
|
||||
indexPatternId: '',
|
||||
columnOrder: [
|
||||
|
@ -1355,8 +1369,7 @@ describe('state_helpers', () => {
|
|||
},
|
||||
incompleteColumns: {},
|
||||
},
|
||||
'col1',
|
||||
'col2'
|
||||
'col1'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1422,8 +1435,7 @@ describe('state_helpers', () => {
|
|||
},
|
||||
incompleteColumns: {},
|
||||
}),
|
||||
'col1',
|
||||
'willBeReference'
|
||||
'col1'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -2374,8 +2386,7 @@ describe('state_helpers', () => {
|
|||
|
||||
expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith(
|
||||
{ indexPatternId: '1', columnOrder: ['col1', 'col2'], columns: { col1: termsColumn } },
|
||||
'col1',
|
||||
'col2'
|
||||
'col1'
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
TermsIndexPatternColumn,
|
||||
} from './definitions';
|
||||
import type {
|
||||
DataViewDragDropOperation,
|
||||
IndexPattern,
|
||||
IndexPatternField,
|
||||
IndexPatternLayer,
|
||||
|
@ -68,96 +69,84 @@ interface ColumnChange {
|
|||
}
|
||||
|
||||
interface ColumnCopy {
|
||||
layer: IndexPatternLayer;
|
||||
targetId: string;
|
||||
sourceColumn: GenericIndexPatternColumn;
|
||||
sourceColumnId: string;
|
||||
indexPattern: IndexPattern;
|
||||
layers: Record<string, IndexPatternLayer>;
|
||||
target: DataViewDragDropOperation;
|
||||
source: DataViewDragDropOperation;
|
||||
shouldDeleteSource?: boolean;
|
||||
}
|
||||
|
||||
export const deleteColumnInLayers = ({
|
||||
layers,
|
||||
source,
|
||||
}: {
|
||||
layers: Record<string, IndexPatternLayer>;
|
||||
source: DataViewDragDropOperation;
|
||||
}) => ({
|
||||
...layers,
|
||||
[source.layerId]: deleteColumn({
|
||||
layer: layers[source.layerId],
|
||||
columnId: source.columnId,
|
||||
indexPattern: source.dataView,
|
||||
}),
|
||||
});
|
||||
|
||||
export function copyColumn({
|
||||
layer,
|
||||
targetId,
|
||||
sourceColumn,
|
||||
layers,
|
||||
source,
|
||||
target,
|
||||
shouldDeleteSource,
|
||||
indexPattern,
|
||||
sourceColumnId,
|
||||
}: ColumnCopy): IndexPatternLayer {
|
||||
let modifiedLayer = copyReferencesRecursively(
|
||||
layer,
|
||||
sourceColumn,
|
||||
sourceColumnId,
|
||||
targetId,
|
||||
indexPattern
|
||||
);
|
||||
|
||||
if (shouldDeleteSource) {
|
||||
modifiedLayer = deleteColumn({
|
||||
layer: modifiedLayer,
|
||||
columnId: sourceColumnId,
|
||||
indexPattern,
|
||||
});
|
||||
}
|
||||
|
||||
return modifiedLayer;
|
||||
}: ColumnCopy): Record<string, IndexPatternLayer> {
|
||||
const outputLayers = createCopiedColumn(layers, target, source);
|
||||
return shouldDeleteSource
|
||||
? deleteColumnInLayers({
|
||||
layers: outputLayers,
|
||||
source,
|
||||
})
|
||||
: outputLayers;
|
||||
}
|
||||
|
||||
function copyReferencesRecursively(
|
||||
layer: IndexPatternLayer,
|
||||
sourceColumn: GenericIndexPatternColumn,
|
||||
sourceId: string,
|
||||
targetId: string,
|
||||
indexPattern: IndexPattern
|
||||
): IndexPatternLayer {
|
||||
let columns = { ...layer.columns };
|
||||
function createCopiedColumn(
|
||||
layers: Record<string, IndexPatternLayer>,
|
||||
target: DataViewDragDropOperation,
|
||||
source: DataViewDragDropOperation
|
||||
): Record<string, IndexPatternLayer> {
|
||||
const sourceLayer = layers[source.layerId];
|
||||
const sourceColumn = sourceLayer.columns[source.columnId];
|
||||
const targetLayer = layers[target.layerId];
|
||||
let columns = { ...targetLayer.columns };
|
||||
if ('references' in sourceColumn) {
|
||||
if (columns[targetId]) {
|
||||
return layer;
|
||||
}
|
||||
|
||||
const def = operationDefinitionMap[sourceColumn.operationType];
|
||||
if ('createCopy' in def) {
|
||||
// Allow managed references to recursively insert new columns
|
||||
return def.createCopy(layer, sourceId, targetId, indexPattern, operationDefinitionMap);
|
||||
return def.createCopy(layers, source, target, operationDefinitionMap); // Allow managed references to recursively insert new columns
|
||||
}
|
||||
const referenceColumns = sourceColumn.references.reduce((refs, sourceRef) => {
|
||||
const newRefId = generateId();
|
||||
return { ...refs, [newRefId]: { ...sourceLayer.columns[sourceRef] } };
|
||||
}, {});
|
||||
|
||||
sourceColumn?.references.forEach((ref, index) => {
|
||||
const newId = generateId();
|
||||
const refColumn = { ...columns[ref] };
|
||||
|
||||
// TODO: For fullReference types, now all references are hidden columns,
|
||||
// but in the future we will have references to visible columns
|
||||
// and visible columns shouldn't be copied
|
||||
const refColumnWithInnerRefs =
|
||||
'references' in refColumn
|
||||
? copyReferencesRecursively(layer, refColumn, sourceId, newId, indexPattern).columns // if a column has references, copy them too
|
||||
: { [newId]: refColumn };
|
||||
|
||||
const newColumn = columns[targetId];
|
||||
let references = [newId];
|
||||
if (newColumn && 'references' in newColumn) {
|
||||
references = newColumn.references;
|
||||
references[index] = newId;
|
||||
}
|
||||
|
||||
columns = {
|
||||
...columns,
|
||||
...refColumnWithInnerRefs,
|
||||
[targetId]: {
|
||||
...sourceColumn,
|
||||
references,
|
||||
},
|
||||
};
|
||||
});
|
||||
columns = {
|
||||
...columns,
|
||||
...referenceColumns,
|
||||
[target.columnId]: {
|
||||
...sourceColumn,
|
||||
references: Object.keys(referenceColumns),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
columns = {
|
||||
...columns,
|
||||
[targetId]: sourceColumn,
|
||||
[target.columnId]: { ...sourceColumn },
|
||||
};
|
||||
}
|
||||
|
||||
return { ...layer, columns, columnOrder: getColumnOrder({ ...layer, columns }) };
|
||||
return {
|
||||
...layers,
|
||||
[target.layerId]: adjustColumnReferences({
|
||||
...targetLayer,
|
||||
columns,
|
||||
columnOrder: getColumnOrder({ ...targetLayer, columns }),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function insertOrReplaceColumn(args: ColumnChange): IndexPatternLayer {
|
||||
|
@ -1046,8 +1035,8 @@ function addBucket(
|
|||
}
|
||||
updatedColumnOrder = reorderByGroups(
|
||||
visualizationGroups,
|
||||
targetGroup,
|
||||
updatedColumnOrder,
|
||||
targetGroup,
|
||||
addedColumnId
|
||||
);
|
||||
const tempLayer = {
|
||||
|
@ -1064,8 +1053,8 @@ function addBucket(
|
|||
|
||||
export function reorderByGroups(
|
||||
visualizationGroups: VisualizationDimensionGroupConfig[],
|
||||
targetGroup: string | undefined,
|
||||
updatedColumnOrder: string[],
|
||||
targetGroup: string | undefined,
|
||||
addedColumnId: string
|
||||
) {
|
||||
const hidesColumnGrouping =
|
||||
|
@ -1184,6 +1173,26 @@ export function updateColumnParam<C extends GenericIndexPatternColumn>({
|
|||
};
|
||||
}
|
||||
|
||||
export function adjustColumnReferences(layer: IndexPatternLayer) {
|
||||
const newColumns = { ...layer.columns };
|
||||
Object.keys(newColumns).forEach((currentColumnId) => {
|
||||
const currentColumn = newColumns[currentColumnId];
|
||||
if (currentColumn?.operationType) {
|
||||
const operationDefinition = operationDefinitionMap[currentColumn.operationType];
|
||||
newColumns[currentColumnId] = operationDefinition.onOtherColumnChanged
|
||||
? operationDefinition.onOtherColumnChanged(
|
||||
{ ...layer, columns: newColumns },
|
||||
currentColumnId
|
||||
)
|
||||
: currentColumn;
|
||||
}
|
||||
});
|
||||
return {
|
||||
...layer,
|
||||
columns: newColumns,
|
||||
};
|
||||
}
|
||||
|
||||
export function adjustColumnReferencesForChangedColumn(
|
||||
layer: IndexPatternLayer,
|
||||
changedColumnId: string
|
||||
|
@ -1196,8 +1205,7 @@ export function adjustColumnReferencesForChangedColumn(
|
|||
newColumns[currentColumnId] = operationDefinition.onOtherColumnChanged
|
||||
? operationDefinition.onOtherColumnChanged(
|
||||
{ ...layer, columns: newColumns },
|
||||
currentColumnId,
|
||||
changedColumnId
|
||||
currentColumnId
|
||||
)
|
||||
: currentColumn;
|
||||
}
|
||||
|
@ -1561,6 +1569,9 @@ export function isColumnValidAsReference({
|
|||
if (!column) return false;
|
||||
const operationType = column.operationType;
|
||||
const operationDefinition = operationDefinitionMap[operationType];
|
||||
if (!operationDefinition) {
|
||||
throw new Error('No suitable operation definition found for ' + operationType);
|
||||
}
|
||||
return (
|
||||
validation.input.includes(operationDefinition.input) &&
|
||||
maybeValidateOperations({
|
||||
|
|
|
@ -113,11 +113,7 @@ describe('time scale utils', () => {
|
|||
it('should keep column if there is no time scale', () => {
|
||||
const column = { ...baseColumn, timeScale: undefined };
|
||||
expect(
|
||||
adjustTimeScaleOnOtherColumnChange(
|
||||
{ ...baseLayer, columns: { col1: column } },
|
||||
'col1',
|
||||
'col2'
|
||||
)
|
||||
adjustTimeScaleOnOtherColumnChange({ ...baseLayer, columns: { col1: column } }, 'col1')
|
||||
).toBe(column);
|
||||
});
|
||||
|
||||
|
@ -138,14 +134,13 @@ describe('time scale utils', () => {
|
|||
} as DateHistogramIndexPatternColumn,
|
||||
},
|
||||
},
|
||||
'col1',
|
||||
'col2'
|
||||
'col1'
|
||||
)
|
||||
).toBe(baseColumn);
|
||||
});
|
||||
|
||||
it('should remove time scale if there is no date histogram', () => {
|
||||
expect(adjustTimeScaleOnOtherColumnChange(baseLayer, 'col1', 'col2')).toHaveProperty(
|
||||
expect(adjustTimeScaleOnOtherColumnChange(baseLayer, 'col1')).toHaveProperty(
|
||||
'timeScale',
|
||||
undefined
|
||||
);
|
||||
|
@ -153,22 +148,14 @@ describe('time scale utils', () => {
|
|||
|
||||
it('should remove suffix from label', () => {
|
||||
expect(
|
||||
adjustTimeScaleOnOtherColumnChange(
|
||||
{ ...baseLayer, columns: { col1: baseColumn } },
|
||||
'col1',
|
||||
'col2'
|
||||
)
|
||||
adjustTimeScaleOnOtherColumnChange({ ...baseLayer, columns: { col1: baseColumn } }, 'col1')
|
||||
).toHaveProperty('label', 'Count of records');
|
||||
});
|
||||
|
||||
it('should keep custom label', () => {
|
||||
const column = { ...baseColumn, label: 'abc', customLabel: true };
|
||||
expect(
|
||||
adjustTimeScaleOnOtherColumnChange(
|
||||
{ ...baseLayer, columns: { col1: column } },
|
||||
'col1',
|
||||
'col2'
|
||||
)
|
||||
adjustTimeScaleOnOtherColumnChange({ ...baseLayer, columns: { col1: column } }, 'col1')
|
||||
).toHaveProperty('label', 'abc');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -46,8 +46,7 @@ export function adjustTimeScaleLabelSuffix(
|
|||
|
||||
export function adjustTimeScaleOnOtherColumnChange<T extends GenericIndexPatternColumn>(
|
||||
layer: IndexPatternLayer,
|
||||
thisColumnId: string,
|
||||
changedColumnId: string
|
||||
thisColumnId: string
|
||||
): T {
|
||||
const columns = layer.columns;
|
||||
const column = columns[thisColumnId] as T;
|
||||
|
|
|
@ -24,3 +24,19 @@ export function mergeLayer({
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeLayers({
|
||||
state,
|
||||
newLayers,
|
||||
}: {
|
||||
state: IndexPatternPrivateState;
|
||||
newLayers: Record<string, IndexPatternLayer>;
|
||||
}) {
|
||||
return {
|
||||
...state,
|
||||
layers: {
|
||||
...state.layers,
|
||||
...newLayers,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import type { FieldSpec } from '@kbn/data-plugin/common';
|
|||
import type { FieldFormatParams } from '@kbn/field-formats-plugin/common';
|
||||
import type { DragDropIdentifier } from '../drag_drop/providers';
|
||||
import type { IncompleteColumn, GenericIndexPatternColumn } from './operations';
|
||||
import { DragDropOperation } from '../types';
|
||||
|
||||
export type {
|
||||
GenericIndexPatternColumn,
|
||||
|
@ -109,3 +110,8 @@ export interface IndexPatternRef {
|
|||
title: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface DataViewDragDropOperation extends DragDropOperation {
|
||||
dataView: IndexPattern;
|
||||
column?: GenericIndexPatternColumn;
|
||||
}
|
||||
|
|
|
@ -199,11 +199,18 @@ interface ChartSettings {
|
|||
};
|
||||
}
|
||||
|
||||
export type GetDropProps<T = unknown> = DatasourceDimensionDropProps<T> & {
|
||||
groupId: string;
|
||||
dragging: DragContextState['dragging'];
|
||||
prioritizedOperation?: string;
|
||||
};
|
||||
export interface GetDropPropsArgs<T = unknown> {
|
||||
state: T;
|
||||
source?: DraggingIdentifier;
|
||||
target: {
|
||||
layerId: string;
|
||||
groupId: string;
|
||||
columnId: string;
|
||||
filterOperations: (meta: OperationMetadata) => boolean;
|
||||
prioritizedOperation?: string;
|
||||
isNewColumn?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the datasource registry
|
||||
|
@ -257,9 +264,9 @@ export interface Datasource<T = unknown, P = unknown> {
|
|||
props: DatasourceLayerPanelProps<T>
|
||||
) => ((cleanupElement: Element) => void) | void;
|
||||
getDropProps: (
|
||||
props: GetDropProps<T>
|
||||
props: GetDropPropsArgs<T>
|
||||
) => { dropTypes: DropType[]; nextLabel?: string } | undefined;
|
||||
onDrop: (props: DatasourceDimensionDropHandlerProps<T>) => false | true | { deleted: string };
|
||||
onDrop: (props: DatasourceDimensionDropHandlerProps<T>) => boolean | undefined;
|
||||
/**
|
||||
* The datasource is allowed to cancel a close event on the dimension editor,
|
||||
* mainly used for formulas
|
||||
|
@ -454,16 +461,14 @@ export interface DatasourceLayerPanelProps<T> {
|
|||
activeData?: Record<string, Datatable>;
|
||||
}
|
||||
|
||||
export interface DraggedOperation extends DraggingIdentifier {
|
||||
export interface DragDropOperation {
|
||||
layerId: string;
|
||||
groupId: string;
|
||||
columnId: string;
|
||||
filterOperations: (operation: OperationMetadata) => boolean;
|
||||
}
|
||||
|
||||
export function isDraggedOperation(
|
||||
operationCandidate: unknown
|
||||
): operationCandidate is DraggedOperation {
|
||||
export function isOperation(operationCandidate: unknown): operationCandidate is DragDropOperation {
|
||||
return (
|
||||
typeof operationCandidate === 'object' &&
|
||||
operationCandidate !== null &&
|
||||
|
@ -471,10 +476,8 @@ export function isDraggedOperation(
|
|||
);
|
||||
}
|
||||
|
||||
export type DatasourceDimensionDropProps<T> = SharedDimensionProps & {
|
||||
layerId: string;
|
||||
groupId: string;
|
||||
columnId: string;
|
||||
export interface DatasourceDimensionDropProps<T> {
|
||||
target: DragDropOperation;
|
||||
state: T;
|
||||
setState: StateSetter<
|
||||
T,
|
||||
|
@ -484,10 +487,10 @@ export type DatasourceDimensionDropProps<T> = SharedDimensionProps & {
|
|||
}
|
||||
>;
|
||||
dimensionGroups: VisualizationDimensionGroupConfig[];
|
||||
};
|
||||
}
|
||||
|
||||
export type DatasourceDimensionDropHandlerProps<T> = DatasourceDimensionDropProps<T> & {
|
||||
droppedItem: unknown;
|
||||
export type DatasourceDimensionDropHandlerProps<S> = DatasourceDimensionDropProps<S> & {
|
||||
source: DragDropIdentifier;
|
||||
dropType: DropType;
|
||||
};
|
||||
|
||||
|
@ -851,7 +854,17 @@ export interface Visualization<T = unknown> {
|
|||
* look at its internal state to determine which dimension is being affected.
|
||||
*/
|
||||
removeDimension: (props: VisualizationDimensionChangeProps<T>) => T;
|
||||
|
||||
/**
|
||||
* Allow defining custom behavior for the visualization when the drop action occurs.
|
||||
*/
|
||||
onDrop?: (props: {
|
||||
prevState: T;
|
||||
target: DragDropOperation;
|
||||
source: DragDropIdentifier;
|
||||
frame: FramePublicAPI;
|
||||
dropType: DropType;
|
||||
group?: VisualizationDimensionGroupConfig;
|
||||
}) => T;
|
||||
/**
|
||||
* Update the configuration for the visualization. This is used to update the state
|
||||
*/
|
||||
|
|
|
@ -118,6 +118,207 @@ export const getAnnotationsSupportedLayer = (
|
|||
};
|
||||
};
|
||||
|
||||
const getDefaultAnnotationConfig = (id: string, timestamp: string): EventAnnotationConfig => ({
|
||||
label: defaultAnnotationLabel,
|
||||
key: {
|
||||
type: 'point_in_time',
|
||||
timestamp,
|
||||
},
|
||||
icon: 'triangle',
|
||||
id,
|
||||
});
|
||||
|
||||
const createCopiedAnnotation = (
|
||||
newId: string,
|
||||
timestamp: string,
|
||||
source?: EventAnnotationConfig
|
||||
): EventAnnotationConfig => {
|
||||
if (!source) {
|
||||
return getDefaultAnnotationConfig(newId, timestamp);
|
||||
}
|
||||
return {
|
||||
...source,
|
||||
id: newId,
|
||||
};
|
||||
};
|
||||
|
||||
export const onAnnotationDrop: Visualization<XYState>['onDrop'] = ({
|
||||
prevState,
|
||||
frame,
|
||||
source,
|
||||
target,
|
||||
dropType,
|
||||
}) => {
|
||||
const targetLayer = prevState.layers.find((l) => l.layerId === target.layerId);
|
||||
const sourceLayer = prevState.layers.find((l) => l.layerId === source.layerId);
|
||||
if (
|
||||
!targetLayer ||
|
||||
!isAnnotationsLayer(targetLayer) ||
|
||||
!sourceLayer ||
|
||||
!isAnnotationsLayer(sourceLayer)
|
||||
) {
|
||||
return prevState;
|
||||
}
|
||||
const targetAnnotation = targetLayer.annotations.find(({ id }) => id === target.columnId);
|
||||
const sourceAnnotation = sourceLayer.annotations.find(({ id }) => id === source.columnId);
|
||||
switch (dropType) {
|
||||
case 'reorder':
|
||||
if (!targetAnnotation || !sourceAnnotation || source.layerId !== target.layerId) {
|
||||
return prevState;
|
||||
}
|
||||
const newAnnotations = targetLayer.annotations.filter((c) => c.id !== sourceAnnotation.id);
|
||||
const targetPosition = newAnnotations.findIndex((c) => c.id === targetAnnotation.id);
|
||||
const targetIndex = targetLayer.annotations.indexOf(sourceAnnotation);
|
||||
const sourceIndex = targetLayer.annotations.indexOf(targetAnnotation);
|
||||
newAnnotations.splice(
|
||||
targetIndex < sourceIndex ? targetPosition + 1 : targetPosition,
|
||||
0,
|
||||
sourceAnnotation
|
||||
);
|
||||
return {
|
||||
...prevState,
|
||||
layers: prevState.layers.map((l) =>
|
||||
l.layerId === target.layerId ? { ...targetLayer, annotations: newAnnotations } : l
|
||||
),
|
||||
};
|
||||
case 'swap_compatible':
|
||||
if (!targetAnnotation || !sourceAnnotation) {
|
||||
return prevState;
|
||||
}
|
||||
return {
|
||||
...prevState,
|
||||
layers: prevState.layers.map((l): XYLayerConfig => {
|
||||
if (!isAnnotationsLayer(l) || !isAnnotationsLayer(targetLayer)) {
|
||||
return l;
|
||||
}
|
||||
if (l.layerId === target.layerId) {
|
||||
return {
|
||||
...targetLayer,
|
||||
annotations: [
|
||||
...targetLayer.annotations.map(
|
||||
(a): EventAnnotationConfig => (a === targetAnnotation ? sourceAnnotation : a)
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
if (l.layerId === source.layerId) {
|
||||
return {
|
||||
...sourceLayer,
|
||||
annotations: [
|
||||
...sourceLayer.annotations.map(
|
||||
(a): EventAnnotationConfig => (a === sourceAnnotation ? targetAnnotation : a)
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
return l;
|
||||
}),
|
||||
};
|
||||
case 'replace_compatible':
|
||||
if (!targetAnnotation || !sourceAnnotation) {
|
||||
return prevState;
|
||||
}
|
||||
|
||||
return {
|
||||
...prevState,
|
||||
layers: prevState.layers.map((l) => {
|
||||
if (l.layerId === source.layerId) {
|
||||
return {
|
||||
...sourceLayer,
|
||||
annotations: sourceLayer.annotations.filter((a) => a !== sourceAnnotation),
|
||||
};
|
||||
}
|
||||
if (l.layerId === target.layerId) {
|
||||
return {
|
||||
...targetLayer,
|
||||
annotations: [
|
||||
...targetLayer.annotations.map((a) =>
|
||||
a === targetAnnotation ? sourceAnnotation : a
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
return l;
|
||||
}),
|
||||
};
|
||||
case 'duplicate_compatible':
|
||||
if (targetAnnotation) {
|
||||
return prevState;
|
||||
}
|
||||
return {
|
||||
...prevState,
|
||||
layers: prevState.layers.map(
|
||||
(l): XYLayerConfig =>
|
||||
l.layerId === target.layerId
|
||||
? {
|
||||
...targetLayer,
|
||||
annotations: [
|
||||
...targetLayer.annotations,
|
||||
createCopiedAnnotation(
|
||||
target.columnId,
|
||||
getStaticDate(getDataLayers(prevState.layers), frame),
|
||||
sourceAnnotation
|
||||
),
|
||||
],
|
||||
}
|
||||
: l
|
||||
),
|
||||
};
|
||||
case 'replace_duplicate_compatible':
|
||||
if (!targetAnnotation) {
|
||||
return prevState;
|
||||
}
|
||||
return {
|
||||
...prevState,
|
||||
layers: prevState.layers.map((l) => {
|
||||
if (l.layerId === target.layerId) {
|
||||
return {
|
||||
...targetLayer,
|
||||
annotations: [
|
||||
...targetLayer.annotations.map((a) =>
|
||||
a === targetAnnotation
|
||||
? createCopiedAnnotation(
|
||||
target.columnId,
|
||||
getStaticDate(getDataLayers(prevState.layers), frame),
|
||||
sourceAnnotation
|
||||
)
|
||||
: a
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
return l;
|
||||
}),
|
||||
};
|
||||
case 'move_compatible':
|
||||
if (targetAnnotation || !sourceAnnotation) {
|
||||
return prevState;
|
||||
}
|
||||
|
||||
return {
|
||||
...prevState,
|
||||
layers: prevState.layers.map((l): XYLayerConfig => {
|
||||
if (l.layerId === source.layerId) {
|
||||
return {
|
||||
...sourceLayer,
|
||||
annotations: sourceLayer.annotations.filter((a) => a !== sourceAnnotation),
|
||||
};
|
||||
}
|
||||
if (l.layerId === target.layerId) {
|
||||
return {
|
||||
...targetLayer,
|
||||
annotations: [...targetLayer.annotations, sourceAnnotation],
|
||||
};
|
||||
}
|
||||
return l;
|
||||
}),
|
||||
};
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
return prevState;
|
||||
};
|
||||
|
||||
export const setAnnotationsDimension: Visualization<XYState>['setDimension'] = ({
|
||||
prevState,
|
||||
layerId,
|
||||
|
@ -125,46 +326,30 @@ export const setAnnotationsDimension: Visualization<XYState>['setDimension'] = (
|
|||
previousColumn,
|
||||
frame,
|
||||
}) => {
|
||||
const foundLayer = prevState.layers.find((l) => l.layerId === layerId);
|
||||
if (!foundLayer || !isAnnotationsLayer(foundLayer)) {
|
||||
const targetLayer = prevState.layers.find((l) => l.layerId === layerId);
|
||||
if (!targetLayer || !isAnnotationsLayer(targetLayer)) {
|
||||
return prevState;
|
||||
}
|
||||
const inputAnnotations = foundLayer.annotations as XYAnnotationLayerConfig['annotations'];
|
||||
const currentConfig = inputAnnotations?.find(({ id }) => id === columnId);
|
||||
const previousConfig = previousColumn
|
||||
? inputAnnotations?.find(({ id }) => id === previousColumn)
|
||||
const sourceAnnotation = previousColumn
|
||||
? targetLayer.annotations?.find(({ id }) => id === previousColumn)
|
||||
: undefined;
|
||||
|
||||
let resultAnnotations = [...inputAnnotations] as XYAnnotationLayerConfig['annotations'];
|
||||
|
||||
if (!currentConfig) {
|
||||
resultAnnotations.push({
|
||||
label: defaultAnnotationLabel,
|
||||
key: {
|
||||
type: 'point_in_time',
|
||||
timestamp: getStaticDate(getDataLayers(prevState.layers), frame),
|
||||
},
|
||||
icon: 'triangle',
|
||||
...previousConfig,
|
||||
id: columnId,
|
||||
});
|
||||
} else if (currentConfig && previousConfig) {
|
||||
// TODO: reordering should not live in setDimension, to be refactored
|
||||
resultAnnotations = inputAnnotations.filter((c) => c.id !== previousConfig.id);
|
||||
const targetPosition = resultAnnotations.findIndex((c) => c.id === currentConfig.id);
|
||||
const targetIndex = inputAnnotations.indexOf(previousConfig);
|
||||
const sourceIndex = inputAnnotations.indexOf(currentConfig);
|
||||
resultAnnotations.splice(
|
||||
targetIndex < sourceIndex ? targetPosition + 1 : targetPosition,
|
||||
0,
|
||||
previousConfig
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...prevState,
|
||||
layers: prevState.layers.map((l) =>
|
||||
l.layerId === layerId ? { ...foundLayer, annotations: resultAnnotations } : l
|
||||
l.layerId === layerId
|
||||
? {
|
||||
...targetLayer,
|
||||
annotations: [
|
||||
...targetLayer.annotations,
|
||||
createCopiedAnnotation(
|
||||
columnId,
|
||||
getStaticDate(getDataLayers(prevState.layers), frame),
|
||||
sourceAnnotation
|
||||
),
|
||||
],
|
||||
}
|
||||
: l
|
||||
),
|
||||
};
|
||||
};
|
||||
|
@ -224,7 +409,6 @@ export const getAnnotationsConfiguration = ({
|
|||
defaultMessage: 'Annotations require a time based chart to work. Add a date histogram.',
|
||||
}),
|
||||
required: false,
|
||||
requiresPreviousColumnOnDuplicate: true,
|
||||
supportsMoreColumns: true,
|
||||
supportFieldFormat: false,
|
||||
enableDimensionEditor: true,
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
checkScaleOperation,
|
||||
getAxisName,
|
||||
getDataLayers,
|
||||
getReferenceLayers,
|
||||
isNumericMetric,
|
||||
isReferenceLayer,
|
||||
} from './visualization_helpers';
|
||||
|
@ -342,7 +343,10 @@ export const setReferenceDimension: Visualization<XYState>['setDimension'] = ({
|
|||
newLayer.accessors = [...newLayer.accessors.filter((a) => a !== columnId), columnId];
|
||||
const hasYConfig = newLayer.yConfig?.some(({ forAccessor }) => forAccessor === columnId);
|
||||
const previousYConfig = previousColumn
|
||||
? newLayer.yConfig?.find(({ forAccessor }) => forAccessor === previousColumn)
|
||||
? getReferenceLayers(prevState.layers)
|
||||
.map(({ yConfig }) => yConfig)
|
||||
.flat()
|
||||
?.find((yConfig) => yConfig?.forAccessor === previousColumn)
|
||||
: false;
|
||||
if (!hasYConfig) {
|
||||
const axisMode: YAxisMode =
|
||||
|
|
|
@ -476,7 +476,7 @@ describe('xy_visualization', () => {
|
|||
});
|
||||
it('should copy previous column if passed and assign a new id', () => {
|
||||
expect(
|
||||
xyVisualization.setDimension({
|
||||
xyVisualization.onDrop!({
|
||||
frame,
|
||||
prevState: {
|
||||
...exampleState(),
|
||||
|
@ -488,10 +488,20 @@ describe('xy_visualization', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
layerId: 'annotation',
|
||||
groupId: 'xAnnotation',
|
||||
previousColumn: 'an2',
|
||||
columnId: 'newColId',
|
||||
dropType: 'duplicate_compatible',
|
||||
source: {
|
||||
layerId: 'annotation',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an2',
|
||||
id: 'an2',
|
||||
humanData: { label: 'an2' },
|
||||
},
|
||||
target: {
|
||||
layerId: 'annotation',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'newColId',
|
||||
filterOperations: Boolean,
|
||||
},
|
||||
}).layers[0]
|
||||
).toEqual({
|
||||
layerId: 'annotation',
|
||||
|
@ -501,7 +511,7 @@ describe('xy_visualization', () => {
|
|||
});
|
||||
it('should reorder a dimension to a annotation layer', () => {
|
||||
expect(
|
||||
xyVisualization.setDimension({
|
||||
xyVisualization.onDrop!({
|
||||
frame,
|
||||
prevState: {
|
||||
...exampleState(),
|
||||
|
@ -513,10 +523,21 @@ describe('xy_visualization', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
layerId: 'annotation',
|
||||
groupId: 'xAnnotation',
|
||||
previousColumn: 'an2',
|
||||
columnId: 'an1',
|
||||
source: {
|
||||
layerId: 'annotation',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an2',
|
||||
id: 'an2',
|
||||
humanData: { label: 'label' },
|
||||
filterOperations: () => true,
|
||||
},
|
||||
target: {
|
||||
layerId: 'annotation',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an1',
|
||||
filterOperations: () => true,
|
||||
},
|
||||
dropType: 'reorder',
|
||||
}).layers[0]
|
||||
).toEqual({
|
||||
layerId: 'annotation',
|
||||
|
@ -524,6 +545,199 @@ describe('xy_visualization', () => {
|
|||
annotations: [exampleAnnotation2, exampleAnnotation],
|
||||
});
|
||||
});
|
||||
|
||||
it('should duplicate the annotations and replace the target in another annotation layer', () => {
|
||||
expect(
|
||||
xyVisualization.onDrop!({
|
||||
frame,
|
||||
prevState: {
|
||||
...exampleState(),
|
||||
layers: [
|
||||
{
|
||||
layerId: 'first',
|
||||
layerType: 'annotations',
|
||||
annotations: [exampleAnnotation],
|
||||
},
|
||||
{
|
||||
layerId: 'second',
|
||||
layerType: 'annotations',
|
||||
annotations: [exampleAnnotation2],
|
||||
},
|
||||
],
|
||||
},
|
||||
source: {
|
||||
layerId: 'first',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an1',
|
||||
id: 'an1',
|
||||
humanData: { label: 'label' },
|
||||
filterOperations: () => true,
|
||||
},
|
||||
target: {
|
||||
layerId: 'second',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an2',
|
||||
filterOperations: () => true,
|
||||
},
|
||||
dropType: 'replace_duplicate_compatible',
|
||||
}).layers
|
||||
).toEqual([
|
||||
{
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
annotations: [exampleAnnotation],
|
||||
},
|
||||
{
|
||||
layerId: 'second',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
annotations: [{ ...exampleAnnotation, id: 'an2' }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('should swap the annotations between layers', () => {
|
||||
expect(
|
||||
xyVisualization.onDrop!({
|
||||
frame,
|
||||
prevState: {
|
||||
...exampleState(),
|
||||
layers: [
|
||||
{
|
||||
layerId: 'first',
|
||||
layerType: 'annotations',
|
||||
annotations: [exampleAnnotation],
|
||||
},
|
||||
{
|
||||
layerId: 'second',
|
||||
layerType: 'annotations',
|
||||
annotations: [exampleAnnotation2],
|
||||
},
|
||||
],
|
||||
},
|
||||
source: {
|
||||
layerId: 'first',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an1',
|
||||
id: 'an1',
|
||||
humanData: { label: 'label' },
|
||||
filterOperations: () => true,
|
||||
},
|
||||
target: {
|
||||
layerId: 'second',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an2',
|
||||
filterOperations: () => true,
|
||||
},
|
||||
dropType: 'swap_compatible',
|
||||
}).layers
|
||||
).toEqual([
|
||||
{
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
annotations: [exampleAnnotation2],
|
||||
},
|
||||
{
|
||||
layerId: 'second',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
annotations: [exampleAnnotation],
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('should replace the target in another annotation layer', () => {
|
||||
expect(
|
||||
xyVisualization.onDrop!({
|
||||
frame,
|
||||
prevState: {
|
||||
...exampleState(),
|
||||
layers: [
|
||||
{
|
||||
layerId: 'first',
|
||||
layerType: 'annotations',
|
||||
annotations: [exampleAnnotation],
|
||||
},
|
||||
{
|
||||
layerId: 'second',
|
||||
layerType: 'annotations',
|
||||
annotations: [exampleAnnotation2],
|
||||
},
|
||||
],
|
||||
},
|
||||
source: {
|
||||
layerId: 'first',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an1',
|
||||
id: 'an1',
|
||||
humanData: { label: 'label' },
|
||||
filterOperations: () => true,
|
||||
},
|
||||
target: {
|
||||
layerId: 'second',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an2',
|
||||
filterOperations: () => true,
|
||||
},
|
||||
dropType: 'replace_compatible',
|
||||
}).layers
|
||||
).toEqual([
|
||||
{
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
annotations: [],
|
||||
},
|
||||
{
|
||||
layerId: 'second',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
annotations: [exampleAnnotation],
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('should move compatible to another annotation layer', () => {
|
||||
expect(
|
||||
xyVisualization.onDrop!({
|
||||
frame,
|
||||
prevState: {
|
||||
...exampleState(),
|
||||
layers: [
|
||||
{
|
||||
layerId: 'first',
|
||||
layerType: 'annotations',
|
||||
annotations: [exampleAnnotation],
|
||||
},
|
||||
{
|
||||
layerId: 'second',
|
||||
layerType: 'annotations',
|
||||
annotations: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
source: {
|
||||
layerId: 'first',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an1',
|
||||
id: 'an1',
|
||||
humanData: { label: 'label' },
|
||||
filterOperations: () => true,
|
||||
},
|
||||
target: {
|
||||
layerId: 'second',
|
||||
groupId: 'xAnnotation',
|
||||
columnId: 'an2',
|
||||
filterOperations: () => true,
|
||||
},
|
||||
dropType: 'move_compatible',
|
||||
}).layers
|
||||
).toEqual([
|
||||
{
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
annotations: [],
|
||||
},
|
||||
{
|
||||
layerId: 'second',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
annotations: [exampleAnnotation],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import { getSuggestions } from './xy_suggestions';
|
|||
import { XyToolbar } from './xy_config_panel';
|
||||
import { DimensionEditor } from './xy_config_panel/dimension_editor';
|
||||
import { LayerHeader } from './xy_config_panel/layer_header';
|
||||
import type { Visualization, AccessorConfig, FramePublicAPI } from '../types';
|
||||
import { Visualization, AccessorConfig, FramePublicAPI } from '../types';
|
||||
import { State, visualizationTypes, XYSuggestion, XYLayerConfig, XYDataLayerConfig } from './types';
|
||||
import { layerTypes } from '../../common';
|
||||
import { isHorizontalChart } from './state_helpers';
|
||||
|
@ -45,6 +45,7 @@ import {
|
|||
getAnnotationsSupportedLayer,
|
||||
setAnnotationsDimension,
|
||||
getUniqueLabels,
|
||||
onAnnotationDrop,
|
||||
} from './annotations/helpers';
|
||||
import {
|
||||
checkXAccessorCompatibility,
|
||||
|
@ -71,6 +72,7 @@ import { ReferenceLinePanel } from './xy_config_panel/reference_line_config_pane
|
|||
import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel';
|
||||
import { DimensionTrigger } from '../shared_components/dimension_trigger';
|
||||
import { defaultAnnotationLabel } from './annotations/helpers';
|
||||
import { onDropForVisualization } from '../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils';
|
||||
|
||||
export const getXyVisualization = ({
|
||||
datatableUtilities,
|
||||
|
@ -303,6 +305,20 @@ export const getXyVisualization = ({
|
|||
return getFirstDataLayer(state.layers)?.palette;
|
||||
},
|
||||
|
||||
onDrop(props) {
|
||||
const targetLayer: XYLayerConfig | undefined = props.prevState.layers.find(
|
||||
(l) => l.layerId === props.target.layerId
|
||||
);
|
||||
if (!targetLayer) {
|
||||
throw new Error('target layer should exist');
|
||||
}
|
||||
|
||||
if (isAnnotationsLayer(targetLayer)) {
|
||||
return onAnnotationDrop?.(props) || props.prevState;
|
||||
}
|
||||
return onDropForVisualization(props, this);
|
||||
},
|
||||
|
||||
setDimension(props) {
|
||||
const { prevState, layerId, columnId, groupId } = props;
|
||||
|
||||
|
|
|
@ -334,6 +334,7 @@ export function validateLayersForDimension(
|
|||
|
||||
export const isNumericMetric = (op: OperationMetadata) =>
|
||||
!op.isBucketed && op.dataType === 'number';
|
||||
|
||||
export const isNumericDynamicMetric = (op: OperationMetadata) =>
|
||||
isNumericMetric(op) && !op.isStaticValue;
|
||||
export const isBucketed = (op: OperationMetadata) => op.isBucketed;
|
||||
|
|
|
@ -209,48 +209,14 @@
|
|||
"xpack.lens.dragDrop.announce.cancelled": "Mouvement annulé. {label} revenu à sa position initiale",
|
||||
"xpack.lens.dragDrop.announce.cancelledItem": "Mouvement annulé. {label} revenu au groupe {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.combine.short": " Maintenir la touche Contrôle enfoncée pour combiner",
|
||||
"xpack.lens.dragDrop.announce.dropped.combineCompatible": "Combinaison de {label} avec {dropGroupLabel} à la position {dropPosition} et de {dropLabel} avec {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.combineIncompatible": "Conversion de {label} en {nextLabel} dans le groupe {groupLabel} à la position {position} et combinaison avec {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition}",
|
||||
"xpack.lens.dragDrop.announce.dropped.duplicated": "{label} dupliqué dans le groupe {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.duplicateIncompatible": "Copie de {label} convertie en {nextLabel} et ajoutée au groupe {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.moveCompatible": "{label} déplacé dans le groupe {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.moveIncompatible": "{label} converti en {nextLabel} et déplacé dans le groupe {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.reordered": "{label} réorganisé dans le groupe {groupLabel} de la position {prevPosition} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.replaceDuplicateIncompatible": "Copie de {label} convertie en {nextLabel} et {dropLabel} remplacé dans le groupe {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.replaceIncompatible": "{label} converti en {nextLabel} et {dropLabel} remplacé dans le groupe {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.swapCompatible": "{label} déplacé dans {dropGroupLabel} à la position {dropPosition} et {dropLabel} dans {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.swapIncompatible": "{label} converti en {nextLabel} dans le groupe {groupLabel} à la position {position} et permuté avec {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition}",
|
||||
"xpack.lens.dragDrop.announce.droppedDefault": "{label} ajouté dans le groupe {dropGroupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.droppedNoPosition": "{label} ajouté à {dropLabel}",
|
||||
"xpack.lens.dragDrop.announce.duplicate.short": " Maintenez la touche Alt ou Option enfoncée pour dupliquer.",
|
||||
"xpack.lens.dragDrop.announce.duplicated.combine": "Combiner {dropLabel} avec {label} dans {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.duplicated.replace": "{dropLabel} remplacé par {label} dans {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.duplicated.replaceDuplicateCompatible": "{dropLabel} remplacé par une copie de {label} dans {groupLabel} à la position {position}",
|
||||
"xpack.lens.dragDrop.announce.lifted": "{label} levé",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combine": "Combinez {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition} avec {label}. Appuyez sur la barre d’espace ou sur Entrée pour combiner.",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combineCompatible": "Combinez {label} dans le groupe {groupLabel} à la position {position} avec {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition}. Maintenez la touche Contrôle enfoncée et appuyez sur la barre d’espace ou sur Entrée pour combiner.",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combineIncompatible": "Convertissez {label} en {nextLabel} dans le groupe {groupLabel} à la position {position} et combinez avec {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition}. Maintenez la touche Contrôle enfoncée et appuyez sur la barre d’espace ou sur Entrée pour combiner.",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combineMain": "Vous faites glisser {label} à partir de {groupLabel} à la position {position} sur {dropLabel} à partir du groupe {dropGroupLabel} à la position {dropPosition}. Appuyer sur la barre d’espace ou sur Entrée pour combiner {dropLabel} avec {label}.{duplicateCopy}{swapCopy}{combineCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.default": "Ajoutez {label} au groupe {dropGroupLabel} à la position {position}. Appuyer sur la barre d'espace ou sur Entrée pour ajouter",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.defaultNoPosition": "Ajoutez {label} à {dropLabel}. Appuyer sur la barre d'espace ou sur Entrée pour ajouter",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.duplicated": "Dupliquez {label} dans le groupe {dropGroupLabel} à la position {position}. Maintenir la touche Alt ou Option enfoncée et appuyer sur la barre d'espace ou sur Entrée pour dupliquer",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.duplicatedInGroup": "Dupliquez {label} dans le groupe {dropGroupLabel} à la position {position}. Appuyer sur la barre d'espace ou sur Entrée pour dupliquer",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.duplicateIncompatible": "Convertissez la copie de {label} en {nextLabel} et ajoutez-la au groupe {groupLabel} à la position {position}. Maintenir la touche Alt ou Option enfoncée et appuyer sur la barre d'espace ou sur Entrée pour dupliquer",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveCompatible": "Déplacez {label} dans le groupe {dropGroupLabel} à la position {dropPosition}. Appuyer sur la barre d'espace ou sur Entrée pour déplacer",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveCompatibleMain": "Vous faites glisser {label} de {groupLabel} à la position {position} vers la position {dropPosition} dans le groupe {dropGroupLabel}. Appuyez sur la barre d'espace ou sur Entrée pour déplacer.{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveIncompatible": "Convertissez {label} en {nextLabel} et déplacez-le dans le groupe {dropGroupLabel} à la position {dropPosition}. Appuyer sur la barre d'espace ou sur Entrée pour déplacer",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveIncompatibleMain": "Vous faites glisser {label} de {groupLabel} à la position {position} vers la position {dropPosition} dans le groupe {dropGroupLabel}. Appuyez sur la barre d'espace ou sur Entrée pour convertir {label} en {nextLabel} et déplacer.{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.noSelected": "Aucune cible sélectionnée. Utiliser les touches fléchées pour sélectionner une cible",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.reordered": "Réorganisez {label} dans le groupe {groupLabel} de la position {prevPosition} à la position {position}. Appuyer sur la barre d'espace ou sur Entrée pour réorganiser",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.reorderedBack": "{label} revenu à sa position initiale {prevPosition}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replace": "Remplacez {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition} avec {label}. Appuyez sur la barre d'espace ou sur Entrée pour remplacer.",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceDuplicateCompatible": "Dupliquez {label} et remplacez {dropLabel} dans {groupLabel} à la position {position}. Maintenir la touche Alt ou Option enfoncée et appuyer sur la barre d'espace ou sur Entrée pour dupliquer et remplacer",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceDuplicateIncompatible": "Convertissez la copie de {label} en {nextLabel} et remplacez {dropLabel} dans le groupe {groupLabel} à la position {position}. Maintenir la touche Alt ou Option enfoncée et appuyer sur la barre d'espace ou sur Entrée pour dupliquer et remplacer",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceIncompatible": "Convertissez {label} en {nextLabel} et remplacez {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition}. Appuyer sur la barre d'espace ou sur Entrée pour remplacer",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceIncompatibleMain": "Vous faites glisser {label} à partir de {groupLabel} à la position {position} sur {dropLabel} à partir du groupe {dropGroupLabel} à la position {dropPosition}. Appuyer sur la barre d'espace ou sur Entrée pour convertir {label} en {nextLabel} et remplacer {dropLabel}.{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceMain": "Vous faites glisser {label} à partir de {groupLabel} à la position {position} sur {dropLabel} à partir du groupe {dropGroupLabel} à la position {dropPosition}. Appuyer sur la barre d'espace ou sur Entrée pour remplacer {dropLabel} par {label}.{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.swapCompatible": "Permutez {label} dans le groupe {groupLabel} à la position {position} avec {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition}. Maintenir la touche Maj enfoncée tout en appuyant sur la barre d'espace ou sur Entrée pour permuter",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.swapIncompatible": "Convertir {label} en {nextLabel} dans le groupe {groupLabel} à la position {position} et permutez avec {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition}. Maintenir la touche Maj enfoncée tout en appuyant sur la barre d'espace ou sur Entrée pour permuter",
|
||||
"xpack.lens.dragDrop.announce.swap.short": " Maintenez la touche Maj enfoncée pour permuter.",
|
||||
"xpack.lens.dragDrop.combine": "Combiner",
|
||||
"xpack.lens.dragDrop.control": "Contrôle",
|
||||
|
|
|
@ -214,43 +214,14 @@
|
|||
"xpack.lens.dragDrop.announce.cancelled": "移動がキャンセルされました。{label}は初期位置に戻りました",
|
||||
"xpack.lens.dragDrop.announce.cancelledItem": "移動がキャンセルされました。{label}は位置{position}の{groupLabel}グループに戻りました",
|
||||
"xpack.lens.dragDrop.announce.combine.short": " Ctrlを押しながら結合します",
|
||||
"xpack.lens.dragDrop.announce.dropped.combineCompatible": "位置{dropPosition}で{label}を{dropGroupLabel}に移動し、位置{position}で{dropLabel}を {groupLabel}グループに結合しました",
|
||||
"xpack.lens.dragDrop.announce.dropped.combineIncompatible": "位置{position}で{label}を{groupLabel}の{nextLabel}に変換し、位置{dropPosition}で{dropGroupLabel}グループの{dropLabel}と結合しました",
|
||||
"xpack.lens.dragDrop.announce.dropped.duplicated": "位置{position}の{groupLabel}グループで{label}を複製しました",
|
||||
"xpack.lens.dragDrop.announce.dropped.duplicateIncompatible": "{label}のコピーを{nextLabel}に変換し、位置{position}の{groupLabel}グループに追加しました",
|
||||
"xpack.lens.dragDrop.announce.dropped.moveCompatible": "位置{position}の{groupLabel}グループに{label}を移動しました",
|
||||
"xpack.lens.dragDrop.announce.dropped.moveIncompatible": "{label}を{nextLabel}に変換し、位置{position}の{groupLabel}グループに移動しました",
|
||||
"xpack.lens.dragDrop.announce.dropped.reordered": "{groupLabel}グループの{label}を位置{prevPosition}から位置{position}に並べ替えました",
|
||||
"xpack.lens.dragDrop.announce.dropped.replaceDuplicateIncompatible": "{label}のコピーを{nextLabel}に変換し、位置{position}の{groupLabel}グループで{dropLabel}を置き換えました",
|
||||
"xpack.lens.dragDrop.announce.dropped.replaceIncompatible": "{label}を{nextLabel}に変換し、位置{position}の{groupLabel}グループで{dropLabel}を置き換えました",
|
||||
"xpack.lens.dragDrop.announce.dropped.swapCompatible": "位置{dropPosition}で{label}を{dropGroupLabel}に移動し、位置{position}で{dropLabel}を {groupLabel}グループに移動しました",
|
||||
"xpack.lens.dragDrop.announce.dropped.swapIncompatible": "位置{position}で{label}を{groupLabel}の{nextLabel}に変換し、位置{dropPosition}で{dropGroupLabel}グループの{dropLabel}と入れ替えました",
|
||||
"xpack.lens.dragDrop.announce.droppedDefault": "位置{position}の{dropGroupLabel}グループで{label}を追加しました",
|
||||
"xpack.lens.dragDrop.announce.droppedNoPosition": "{label}を{dropLabel}に追加しました",
|
||||
"xpack.lens.dragDrop.announce.duplicate.short": " AltキーまたはOptionを押し続けると複製します。",
|
||||
"xpack.lens.dragDrop.announce.duplicated.combine": "位置{position}の{groupLabel}で{dropLabel}を{label}と結合しました",
|
||||
"xpack.lens.dragDrop.announce.duplicated.replace": "位置{position}の{groupLabel}で{dropLabel}を{label}に置き換えました",
|
||||
"xpack.lens.dragDrop.announce.duplicated.replaceDuplicateCompatible": "位置{position}の{groupLabel}で{dropLabel}を{label}のコピーに置き換えました",
|
||||
"xpack.lens.dragDrop.announce.lifted": "{label}を上げました",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combineCompatible": "位置{position}の{groupLabel}の{label}を、位置{dropPosition}の{dropGroupLabel}グループの{dropLabel}と結合します。Ctrlキーを押しながらスペースバーまたはEnterキーを押すと、結合します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combineIncompatible": "位置{position}で{label}を{groupLabel}の{nextLabel}に変換し、位置{dropPosition}で{dropGroupLabel}グループの{dropLabel}と結合します。Ctrlキーを押しながらスペースバーまたはEnterキーを押すと、結合します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.default": "位置{position}の{dropGroupLabel}グループに{label}を追加しました。スペースまたはEnterを押して追加します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.defaultNoPosition": "{label}を{dropLabel}に追加します。スペースまたはEnterを押して追加します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.duplicated": "位置{position}の{dropGroupLabel}グループに{label}を複製しました。AltまたはOptionを押しながらスペースバーまたはEnterキーを押すと、複製します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.duplicatedInGroup": "位置{position}の{dropGroupLabel}グループに{label}を複製しました。スペースまたはEnterを押して複製します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.duplicateIncompatible": "{label}のコピーを{nextLabel}に変換し、位置{position}で{groupLabel}グループに移動します。AltまたはOptionを押しながらスペースバーまたはEnterキーを押すと、複製します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveCompatibleMain": "位置{position}で{groupLabel}の{label}を{dropGroupLabel}グループの位置{dropPosition}にドラッグしています。スペースバーまたはEnterキーを押すと移動します。{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveIncompatible": "{label}を{nextLabel}に変換し、位置{dropPosition}で{dropGroupLabel}グループに移動します。スペースまたはEnterを押して移動します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveIncompatibleMain": "位置{position}で{groupLabel}の{label}を{dropGroupLabel}グループの位置{dropPosition}にドラッグしています。スペースバーまたはEnterキーを押して、{label}を{nextLabel}に変換して移動します。{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.noSelected": "対象が選択されていません。矢印キーを使用して対象を選択してください",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.reordered": "{groupLabel}グループの{label}を位置{prevPosition}から位置{position}に並べ替えます。スペースまたはEnterを押して並べ替えます",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.reorderedBack": "{label}は初期位置{prevPosition}に戻りました",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceDuplicateCompatible": "位置{position}で{label}を複製し、{groupLabel}グループで{dropLabel}を置き換えます。AltまたはOptionを押しながらスペースバーまたはEnterキーを押すと、複製して置換します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceDuplicateIncompatible": "{label}のコピーを{nextLabel}に変換し、位置{position}で{groupLabel}グループの{dropLabel}を置き換えます。AltまたはOptionを押しながらスペースバーまたはEnterキーを押すと、複製して置換します",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceIncompatibleMain": "位置{position}の{groupLabel}の{label}を、位置{dropPosition}の{dropGroupLabel}グループの{dropLabel}にドラッグしています。スペースバーまたはEnterキーを押して、{label}を{nextLabel}に変換して、{dropLabel}を置き換えます。{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceMain": "位置{position}の{groupLabel}の{label}を、位置{dropPosition}の{dropGroupLabel}グループの{dropLabel}にドラッグしています。スペースまたはEnterを押して、{dropLabel}を{label}で置き換えます。{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.swapCompatible": "位置{position}の{groupLabel}の{label}を、位置{dropPosition}の{dropGroupLabel}グループの{dropLabel}と入れ替えます。Shiftキーを押しながらスペースバーまたはEnterキーを押すと、入れ替えます",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.swapIncompatible": "位置{position}で{label}を{groupLabel}の{nextLabel}に変換し、位置{dropPosition}で{dropGroupLabel}グループの{dropLabel}と入れ替えます。Shiftキーを押しながらスペースバーまたはEnterキーを押すと、入れ替えます",
|
||||
"xpack.lens.dragDrop.announce.swap.short": " Shiftキーを押すと入れ替えます。",
|
||||
"xpack.lens.dragDrop.combine": "結合",
|
||||
"xpack.lens.dragDrop.control": "Control",
|
||||
|
|
|
@ -214,48 +214,14 @@
|
|||
"xpack.lens.dragDrop.announce.cancelled": "移动已取消。{label} 将返回至其初始位置",
|
||||
"xpack.lens.dragDrop.announce.cancelledItem": "移动已取消。{label} 返回至 {groupLabel} 组中的位置 {position}",
|
||||
"xpack.lens.dragDrop.announce.combine.short": " 按住 Control 键组合",
|
||||
"xpack.lens.dragDrop.announce.dropped.combineCompatible": "已将 {label} 组合到 {dropGroupLabel} 中的位置 {dropPosition} 并将 {dropLabel} 组合到组 {groupLabel} 中的位置 {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.combineIncompatible": "已将 {label} 转换为组 {groupLabel} 中位置 {position} 上的 {nextLabel},并与组 {dropGroupLabel} 中位置 {dropPosition} 上的 {dropLabel} 组合",
|
||||
"xpack.lens.dragDrop.announce.dropped.duplicated": "已在 {groupLabel} 组中的位置 {position} 复制 {label}",
|
||||
"xpack.lens.dragDrop.announce.dropped.duplicateIncompatible": "已将 {label} 的副本转换为 {nextLabel} 并添加 {groupLabel} 组中的位置 {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.moveCompatible": "已将 {label} 移到 {groupLabel} 组中的位置 {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.moveIncompatible": "已将 {label} 转换为 {nextLabel} 并移到 {groupLabel} 组中的位置 {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.reordered": "已将 {groupLabel} 组中的 {label} 从位置 {prevPosition} 重新排到位置 {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.replaceDuplicateIncompatible": "已将 {label} 的副本转换为 {nextLabel} 并替换了 {groupLabel} 组中位置 {position} 上的 {dropLabel}",
|
||||
"xpack.lens.dragDrop.announce.dropped.replaceIncompatible": "已将 {label} 转换为 {nextLabel} 并替换了 {groupLabel} 组中位置 {position} 上的 {dropLabel}",
|
||||
"xpack.lens.dragDrop.announce.dropped.swapCompatible": "已将 {label} 移至 {dropGroupLabel} 中的位置 {dropPosition} 并将 {dropLabel} 移至组 {groupLabel} 中的位置 {position}",
|
||||
"xpack.lens.dragDrop.announce.dropped.swapIncompatible": "已将 {label} 转换为组 {groupLabel} 中位置 {position} 上的 {nextLabel},并与组 {dropGroupLabel} 中位置 {dropPosition} 上的 {dropLabel} 交换",
|
||||
"xpack.lens.dragDrop.announce.droppedDefault": "已将 {label} 添加到 {dropGroupLabel} 组中的位置 {position}",
|
||||
"xpack.lens.dragDrop.announce.droppedNoPosition": "已将 {label} 添加到 {dropLabel}",
|
||||
"xpack.lens.dragDrop.announce.duplicate.short": " 按住 alt 或 option 键以复制。",
|
||||
"xpack.lens.dragDrop.announce.duplicated.combine": "将 {dropLabel} 与 {groupLabel} 中位置 {position} 上的 {label} 组合",
|
||||
"xpack.lens.dragDrop.announce.duplicated.replace": "已将 {groupLabel} 组中位置 {position} 上的 {dropLabel} 替换为 {label}",
|
||||
"xpack.lens.dragDrop.announce.duplicated.replaceDuplicateCompatible": "已将 {groupLabel} 组中位置 {position} 上的 {dropLabel} 替换为 {label} 的副本",
|
||||
"xpack.lens.dragDrop.announce.lifted": "已提升 {label}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combine": "将 {dropGroupLabel} 组中位置 {dropPosition} 上的 {dropLabel} 与 {label} 组合。按空格键或 enter 键组合。",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combineCompatible": "将组 {groupLabel} 中位置 {position} 上的 {label} 与组 {dropGroupLabel} 中位置 {dropPosition} 上的 {dropLabel} 组合。按住 Control 键并按空格键或 enter 键组合",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combineIncompatible": "将 {label} 转换为组 {groupLabel} 中位置 {position} 上的 {nextLabel},并与组 {dropGroupLabel} 中位置 {dropPosition} 上的 {dropLabel} 组合。按住 Control 键并按空格键或 enter 键组合",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.combineMain": "您正将 {groupLabel} 中位置 {position} 上的{label} 拖到 {dropGroupLabel} 组中位置 {dropPosition} 上的 {dropLabel}。按空格键或 enter 键以将 {dropLabel} 与 {label} 组合。{duplicateCopy}{swapCopy}{combineCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.default": "将 {label} 添加到 {dropGroupLabel} 组中的位置 {position}。按空格键或 enter 键添加",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.defaultNoPosition": "将 {label} 添加到 {dropLabel}。按空格键或 enter 键添加",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.duplicated": "将 {label} 复制到 {dropGroupLabel} 组中的位置 {position}。按住 Alt 或 Option 并按空格键或 enter 键以复制",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.duplicatedInGroup": "将 {label} 复制到 {dropGroupLabel} 组中的位置 {position}。按空格键或 enter 键复制",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.duplicateIncompatible": "将 {label} 转换为 {nextLabel} 并移到 {groupLabel} 组中的位置 {position}。按住 Alt 或 Option 并按空格键或 enter 键以复制",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveCompatible": "将 {label} 移至 {dropGroupLabel} 组中的位置 {dropPosition}。按空格键或 enter 键移动",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveCompatibleMain": "您正将 {groupLabel} 中位置 {position} 上的{label} 拖到 {dropGroupLabel} 组中的位置 {dropPosition} 上。按空格键或 enter 键移动。{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveIncompatible": "将 {label} 转换为 {nextLabel} 并移到 {dropGroupLabel} 组中的位置 {dropPosition}。按空格键或 enter 键移动",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.moveIncompatibleMain": "您正将 {groupLabel} 中位置 {position} 上的{label} 拖到 {dropGroupLabel} 组中的位置 {dropPosition} 上。按空格键或 enter 键以将 {label} 转换为 {nextLabel} 并移动。{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.noSelected": "未选择任何目标。使用箭头键选择目标",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.reordered": "将 {groupLabel} 组中的 {label} 从位置 {prevPosition} 重新排到位置 {position}。按空格键或 enter 键重新排列",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.reorderedBack": "{label} 已返回至其初始位置 {prevPosition}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replace": "将 {dropGroupLabel} 组中位置 {dropPosition} 上的 {dropLabel} 替换为 {label}。按空格键或 enter 键替换。",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceDuplicateCompatible": "复制 {label} 并替换 {groupLabel} 中位置 {position} 上的 {dropLabel}。按住 Alt 或 Option 并按空格键或 enter 键以复制并替换",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceDuplicateIncompatible": "将 {label} 的副本转换为 {nextLabel} 并替换 {groupLabel} 组中位置 {position} 上的 {dropLabel}。按住 Alt 或 Option 并按空格键或 enter 键以复制并替换",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceIncompatible": "将 {label} 转换为 {nextLabel} 并替换 {dropGroupLabel} 组中位置 {dropPosition} 上的 {dropLabel}。按空格键或 enter 键替换",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceIncompatibleMain": "您正将 {groupLabel} 中位置 {position} 上的{label} 拖到 {dropGroupLabel} 组中位置 {dropPosition} 上的 {dropLabel}。按空格键或 enter 键以将 {label} 转换为 {nextLabel} 并替换 {dropLabel}。{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.replaceMain": "您正将 {groupLabel} 中位置 {position} 上的{label} 拖到 {dropGroupLabel} 组中位置 {dropPosition} 上的 {dropLabel}。按空格键或 enter 键以将 {dropLabel} 替换为 {label}。{duplicateCopy}{swapCopy}",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.swapCompatible": "将组 {groupLabel} 中位置 {position} 上的 {label} 与组 {dropGroupLabel} 中位置 {dropPosition} 上的 {dropLabel} 交换。按住 Shift 键并按空格键或 enter 键交换",
|
||||
"xpack.lens.dragDrop.announce.selectedTarget.swapIncompatible": "将 {label} 转换为组 {groupLabel} 中位置 {position} 上的 {nextLabel},并与组 {dropGroupLabel} 中位置 {dropPosition} 上的 {dropLabel} 交换。按住 Shift 键并按空格键或 enter 键交换",
|
||||
"xpack.lens.dragDrop.announce.swap.short": " 按住 Shift 键交换。",
|
||||
"xpack.lens.dragDrop.combine": "组合",
|
||||
"xpack.lens.dragDrop.control": "Control 键",
|
||||
|
|
|
@ -146,23 +146,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.lens.createLayer();
|
||||
|
||||
await PageObjects.lens.switchToVisualization('area');
|
||||
await PageObjects.lens.configureDimension(
|
||||
{
|
||||
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'date_histogram',
|
||||
field: '@timestamp',
|
||||
},
|
||||
1
|
||||
);
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'date_histogram',
|
||||
field: '@timestamp',
|
||||
});
|
||||
|
||||
await PageObjects.lens.configureDimension(
|
||||
{
|
||||
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'median',
|
||||
field: 'bytes',
|
||||
},
|
||||
1
|
||||
);
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'median',
|
||||
field: 'bytes',
|
||||
});
|
||||
await a11y.testAppSnapshot();
|
||||
});
|
||||
|
||||
|
|
|
@ -125,23 +125,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(await PageObjects.lens.hasChartSwitchWarning('line')).to.eql(false);
|
||||
|
||||
await PageObjects.lens.switchToVisualization('line');
|
||||
await PageObjects.lens.configureDimension(
|
||||
{
|
||||
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'terms',
|
||||
field: 'geo.src',
|
||||
},
|
||||
1
|
||||
);
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'terms',
|
||||
field: 'geo.src',
|
||||
});
|
||||
|
||||
await PageObjects.lens.configureDimension(
|
||||
{
|
||||
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'median',
|
||||
field: 'bytes',
|
||||
},
|
||||
1
|
||||
);
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'median',
|
||||
field: 'bytes',
|
||||
});
|
||||
|
||||
expect(await PageObjects.lens.getLayerCount()).to.eql(2);
|
||||
await PageObjects.lens.removeLayer();
|
||||
|
@ -308,23 +302,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
await PageObjects.lens.createLayer();
|
||||
|
||||
await PageObjects.lens.configureDimension(
|
||||
{
|
||||
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'terms',
|
||||
field: 'geo.src',
|
||||
},
|
||||
1
|
||||
);
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'terms',
|
||||
field: 'geo.src',
|
||||
});
|
||||
|
||||
await PageObjects.lens.configureDimension(
|
||||
{
|
||||
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'average',
|
||||
field: 'bytes',
|
||||
},
|
||||
1
|
||||
);
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'average',
|
||||
field: 'bytes',
|
||||
});
|
||||
|
||||
await PageObjects.lens.save('twolayerchart');
|
||||
await testSubjects.click('lnsSuggestion-asDonut > lnsSuggestion');
|
||||
|
|
|
@ -73,10 +73,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should allow to transpose columns', async () => {
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsDatatable_rows > lns-dimensionTrigger',
|
||||
'lnsDatatable_columns > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsDatatable_rows > lns-dimensionTrigger',
|
||||
to: 'lnsDatatable_columns > lns-empty-dimension',
|
||||
});
|
||||
expect(await PageObjects.lens.getDatatableHeaderText(0)).to.equal('@timestamp per 3 hours');
|
||||
expect(await PageObjects.lens.getDatatableHeaderText(1)).to.equal(
|
||||
'169.228.188.120 › Average of bytes'
|
||||
|
|
|
@ -183,23 +183,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(await PageObjects.lens.hasChartSwitchWarning('line')).to.eql(false);
|
||||
|
||||
await PageObjects.lens.switchToVisualization('line');
|
||||
await PageObjects.lens.configureDimension(
|
||||
{
|
||||
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'date_histogram',
|
||||
field: '@timestamp',
|
||||
},
|
||||
1
|
||||
);
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'date_histogram',
|
||||
field: '@timestamp',
|
||||
});
|
||||
|
||||
await PageObjects.lens.configureDimension(
|
||||
{
|
||||
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'median',
|
||||
field: 'bytes',
|
||||
},
|
||||
1
|
||||
);
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'median',
|
||||
field: 'bytes',
|
||||
});
|
||||
await PageObjects.lens.saveAndReturn();
|
||||
|
||||
await panelActions.openContextMenu();
|
||||
|
|
|
@ -51,10 +51,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('should duplicate the style when duplicating an annotation and group them in the chart', async () => {
|
||||
// drag and drop to the empty field to generate a duplicate
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsXY_xAnnotationsPanel > lns-dimensionTrigger',
|
||||
'lnsXY_xAnnotationsPanel > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsXY_xAnnotationsPanel > lns-dimensionTrigger',
|
||||
to: 'lnsXY_xAnnotationsPanel > lns-empty-dimension',
|
||||
});
|
||||
|
||||
await (
|
||||
await find.byCssSelector(
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getPageObjects }: FtrProviderContext) {
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']);
|
||||
|
||||
const listingTable = getService('listingTable');
|
||||
const xyChartContainer = 'xyVisChart';
|
||||
|
||||
describe('lens drag and drop tests', () => {
|
||||
|
@ -72,10 +74,10 @@ export default function ({ getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')
|
||||
).to.eql(['Top 3 values of clientip']);
|
||||
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsXY_xDimensionPanel > lns-dimensionTrigger',
|
||||
'lnsXY_splitDimensionPanel > lns-dimensionTrigger'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lns-layerPanel-0 > lnsXY_xDimensionPanel > lns-dimensionTrigger',
|
||||
to: 'lns-layerPanel-0 > lnsXY_splitDimensionPanel > lns-dimensionTrigger',
|
||||
});
|
||||
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql(
|
||||
[]
|
||||
|
@ -90,10 +92,10 @@ export default function ({ getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')
|
||||
).to.eql(['Top 3 values of @message.raw']);
|
||||
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsXY_splitDimensionPanel > lns-dimensionTrigger',
|
||||
'lnsXY_yDimensionPanel > lns-dimensionTrigger'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsXY_splitDimensionPanel > lns-dimensionTrigger',
|
||||
to: 'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
});
|
||||
|
||||
expect(
|
||||
await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel')
|
||||
|
@ -106,14 +108,14 @@ export default function ({ getPageObjects }: FtrProviderContext) {
|
|||
]);
|
||||
});
|
||||
it('should duplicate the column when dragging to empty dimension in the same group', async () => {
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
'lnsXY_yDimensionPanel > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
'lnsXY_yDimensionPanel > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
to: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
});
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
to: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
});
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_yDimensionPanel')).to.eql([
|
||||
'Unique count of @message.raw',
|
||||
'Unique count of @message.raw [1]',
|
||||
|
@ -121,10 +123,10 @@ export default function ({ getPageObjects }: FtrProviderContext) {
|
|||
]);
|
||||
});
|
||||
it('should move duplicated column to non-compatible dimension group', async () => {
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
'lnsXY_xDimensionPanel > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
to: 'lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
});
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_yDimensionPanel')).to.eql([
|
||||
'Unique count of @message.raw',
|
||||
'Unique count of @message.raw [1]',
|
||||
|
@ -340,5 +342,132 @@ export default function ({ getPageObjects }: FtrProviderContext) {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dropping between layers', () => {
|
||||
it('should move the column', async () => {
|
||||
await PageObjects.visualize.gotoVisualizationLandingPage();
|
||||
await listingTable.searchForItemWithName('lnsXYvis');
|
||||
await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis');
|
||||
await PageObjects.lens.goToTimeRange();
|
||||
|
||||
await PageObjects.lens.createLayer('data');
|
||||
|
||||
await PageObjects.lens.dragDimensionToExtraDropType(
|
||||
'lns-layerPanel-0 > lnsXY_xDimensionPanel > lns-dimensionTrigger',
|
||||
'lns-layerPanel-1 > lnsXY_xDimensionPanel',
|
||||
'duplicate'
|
||||
);
|
||||
|
||||
await PageObjects.lens.assertFocusedDimension('@timestamp [1]');
|
||||
|
||||
await PageObjects.lens.dragDimensionToExtraDropType(
|
||||
'lns-layerPanel-0 > lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
'lns-layerPanel-1 > lnsXY_yDimensionPanel',
|
||||
'duplicate'
|
||||
);
|
||||
|
||||
await PageObjects.lens.assertFocusedDimension('Average of bytes [1]');
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-0')).to.eql([
|
||||
'@timestamp',
|
||||
'Average of bytes',
|
||||
'Top values of ip',
|
||||
]);
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-1')).to.eql([
|
||||
'@timestamp [1]',
|
||||
'Average of bytes [1]',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should move formula to empty dimension', async () => {
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
operation: 'formula',
|
||||
formula: `moving_average(average(bytes), window=5`,
|
||||
});
|
||||
await PageObjects.lens.dragDimensionToExtraDropType(
|
||||
'lns-layerPanel-0 > lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
'lns-layerPanel-1 > lnsXY_yDimensionPanel',
|
||||
'duplicate'
|
||||
);
|
||||
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-0')).to.eql([
|
||||
'@timestamp',
|
||||
'moving_average(average(bytes), window=5)',
|
||||
'Top 3 values of ip',
|
||||
]);
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-1')).to.eql([
|
||||
'@timestamp [1]',
|
||||
'moving_average(average(bytes), window=5) [1]',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should replace formula with another formula', async () => {
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
operation: 'formula',
|
||||
formula: `sum(bytes) + 5`,
|
||||
});
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lns-layerPanel-0 > lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
to: 'lns-layerPanel-1 > lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
});
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-0')).to.eql([
|
||||
'@timestamp',
|
||||
'Top 3 values of ip',
|
||||
]);
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-1')).to.eql([
|
||||
'@timestamp [1]',
|
||||
'moving_average(average(bytes), window=5)',
|
||||
]);
|
||||
});
|
||||
it('swaps dimensions', async () => {
|
||||
await PageObjects.visualize.gotoVisualizationLandingPage();
|
||||
await listingTable.searchForItemWithName('lnsXYvis');
|
||||
await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis');
|
||||
await PageObjects.lens.goToTimeRange();
|
||||
|
||||
await PageObjects.lens.createLayer('data');
|
||||
await PageObjects.lens.dragFieldToDimensionTrigger(
|
||||
'bytes',
|
||||
'lns-layerPanel-0 > lnsXY_yDimensionPanel > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragFieldToDimensionTrigger(
|
||||
'bytes',
|
||||
'lns-layerPanel-1 > lnsXY_splitDimensionPanel > lns-empty-dimension'
|
||||
);
|
||||
|
||||
await PageObjects.lens.dragDimensionToExtraDropType(
|
||||
'lns-layerPanel-1 > lnsXY_splitDimensionPanel > lns-dimensionTrigger',
|
||||
'lns-layerPanel-0 > lnsXY_splitDimensionPanel',
|
||||
'swap'
|
||||
);
|
||||
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-0')).to.eql([
|
||||
'@timestamp',
|
||||
'Average of bytes',
|
||||
'Median of bytes',
|
||||
'bytes',
|
||||
]);
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-1')).to.eql([
|
||||
'Top 3 values of ip',
|
||||
]);
|
||||
});
|
||||
it('can combine dimensions', async () => {
|
||||
await PageObjects.lens.dragDimensionToExtraDropType(
|
||||
'lns-layerPanel-0 > lnsXY_splitDimensionPanel > lns-dimensionTrigger',
|
||||
'lns-layerPanel-1 > lnsXY_splitDimensionPanel',
|
||||
'combine'
|
||||
);
|
||||
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-0')).to.eql([
|
||||
'@timestamp',
|
||||
'Average of bytes',
|
||||
'Median of bytes',
|
||||
]);
|
||||
expect(await PageObjects.lens.getDimensionTriggersTexts('lns-layerPanel-1')).to.eql([
|
||||
'Top values of ip + 1 other',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -56,10 +56,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.lens.waitForMissingDataViewWarning();
|
||||
await PageObjects.lens.openDimensionEditor('lnsXY_yDimensionPanel > lns-dimensionTrigger');
|
||||
await PageObjects.lens.closeDimensionEditor();
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
'lnsXY_yDimensionPanel > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsXY_yDimensionPanel > lns-dimensionTrigger',
|
||||
to: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
});
|
||||
await PageObjects.lens.switchFirstLayerIndexPattern('log*');
|
||||
await PageObjects.lens.waitForMissingDataViewWarningDisappear();
|
||||
await PageObjects.lens.waitForEmptyWorkspace();
|
||||
|
|
|
@ -205,10 +205,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
await PageObjects.lens.closeDimensionEditor();
|
||||
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsDatatable_metrics > lns-dimensionTrigger',
|
||||
'lnsDatatable_metrics > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsDatatable_metrics > lns-dimensionTrigger',
|
||||
to: 'lnsDatatable_metrics > lns-empty-dimension',
|
||||
});
|
||||
expect(await PageObjects.lens.getDatatableCellText(1, 1)).to.eql('222,420');
|
||||
expect(await PageObjects.lens.getDatatableCellText(1, 2)).to.eql('222,420');
|
||||
});
|
||||
|
@ -249,15 +249,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
await PageObjects.lens.createLayer('referenceLine');
|
||||
|
||||
await PageObjects.lens.configureDimension(
|
||||
{
|
||||
dimension: 'lnsXY_yReferenceLineLeftPanel > lns-dimensionTrigger',
|
||||
operation: 'formula',
|
||||
formula: `count()`,
|
||||
keepOpen: true,
|
||||
},
|
||||
1
|
||||
);
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lns-layerPanel-1 > lnsXY_yReferenceLineLeftPanel > lns-dimensionTrigger',
|
||||
operation: 'formula',
|
||||
formula: `count()`,
|
||||
keepOpen: true,
|
||||
});
|
||||
|
||||
await PageObjects.lens.switchToStaticValue();
|
||||
await PageObjects.lens.closeDimensionEditor();
|
||||
|
@ -280,10 +277,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
formula: `0`,
|
||||
});
|
||||
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsDatatable_metrics > lns-dimensionTrigger',
|
||||
'lnsDatatable_metrics > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsDatatable_metrics > lns-dimensionTrigger',
|
||||
to: 'lnsDatatable_metrics > lns-empty-dimension',
|
||||
});
|
||||
expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('0');
|
||||
expect(await PageObjects.lens.getDatatableCellText(0, 1)).to.eql('0');
|
||||
});
|
||||
|
|
|
@ -86,10 +86,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.lens.closeDimensionEditor();
|
||||
|
||||
// drag and drop it to the left axis
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsXY_yReferenceLineLeftPanel > lns-dimensionTrigger',
|
||||
'lnsXY_yReferenceLineRightPanel > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsXY_yReferenceLineLeftPanel > lns-dimensionTrigger',
|
||||
to: 'lnsXY_yReferenceLineRightPanel > lns-empty-dimension',
|
||||
});
|
||||
|
||||
await testSubjects.click('lnsXY_yReferenceLineRightPanel > lns-dimensionTrigger');
|
||||
expect(
|
||||
|
@ -100,10 +100,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('should duplicate also the original style when duplicating a reference line', async () => {
|
||||
// drag and drop to the empty field to generate a duplicate
|
||||
await PageObjects.lens.dragDimensionToDimension(
|
||||
'lnsXY_yReferenceLineRightPanel > lns-dimensionTrigger',
|
||||
'lnsXY_yReferenceLineRightPanel > lns-empty-dimension'
|
||||
);
|
||||
await PageObjects.lens.dragDimensionToDimension({
|
||||
from: 'lnsXY_yReferenceLineRightPanel > lns-dimensionTrigger',
|
||||
to: 'lnsXY_yReferenceLineRightPanel > lns-empty-dimension',
|
||||
});
|
||||
|
||||
await (
|
||||
await find.byCssSelector(
|
||||
|
|
|
@ -111,22 +111,19 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
* @param opts.field - the desired field for the dimension
|
||||
* @param layerIndex - the index of the layer
|
||||
*/
|
||||
async configureDimension(
|
||||
opts: {
|
||||
dimension: string;
|
||||
operation: string;
|
||||
field?: string;
|
||||
isPreviousIncompatible?: boolean;
|
||||
keepOpen?: boolean;
|
||||
palette?: string;
|
||||
formula?: string;
|
||||
disableEmptyRows?: boolean;
|
||||
},
|
||||
layerIndex = 0
|
||||
) {
|
||||
async configureDimension(opts: {
|
||||
dimension: string;
|
||||
operation: string;
|
||||
field?: string;
|
||||
isPreviousIncompatible?: boolean;
|
||||
keepOpen?: boolean;
|
||||
palette?: string;
|
||||
formula?: string;
|
||||
disableEmptyRows?: boolean;
|
||||
}) {
|
||||
await retry.try(async () => {
|
||||
if (!(await testSubjects.exists('lns-indexPattern-dimensionContainerClose'))) {
|
||||
await testSubjects.click(`lns-layerPanel-${layerIndex} > ${opts.dimension}`);
|
||||
await testSubjects.click(opts.dimension);
|
||||
}
|
||||
await testSubjects.existOrFail('lns-indexPattern-dimensionContainerClose');
|
||||
});
|
||||
|
@ -450,8 +447,9 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
* @param from - the selector of the dimension being moved
|
||||
* @param to - the selector of the dimension being dropped to
|
||||
* */
|
||||
async dragDimensionToDimension(from: string, to: string) {
|
||||
async dragDimensionToDimension({ from, to }: { from: string; to: string }) {
|
||||
await find.existsByCssSelector(from);
|
||||
await find.existsByCssSelector(to);
|
||||
await browser.html5DragAndDrop(
|
||||
testSubjects.getCssSelector(from),
|
||||
testSubjects.getCssSelector(to)
|
||||
|
@ -891,7 +889,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
return dimensionTexts[index];
|
||||
},
|
||||
/**
|
||||
* Gets label of all dimension triggers in dimension group
|
||||
* Gets label of all dimension triggers in an element
|
||||
*
|
||||
* @param dimension - the selector of the dimension
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue