Skip to content

Commit 2ff046c

Browse files
committed
feat: extract dnd
1 parent bea7667 commit 2ff046c

File tree

4 files changed

+188
-151
lines changed

4 files changed

+188
-151
lines changed

x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx

Lines changed: 53 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,14 @@ import './filters.scss';
88
import React, { MouseEventHandler, useState } from 'react';
99
import { omit } from 'lodash';
1010
import { i18n } from '@kbn/i18n';
11-
import {
12-
EuiDragDropContext,
13-
EuiDraggable,
14-
EuiDroppable,
15-
euiDragDropReorder,
16-
EuiButtonEmpty,
17-
EuiFormRow,
18-
EuiLink,
19-
htmlIdGenerator,
20-
} from '@elastic/eui';
11+
import { EuiFormRow, EuiLink, htmlIdGenerator } from '@elastic/eui';
2112
import { updateColumnParam } from '../../../state_helpers';
2213
import { OperationDefinition } from '../index';
2314
import { FieldBasedIndexPatternColumn } from '../column_types';
2415
import { FilterPopover } from './filter_popover';
2516
import { IndexPattern } from '../../../types';
2617
import { Query, esKuery, esQuery } from '../../../../../../../../src/plugins/data/public';
27-
import { CustomBucketContainer } from '../shared_components';
18+
import { NewBucketButton, DragDropBuckets, DraggableBucketContainer } from '../shared_components';
2819

2920
const generateId = htmlIdGenerator();
3021

@@ -33,10 +24,11 @@ export interface Filter {
3324
input: Query;
3425
label: string;
3526
}
27+
3628
export interface FilterValue {
29+
id: string;
3730
input: Query;
3831
label: string;
39-
id: string;
4032
}
4133

