diff --git a/x-pack/plugins/lens/public/indexpattern_plugin/__snapshots__/indexpattern.test.tsx.snap b/x-pack/plugins/lens/public/indexpattern_plugin/__snapshots__/indexpattern.test.tsx.snap index fb8abf6ccb664..45760f0bf5efb 100644 --- a/x-pack/plugins/lens/public/indexpattern_plugin/__snapshots__/indexpattern.test.tsx.snap +++ b/x-pack/plugins/lens/public/indexpattern_plugin/__snapshots__/indexpattern.test.tsx.snap @@ -3,6 +3,35 @@ exports[`IndexPattern Data Source #getPublicAPI renderDimensionPanel should render a dimension panel 1`] = `
Dimension Panel +
`; diff --git a/x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.test.tsx b/x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.test.tsx index 4130c42617eb2..9e19f7bd2c4d3 100644 --- a/x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.test.tsx @@ -199,12 +199,74 @@ describe('IndexPattern Data Source', () => { {}} + columnId={'col2'} filterOperations={(operation: Operation) => true} /> ); expect(wrapper).toMatchSnapshot(); }); + + it('should call the filterOperations function', () => { + const filterOperations = jest.fn().mockReturnValue(true); + + shallow( + {}} + columnId={'col2'} + filterOperations={filterOperations} + /> + ); + + expect(filterOperations).toBeCalledTimes(3); + }); + + it('should filter out all selections if the filter returns false', () => { + const wrapper = shallow( + {}} + columnId={'col2'} + filterOperations={() => false} + /> + ); + + expect(wrapper.find(EuiComboBox)!.prop('options')!.length).toEqual(0); + }); + + it('should update the datasource state on selection', () => { + const setState = jest.fn(); + + const wrapper = shallow( + true} + /> + ); + + const comboBox = wrapper.find(EuiComboBox)!; + const firstOption = comboBox.prop('options')![0]; + + comboBox.prop('onChange')!([firstOption]); + + expect(setState).toHaveBeenCalledWith({ + ...state, + columns: { + ...state.columns, + col2: { + operationId: firstOption.value, + label: 'Value of timestamp', + dataType: 'date', + isBucketed: false, + operationType: 'value', + }, + }, + columnOrder: ['col1', 'col2'], + }); + }); }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.tsx index 140c54031677f..81549315fe41b 100644 --- a/x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_plugin/indexpattern.tsx @@ -20,7 +20,7 @@ interface IndexPatternColumn { operationId: string; label: string; dataType: DataType; - isBucketed: false; + isBucketed: boolean; // Private operationType: OperationType; @@ -99,7 +99,69 @@ export type IndexPatternDimensionPanelProps = DatasourceDimensionPanelProps & { }; export function IndexPatternDimensionPanel(props: IndexPatternDimensionPanelProps) { - return
Dimension Panel
; + const fields = props.state.indexPatterns[props.state.currentIndexPatternId].fields; + const columns: IndexPatternColumn[] = fields.map((field, index) => ({ + operationId: `${index}`, + label: `Value of ${field.name}`, + dataType: field.type as DataType, + isBucketed: false, + + operationType: 'value' as OperationType, + })); + + const filteredColumns = columns.filter(col => { + const { operationId, label, dataType, isBucketed } = col; + + return props.filterOperations({ + id: operationId, + label, + dataType, + isBucketed, + }); + }); + + const selectedColumn: IndexPatternColumn | null = props.state.columns[props.columnId] || null; + + return ( +
+ Dimension Panel + ({ + label: col.label, + value: col.operationId, + }))} + selectedOptions={ + selectedColumn + ? [ + { + label: selectedColumn.label, + value: selectedColumn.operationId, + }, + ] + : [] + } + singleSelection={{ asPlainText: true }} + isClearable={false} + onChange={choices => { + const column: IndexPatternColumn = columns.find( + ({ operationId }) => operationId === choices[0].value + )!; + const newColumns: IndexPatternPrivateState['columns'] = { + ...props.state.columns, + [props.columnId]: column, + }; + + props.setState({ + ...props.state, + columns: newColumns, + // Order is not meaningful until we aggregate + columnOrder: Object.keys(newColumns), + }); + }} + /> +
+ ); } export function getIndexPatternDatasource(chrome: Chrome, toastNotifications: ToastNotifications) { @@ -151,16 +213,13 @@ export function getIndexPatternDatasource(chrome: Chrome, toastNotifications: To }, getOperationForColumnId: (columnId: string) => { const column = state.columns[columnId]; - if (columnId) { - const { dataType, label, isBucketed, operationId } = column; - return { - id: operationId, - label, - dataType, - isBucketed, - }; - } - return null; + const { dataType, label, isBucketed, operationId } = column; + return { + id: operationId, + label, + dataType, + isBucketed, + }; }, renderDimensionPanel: (domElement: Element, props: DatasourceDimensionPanelProps) => { diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 7f73a0764d056..f77822191c6fd 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -76,7 +76,7 @@ export interface DatasourceDataPanelProps { // The only way a visualization has to restrict the query building export interface DatasourceDimensionPanelProps { // If no columnId is passed, it will render as empty - columnId?: string; + columnId: string; // Visualizations can restrict operations based on their own rules filterOperations: (operation: Operation) => boolean; diff --git a/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx b/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx index c5d69175e90cd..1da16b073b148 100644 --- a/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx @@ -6,7 +6,8 @@ import React from 'react'; import { render } from 'react-dom'; -import { Visualization } from '../types'; +import { Visualization, Operation } from '../types'; +import { NativeRenderer } from '../native_renderer'; export interface XyVisualizationState { roles: string[]; @@ -26,7 +27,20 @@ export const xyVisualization: Visualization { - render(
XY Visualization
, domElement); + render( +
+ XY Visualization + true, + suggestedOrder: 1, + }} + render={props.datasource.renderDimensionPanel} + /> +
, + domElement + ); }, getSuggestions: options => [],