@@ -17,7 +17,7 @@ import {
1717} from '@elastic/eui' ;
1818import { EuiFormLabel } from '@elastic/eui' ;
1919import { IndexPatternColumn , OperationType } from '../indexpattern' ;
20- import { IndexPatternDimensionEditorProps , OperationFieldSupportMatrix } from './dimension_panel' ;
20+ import { IndexPatternDimensionEditorProps , OperationSupportMatrix } from './dimension_panel' ;
2121import {
2222 operationDefinitionMap ,
2323 getOperationDisplay ,
@@ -36,7 +36,7 @@ const operationPanels = getOperationDisplay();
3636
3737export interface DimensionEditorProps extends IndexPatternDimensionEditorProps {
3838 selectedColumn ?: IndexPatternColumn ;
39- operationFieldSupportMatrix : OperationFieldSupportMatrix ;
39+ operationSupportMatrix : OperationSupportMatrix ;
4040 currentIndexPattern : IndexPattern ;
4141}
4242
@@ -90,22 +90,24 @@ const LabelInput = ({ value, onChange }: { value: string; onChange: (value: stri
9090export function DimensionEditor ( props : DimensionEditorProps ) {
9191 const {
9292 selectedColumn,
93- operationFieldSupportMatrix ,
93+ operationSupportMatrix ,
9494 state,
9595 columnId,
9696 setState,
9797 layerId,
9898 currentIndexPattern,
9999 hideGrouping,
100100 } = props ;
101- const { operationByField, fieldByOperation } = operationFieldSupportMatrix ;
101+ const { operationByField, fieldByOperation } = operationSupportMatrix ;
102102 const [
103103 incompatibleSelectedOperationType ,
104104 setInvalidOperationType ,
105105 ] = useState < OperationType | null > ( null ) ;
106106
107- const ParamEditor =
108- selectedColumn && operationDefinitionMap [ selectedColumn . operationType ] . paramEditor ;
107+ const selectedOperationDefinition =
108+ selectedColumn && operationDefinitionMap [ selectedColumn . operationType ] ;
109+
110+ const ParamEditor = selectedOperationDefinition ?. paramEditor ;
109111
110112 const fieldMap : Record < string , IndexPatternField > = useMemo ( ( ) => {
111113 const fields : Record < string , IndexPatternField > = { } ;
@@ -129,6 +131,10 @@ export function DimensionEditor(props: DimensionEditorProps) {
129131 [
130132 ...asOperationOptions ( validOperationTypes , true ) ,
131133 ...asOperationOptions ( possibleOperationTypes , false ) ,
134+ ...asOperationOptions (
135+ operationSupportMatrix . operationWithoutField ,
136+ ! selectedColumn || ! hasField ( selectedColumn )
137+ ) ,
132138 ] ,
133139 'operationType'
134140 ) ;
@@ -166,12 +172,30 @@ export function DimensionEditor(props: DimensionEditorProps) {
166172 compatibleWithCurrentField ? '' : ' incompatible'
167173 } `,
168174 onClick ( ) {
169- // todo: when moving from terms agg to filters, we want to create a filter `$field.name : *`
170- // it probably has to be re-thought when removing the field name.
171- const isTermsToFilters =
172- selectedColumn ?. operationType === 'terms' && operationType === 'filters' ;
173-
174- if ( ! selectedColumn || ! compatibleWithCurrentField ) {
175+ if ( operationDefinitionMap [ operationType ] . input === 'none' ) {
176+ // Clear invalid state because we are creating a valid column
177+ setInvalidOperationType ( null ) ;
178+ if ( selectedColumn ?. operationType === operationType ) {
179+ return ;
180+ }
181+ setState (
182+ changeColumn ( {
183+ state,
184+ layerId,
185+ columnId,
186+ newColumn : buildColumn ( {
187+ columns : props . state . layers [ props . layerId ] . columns ,
188+ suggestedPriority : props . suggestedPriority ,
189+ layerId : props . layerId ,
190+ op : operationType ,
191+ indexPattern : currentIndexPattern ,
192+ previousColumn : selectedColumn ,
193+ } ) ,
194+ } )
195+ ) ;
196+ trackUiEvent ( `indexpattern_dimension_operation_${ operationType } ` ) ;
197+ return ;
198+ } else if ( ! selectedColumn || ! compatibleWithCurrentField ) {
175199 const possibleFields = fieldByOperation [ operationType ] || [ ] ;
176200
177201 if ( possibleFields . length === 1 ) {
@@ -197,19 +221,20 @@ export function DimensionEditor(props: DimensionEditorProps) {
197221 trackUiEvent ( `indexpattern_dimension_operation_${ operationType } ` ) ;
198222 return ;
199223 }
200- if ( incompatibleSelectedOperationType && ! isTermsToFilters ) {
201- setInvalidOperationType ( null ) ;
202- }
203- if ( selectedColumn . operationType === operationType ) {
224+
225+ setInvalidOperationType ( null ) ;
226+
227+ if ( selectedColumn ? .operationType === operationType ) {
204228 return ;
205229 }
230+
206231 const newColumn : IndexPatternColumn = buildColumn ( {
207232 columns : props . state . layers [ props . layerId ] . columns ,
208233 suggestedPriority : props . suggestedPriority ,
209234 layerId : props . layerId ,
210235 op : operationType ,
211236 indexPattern : currentIndexPattern ,
212- field : fieldMap [ selectedColumn . sourceField ] ,
237+ field : hasField ( selectedColumn ) ? fieldMap [ selectedColumn . sourceField ] : undefined ,
213238 previousColumn : selectedColumn ,
214239 } ) ;
215240
@@ -244,93 +269,101 @@ export function DimensionEditor(props: DimensionEditorProps) {
244269 </ div >
245270 < EuiSpacer size = "s" />
246271 < div className = "lnsIndexPatternDimensionEditor__section lnsIndexPatternDimensionEditor__section--shaded" >
247- < EuiFormRow
248- data-test-subj = "indexPattern-field-selection-row"
249- label = { i18n . translate ( 'xpack.lens.indexPattern.chooseField' , {
250- defaultMessage : 'Choose a field' ,
251- } ) }
252- fullWidth
253- isInvalid = { Boolean ( incompatibleSelectedOperationType ) }
254- error = {
255- selectedColumn
256- ? i18n . translate ( 'xpack.lens.indexPattern.invalidOperationLabel' , {
257- defaultMessage : 'To use this function, select a different field.' ,
258- } )
259- : undefined
260- }
261- >
262- < FieldSelect
263- currentIndexPattern = { currentIndexPattern }
264- existingFields = { state . existingFields }
265- fieldMap = { fieldMap }
266- operationFieldSupportMatrix = { operationFieldSupportMatrix }
267- selectedColumnOperationType = { selectedColumn && selectedColumn . operationType }
268- selectedColumnSourceField = {
269- selectedColumn && hasField ( selectedColumn ) ? selectedColumn . sourceField : undefined
272+ { ! selectedColumn ||
273+ selectedOperationDefinition ?. input === 'field' ||
274+ ( incompatibleSelectedOperationType &&
275+ operationDefinitionMap [ incompatibleSelectedOperationType ] . input === 'field' ) ? (
276+ < EuiFormRow
277+ data-test-subj = "indexPattern-field-selection-row"
278+ label = { i18n . translate ( 'xpack.lens.indexPattern.chooseField' , {
279+ defaultMessage : 'Choose a field' ,
280+ } ) }
281+ fullWidth
282+ isInvalid = { Boolean ( incompatibleSelectedOperationType ) }
283+ error = {
284+ selectedColumn && incompatibleSelectedOperationType
285+ ? selectedOperationDefinition ?. input === 'field'
286+ ? i18n . translate ( 'xpack.lens.indexPattern.invalidOperationLabel' , {
287+ defaultMessage : 'To use this function, select a different field.' ,
288+ } )
289+ : i18n . translate ( 'xpack.lens.indexPattern.chooseFieldLabel' , {
290+ defaultMessage : 'To use this function, select a field.' ,
291+ } )
292+ : undefined
270293 }
271- incompatibleSelectedOperationType = { incompatibleSelectedOperationType }
272- onDeleteColumn = { ( ) => {
273- setState (
274- deleteColumn ( {
275- state,
276- layerId,
277- columnId,
278- } )
279- ) ;
280- } }
281- onChoose = { ( choice ) => {
282- let column : IndexPatternColumn ;
283- if (
284- ! incompatibleSelectedOperationType &&
285- selectedColumn &&
286- 'field' in choice &&
287- choice . operationType === selectedColumn . operationType
288- ) {
289- // If we just changed the field are not in an error state and the operation didn't change,
290- // we use the operations onFieldChange method to calculate the new column.
291- column = changeField ( selectedColumn , currentIndexPattern , fieldMap [ choice . field ] ) ;
292- } else {
293- // Otherwise we'll use the buildColumn method to calculate a new column
294- const compatibleOperations =
295- ( 'field' in choice &&
296- operationFieldSupportMatrix . operationByField [ choice . field ] ) ||
297- [ ] ;
298- let operation ;
299- if ( compatibleOperations . length > 0 ) {
300- operation =
301- incompatibleSelectedOperationType &&
302- compatibleOperations . includes ( incompatibleSelectedOperationType )
303- ? incompatibleSelectedOperationType
304- : compatibleOperations [ 0 ] ;
305- } else if ( 'field' in choice ) {
306- operation = choice . operationType ;
307- }
308- column = buildColumn ( {
309- columns : props . state . layers [ props . layerId ] . columns ,
310- field : fieldMap [ choice . field ] ,
311- indexPattern : currentIndexPattern ,
312- layerId : props . layerId ,
313- suggestedPriority : props . suggestedPriority ,
314- op : operation as OperationType ,
315- previousColumn : selectedColumn ,
316- } ) ;
294+ >
295+ < FieldSelect
296+ currentIndexPattern = { currentIndexPattern }
297+ existingFields = { state . existingFields }
298+ fieldMap = { fieldMap }
299+ operationSupportMatrix = { operationSupportMatrix }
300+ selectedColumnOperationType = { selectedColumn && selectedColumn . operationType }
301+ selectedColumnSourceField = {
302+ selectedColumn && hasField ( selectedColumn ) ? selectedColumn . sourceField : undefined
317303 }
304+ incompatibleSelectedOperationType = { incompatibleSelectedOperationType }
305+ onDeleteColumn = { ( ) => {
306+ setState (
307+ deleteColumn ( {
308+ state,
309+ layerId,
310+ columnId,
311+ } )
312+ ) ;
313+ } }
314+ onChoose = { ( choice ) => {
315+ let column : IndexPatternColumn ;
316+ if (
317+ ! incompatibleSelectedOperationType &&
318+ selectedColumn &&
319+ 'field' in choice &&
320+ choice . operationType === selectedColumn . operationType
321+ ) {
322+ // If we just changed the field are not in an error state and the operation didn't change,
323+ // we use the operations onFieldChange method to calculate the new column.
324+ column = changeField ( selectedColumn , currentIndexPattern , fieldMap [ choice . field ] ) ;
325+ } else {
326+ // Otherwise we'll use the buildColumn method to calculate a new column
327+ const compatibleOperations =
328+ ( 'field' in choice && operationSupportMatrix . operationByField [ choice . field ] ) ||
329+ [ ] ;
330+ let operation ;
331+ if ( compatibleOperations . length > 0 ) {
332+ operation =
333+ incompatibleSelectedOperationType &&
334+ compatibleOperations . includes ( incompatibleSelectedOperationType )
335+ ? incompatibleSelectedOperationType
336+ : compatibleOperations [ 0 ] ;
337+ } else if ( 'field' in choice ) {
338+ operation = choice . operationType ;
339+ }
340+ column = buildColumn ( {
341+ columns : props . state . layers [ props . layerId ] . columns ,
342+ field : fieldMap [ choice . field ] ,
343+ indexPattern : currentIndexPattern ,
344+ layerId : props . layerId ,
345+ suggestedPriority : props . suggestedPriority ,
346+ op : operation as OperationType ,
347+ previousColumn : selectedColumn ,
348+ } ) ;
349+ }
318350
319- setState (
320- changeColumn ( {
321- state,
322- layerId,
323- columnId,
324- newColumn : column ,
325- keepParams : false ,
326- } )
327- ) ;
328- setInvalidOperationType ( null ) ;
329- } }
330- />
331- </ EuiFormRow >
351+ setState (
352+ changeColumn ( {
353+ state,
354+ layerId,
355+ columnId,
356+ newColumn : column ,
357+ keepParams : false ,
358+ } )
359+ ) ;
360+ setInvalidOperationType ( null ) ;
361+ } }
362+ />
363+ </ EuiFormRow >
364+ ) : null }
332365
333- { ! incompatibleSelectedOperationType && ParamEditor && (
366+ { ! incompatibleSelectedOperationType && selectedColumn && ParamEditor && (
334367 < >
335368 < ParamEditor
336369 state = { state }
0 commit comments