Skip to content

Commit

Permalink
Create multi filter plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
cqliu1 committed Feb 7, 2020
0 parents commit 1314b36
Show file tree
Hide file tree
Showing 12 changed files with 396 additions and 0 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# canvas-multi-filter

> Adds a multiple filter element to Canvas that allows users filters for exact values from multiple fields
---

## Development

See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment.

21 changes: 21 additions & 0 deletions common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export const PLUGIN_ID = 'canvasMultiFilter';
export const PLUGIN_NAME = 'canvas-multi-filter';
42 changes: 42 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export default function(kibana) {
return new kibana.Plugin({
// Tell Kibana that this plugin needs canvas and the Kibana interpreter
require: ['interpreter', 'canvas'],

// The name of your plugin. Make this whatever you want.
name: 'canvas_multi_filter',

uiExports: {
// Tell Kibana that the files in `/public` should be loaded into the
// browser only when the user is in the Canvas app.
canvas: ['plugins/canvas_multi_filter'],
},

// Enable the plugin by default
config(Joi) {
console.log('REGISTERING CANVAS MULTI FILTER');
return Joi.object({
enabled: Joi.boolean().default(true),
}).default();
},
});
}
8 changes: 8 additions & 0 deletions kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"id": "canvasMultiFilter",
"version": "7.5.2",
"server": false,
"ui": true,
"requiredPlugins": ["navigation"],
"optionalPlugins": []
}
Binary file added public/.DS_Store
Binary file not shown.
Binary file added public/element/header.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions public/element/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import header from './header.png';

export const multiFilter = () => ({
name: 'multi_filter',
displayName: 'Multi filter',
tags: ['filter'],
help:
'A dropdown from which you can select values from different fields to apply multiple "exactly" filters',
image: header,
height: 50,
expression: `demodata
| multifilterControl
| render`,
filter: '',
});
49 changes: 49 additions & 0 deletions public/function/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export function multiFilterControl() {
return {
name: 'multiFilterControl',
aliases: [],
type: 'render',
context: {
types: ['datatable'],
},
help: 'Multi-filter control',
args: {
columns: {
types: ['string'],
multi: true,
aliases: ['field', 'c'],
help: 'The columns to include.',
},
filterGroup: {
types: ['string'],
help: 'The name of the grouping filter, if applicable.',
},
},
fn: (context, args) => {
return {
type: 'render',
as: 'multi_filter',
value: { datatable: context, ...args },
};
},
};
}
39 changes: 39 additions & 0 deletions public/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { multiFilter as multiFilterRenderer } from './renderer';
import { multiFilter as multiFilterElement } from './element';
import { multiFilterControl as multiFitlerControlFn } from './function';
import { multiFilterControl as multiFitlerControlView } from './view';

const elements = [multiFilterElement];

const browserFunctions = [multiFitlerControlFn];

const views = [multiFitlerControlView];

const renderers = [multiFilterRenderer];

// Register our elements and browserFunctions with the Canvas interpreter.
kbnInterpreter.register({
elements,
browserFunctions,
views,
renderers,
});
67 changes: 67 additions & 0 deletions public/renderer/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React, { useState } from 'react';
import { EuiComboBox } from '@elastic/eui';

export const MultiFilter = ({ datatable, selected = [], onChange }) => {
const toOption = value => ({
label: value.column + ':' + value.value,
value,
});

const [selectedOptions, setSelectedOptions] = useState(selected.map(toOption));

// Create a collection of groupings of options
const groupings =
((acc, column) => {
const objs = datatable.rows.map(row => ({ column, value: row[column] }));
acc[column] = Array.from(new Set(objs.map(item => item.value))).map(value =>
objs.find(item => item.value === value)
);
return acc;
},
{});

// Create the appropriately-shaped choices for the drop down.
const options = Object.keys(groupings).map(group => ({
label: group,
options: groupings[group].map(item => toOption({ column: group, value: item.value })),
}));

const onChangeHandler = items => {
setSelectedOptions(items);
};

return (
<div className="">
<EuiComboBox
placeholder="Select filters..."
options={options}
selectedOptions={selectedOptions}
onChange={onChangeHandler}
onBlur={() => {
onChange(selectedOptions.map(option => option.value));
}}
fullWidth
compressed
/>
</div>
);
};
84 changes: 84 additions & 0 deletions public/renderer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { fromExpression, toExpression } from '@kbn/interpreter/common';
import { get } from 'lodash';
import React from 'react';
import ReactDOM from 'react-dom';
import { MultiFilter } from './component';

const getFilterValues = filterExpression => {
if (filterExpression === '') {
return [];
}

const filterAST = fromExpression(filterExpression);
return filterAST.chain.map(chain => ({
value: get(chain, 'arguments.value[0]'),
column: get(chain, 'arguments.column[0]'),
}));
};

export const multiFilter = () => ({
name: 'multi_filter',
displayName: 'Multiple filter',
help: 'Renders a multiple selection dropdown filter',
reuseDomNode: true,
height: 50,
render(domNode, config, handlers) {
const filterExpression = handlers.getFilter();

const onChange = options => {
if (options.length === 0) {
handlers.setFilter('');
} else {
const filterChain = options.map(({ column, value }) => {
return {
type: 'function',
function: 'exactly',
arguments: {
value: [value],
column: [column],
filterGroup: [config.filterGroup],
},
};
});

const newFilterAST = {
type: 'expression',
chain: filterChain,
};

const newFilter = toExpression(newFilterAST);
handlers.setFilter(newFilter);
}
};

const { datatable, columns } = config;
const selected = getFilterValues(filterExpression);

ReactDOM.render(<MultiFilter {...{ onChange, datatable, columns, selected }} />, domNode, () =>
handlers.done()
);

handlers.onDestroy(() => {
ReactDOM.unmountComponentAtNode(domNode);
});
},
});
Loading

0 comments on commit 1314b36

Please sign in to comment.