4234
const customQueryLabel = i18n.translate('xpack.lens.indexPattern.customQuery', {
@@ -69,11 +61,6 @@ export const isQueryValid = (input: Query, indexPattern: IndexPattern) => {
6961
}
7062
};
7163

72-
interface DraggableLocation {
73-
droppableId: string;
74-
index: number;
75-
}
76-
7764
export interface FiltersIndexPatternColumn extends FieldBasedIndexPatternColumn {
7865
operationType: 'filters';
7966
params: {
@@ -215,92 +202,67 @@ export const FilterList = ({
215202
)
216203
);
217204

218-
const onDragEnd = ({
219-
source,
220-
destination,
221-
}: {
222-
source?: DraggableLocation;
223-
destination?: DraggableLocation;
224-
}) => {
225-
if (source && destination) {
226-
const items = euiDragDropReorder(localFilters, source.index, destination.index);
227-
updateFilters(items);
228-
}
229-
};
230-
231205
return (
232206
<>
233-
<EuiDragDropContext onDragEnd={onDragEnd} onDragStart={() => setIsOpenByCreation(false)}>
234-
<EuiDroppable droppableId="FILTERS_DROPPABLE_AREA" spacing="s">
235-
{localFilters?.map((filter: FilterValue, idx: number) => {
236-
const { input, label, id } = filter;
237-
const isInvalid = !isQueryValid(input, indexPattern);
207+
<DragDropBuckets
208+
onDragEnd={updateFilters}
209+
onDragStart={() => setIsOpenByCreation(false)}
210+
droppableId="FILTERS_DROPPABLE_AREA"
211+
items={localFilters}
212+
>
213+
{localFilters?.map((filter: FilterValue, idx: number) => {
214+
const isInvalid = !isQueryValid(filter.input, indexPattern);
238215

239-
return (
240-
<EuiDraggable
241-
spacing="m"
242-
key={id}
243-
index={idx}
244-
draggableId={id}
245-
disableInteractiveElementBlocking
246-
>
247-
{(provided) => (
248-
<CustomBucketContainer
249-
isInvalid={isInvalid}
250-
invalidMessage={i18n.translate('xpack.lens.indexPattern.filters.isInvalid', {
251-
defaultMessage: 'This query is invalid',
216+
return (
217+
<DraggableBucketContainer
218+
id={filter.id}
219+
key={filter.id}
220+
idx={idx}
221+
isInvalid={isInvalid}
222+
invalidMessage={i18n.translate('xpack.lens.indexPattern.filters.isInvalid', {
223+
defaultMessage: 'This query is invalid',
224+
})}
225+
onRemoveClick={() => onRemoveFilter(filter.id)}
226+
removeTitle={i18n.translate('xpack.lens.indexPattern.filters.removeCustomQuery', {
227+
defaultMessage: 'Remove custom query',
228+
})}
229+
>
230+
<FilterPopover
231+
data-test-subj="indexPattern-filters-existingFilterContainer"
232+
isOpenByCreation={idx === localFilters.length - 1 && isOpenByCreation}
233+
setIsOpenByCreation={setIsOpenByCreation}
234+
indexPattern={indexPattern}
235+
filter={filter}
236+
setFilter={(f: FilterValue) => {
237+
onChangeValue(f.id, f.input, f.label);
238+
}}
239+
Button={({ onClick }: { onClick: MouseEventHandler }) => (
240+
<EuiLink
241+
className="lnsFiltersOperation__popoverButton"
242+
data-test-subj="indexPattern-filters-existingFilterTrigger"
243+
onClick={onClick}
244+
color={isInvalid ? 'danger' : 'text'}
245+
title={i18n.translate('xpack.lens.indexPattern.filters.clickToEdit', {
246+
defaultMessage: 'Click to edit',
252247
})}
253-
onRemoveClick={() => onRemoveFilter(filter.id)}
254-
removeTitle={i18n.translate(
255-
'xpack.lens.indexPattern.filters.removeCustomQuery',
256-
{
257-
defaultMessage: 'Remove custom query',
258-
}
259-
)}
260248
>
261-
<FilterPopover
262-
data-test-subj="indexPattern-filters-existingFilterContainer"
263-
isOpenByCreation={idx === localFilters.length - 1 && isOpenByCreation}
264-
setIsOpenByCreation={setIsOpenByCreation}
265-
indexPattern={indexPattern}
266-
filter={filter}
267-
setFilter={(f: FilterValue) => {
268-
onChangeValue(f.id, f.input, f.label);
269-
}}
270-
Button={({ onClick }: { onClick: MouseEventHandler }) => (
271-
<EuiLink
272-
className="lnsFiltersOperation__popoverButton"
273-
data-test-subj="indexPattern-filters-existingFilterTrigger"
274-
onClick={onClick}
275-
color={isInvalid ? 'danger' : 'text'}
276-
title={i18n.translate('xpack.lens.indexPattern.filters.clickToEdit', {
277-
defaultMessage: 'Click to edit',
278-
})}
279-
>
280-
{label || input.query || defaultLabel}
281-
</EuiLink>
282-
)}
283-
/>
284-
</CustomBucketContainer>
249+
{filter.label || filter.input.query || defaultLabel}
250+
</EuiLink>
285251
)}
286-
</EuiDraggable>
287-
);
288-
})}
289-
</EuiDroppable>
290-
</EuiDragDropContext>
291-
292-
<EuiButtonEmpty
293-
size="xs"
294-
iconType="plusInCircle"
252+
/>
253+
</DraggableBucketContainer>
254+
);
255+
})}
256+
</DragDropBuckets>
257+
<NewBucketButton
295258
onClick={() => {
296259
onAddFilter();
297260
setIsOpenByCreation(true);
298261
}}
299-
>
300-
{i18n.translate('xpack.lens.indexPattern.filters.addCustomQuery', {
262+
label={i18n.translate('xpack.lens.indexPattern.filters.addCustomQuery', {
301263
defaultMessage: 'Add a custom query',
302264
})}
303-
</EuiButtonEmpty>
265+
/>
304266
</>
305267
);
306268
};
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React from 'react';
8+
import { i18n } from '@kbn/i18n';
9+
import {
10+
EuiFlexGroup,
11+
EuiFlexItem,
12+
EuiPanel,
13+
EuiButtonIcon,
14+
EuiIcon,
15+
EuiDragDropContext,
16+
euiDragDropReorder,
17+
EuiDraggable,
18+
EuiDroppable,
19+
EuiButtonEmpty,
20+
} from '@elastic/eui';
21+
22+
export interface BucketContainerProps {
23+
isInvalid?: boolean;
24+
invalidMessage: string;
25+
onRemoveClick: () => void;
26+
removeTitle: string;
27+
children: React.ReactNode;
28+
dataTestSubj?: string;
29+
}
30+
31+
export const BucketContainer = ({
32+
isInvalid,
33+
invalidMessage,
34+
onRemoveClick,
35+
removeTitle,
36+
children,
37+
dataTestSubj,
38+
}: BucketContainerProps) => {
39+
return (
40+
<EuiPanel paddingSize="none" data-test-subj={dataTestSubj}>
41+
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
42+
<EuiFlexItem grow={false}>{/* Empty for spacing */}</EuiFlexItem>
43+
<EuiFlexItem grow={false}>
44+
<EuiIcon
45+
size="s"
46+
color={isInvalid ? 'danger' : 'subdued'}
47+
type={isInvalid ? 'alert' : 'grab'}
48+
title={
49+
isInvalid
50+
? invalidMessage
51+
: i18n.translate('xpack.lens.customBucketContainer.dragToReorder', {
52+
defaultMessage: 'Drag to reorder',
53+
})
54+
}
55+
/>
56+
</EuiFlexItem>
57+
<EuiFlexItem grow={true}>{children}</EuiFlexItem>
58+
<EuiFlexItem grow={false}>
59+
<EuiButtonIcon
60+
iconSize="s"
61+
iconType="cross"
62+
color="danger"
63+
data-test-subj="lns-customBucketContainer-remove"
64+
onClick={onRemoveClick}
65+
aria-label={removeTitle}
66+
title={removeTitle}
67+
/>
68+
</EuiFlexItem>
69+
</EuiFlexGroup>
70+
</EuiPanel>
71+
);
72+
};
73+
74+
export const DraggableBucketContainer = ({
75+
id,
76+
idx,
77+
children,
78+
...bucketContainerProps
79+
}: {
80+
id: string;
81+
idx: number;
82+
children: React.ReactNode;
83+
} & BucketContainerProps) => {
84+
return (
85+
<EuiDraggable spacing="m" index={idx} draggableId={id} disableInteractiveElementBlocking>
86+
{(provided) => <BucketContainer {...bucketContainerProps}>{children}</BucketContainer>}
87+
</EuiDraggable>
88+
);
89+
};
90+
91+
interface DraggableLocation {
92+
droppableId: string;
93+
index: number;
94+
}
95+
96+
export const DragDropBuckets = ({
97+
items,
98+
onDragStart,
99+
onDragEnd,
100+
droppableId,
101+
children,
102+
}: {
103+
items: any; // eslint-disable-line @typescript-eslint/no-explicit-any
104+
onDragStart: () => void;
105+
onDragEnd: (items: any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any
106+
droppableId: string;
107+
children: React.ReactElement[];
108+
}) => {
109+
const handleDragEnd = ({
110+
source,
111+
destination,
112+
}: {
113+
source?: DraggableLocation;
114+
destination?: DraggableLocation;
115+
}) => {
116+
if (source && destination) {
117+
const newItems = euiDragDropReorder(items, source.index, destination.index);
118+
onDragEnd(newItems);
119+
}
120+
};
121+
return (
122+
<EuiDragDropContext onDragEnd={handleDragEnd} onDragStart={onDragStart}>
123+
<EuiDroppable droppableId={droppableId} spacing="s">
124+
{children}
125+
</EuiDroppable>
126+
</EuiDragDropContext>
127+
);
128+
};
129+
130+
export const NewBucketButton = ({ label, onClick }: { label: string; onClick: () => void }) => (
131+
<EuiButtonEmpty size="xs" iconType="plusInCircle" onClick={onClick}>
132+
{label}
133+
</EuiButtonEmpty>
134+
);

x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/custom_bucket_container.tsx

Lines changed: 0 additions & 59 deletions
This file was deleted.

x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
*/
66

77
export * from './label_input';
8-
export * from './custom_bucket_container';
8+
export * from './bucket_container';

0 commit comments

Comments
 (0)