[Canvas] Adds Label option for Dropdown Control (#88505)

* Adds Label option for Dropdown Control

* Update Snapshots

* Fix typecheck
This commit is contained in:
Corey Robertson 2021-02-11 13:51:23 -05:00 committed by GitHub
parent 609b5bf1b7
commit 8bd0e3217b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 26 deletions

View file

@ -205,4 +205,34 @@ const stringTable: Datatable = {
],
};
export { emptyTable, testTable, stringTable };
const relationalTable: Datatable = {
type: 'datatable',
columns: [
{
id: 'id',
name: 'id',
meta: { type: 'string' },
},
{
id: 'name',
name: 'name',
meta: { type: 'string' },
},
],
rows: [
{
id: '1',
name: 'One',
},
{
id: '2',
name: 'Two',
},
{
id: '3',
name: 'Three',
},
],
};
export { emptyTable, testTable, stringTable, relationalTable };

View file

@ -5,26 +5,27 @@
* 2.0.
*/
import { uniq } from 'lodash';
import { Datatable, Render, ExpressionFunctionDefinition } from '../../../types';
import { uniqBy } from 'lodash';
import { Datatable, ExpressionValueRender, ExpressionFunctionDefinition } from '../../../types';
import { getFunctionHelp } from '../../../i18n';
interface Arguments {
filterColumn: string;
labelColumn: string;
valueColumn: string;
filterGroup: string;
}
interface Return {
column: string;
choices: any;
choices: Array<[string, string]>;
}
export function dropdownControl(): ExpressionFunctionDefinition<
'dropdownControl',
Datatable,
Arguments,
Render<Return>
ExpressionValueRender<Return>
> {
const { help, args: argHelp } = getFunctionHelp().dropdownControl;
@ -40,6 +41,11 @@ export function dropdownControl(): ExpressionFunctionDefinition<
required: true,
help: argHelp.filterColumn,
},
labelColumn: {
types: ['string'],
required: false,
help: argHelp.labelColumn,
},
valueColumn: {
types: ['string'],
required: true,
@ -50,15 +56,18 @@ export function dropdownControl(): ExpressionFunctionDefinition<
help: argHelp.filterGroup,
},
},
fn: (input, { valueColumn, filterColumn, filterGroup }) => {
let choices = [];
fn: (input, { valueColumn, filterColumn, filterGroup, labelColumn }) => {
let choices: Array<[string, string]> = [];
const labelCol = labelColumn || valueColumn;
const filteredRows = input.rows.filter(
(row) => row[valueColumn] !== null && row[valueColumn] !== undefined
);
if (filteredRows.length > 0) {
choices = uniq(filteredRows.map((row) => row[valueColumn])).sort();
choices = filteredRows.map((row) => [row[valueColumn], row[labelCol]]);
choices = uniqBy(choices, (choice) => choice[0]);
}
const column = filterColumn || valueColumn;

View file

@ -5,16 +5,13 @@
* 2.0.
*/
// @ts-expect-error untyped local
import { functionWrapper } from '../../../test_helpers/function_wrapper';
import { testTable } from './__fixtures__/test_tables';
import { testTable, relationalTable } from './__fixtures__/test_tables';
import { dropdownControl } from './dropdownControl';
describe('dropdownControl', () => {
const fn = functionWrapper(dropdownControl);
const uniqueNames = testTable.rows.reduce(
(unique, { name }) => (unique.includes(name) ? unique : unique.concat([name])),
[]
);
it('returns a render as dropdown_filter', () => {
expect(fn(testTable, { filterColumn: 'name', valueColumn: 'name' })).toHaveProperty(
@ -30,6 +27,11 @@ describe('dropdownControl', () => {
describe('args', () => {
describe('valueColumn', () => {
it('populates dropdown choices with unique values in valueColumn', () => {
const uniqueNames = testTable.rows.reduce<Array<[string, string]>>(
(unique, { name }) =>
unique.find(([value, label]) => value === name) ? unique : [...unique, [name, name]],
[]
);
expect(fn(testTable, { valueColumn: 'name' }).value.choices).toEqual(uniqueNames);
});
@ -38,6 +40,15 @@ describe('dropdownControl', () => {
expect(fn(testTable, { valueColumn: '' }).value.choices).toEqual([]);
});
});
describe('labelColumn', () => {
it('populates dropdown choices with labels from label column', () => {
const expectedChoices = relationalTable.rows.map((row) => [row.id, row.name]);
expect(
fn(relationalTable, { valueColumn: 'id', labelColumn: 'name' }).value.choices
).toEqual(expectedChoices);
});
});
});
describe('filterColumn', () => {

View file

@ -40,19 +40,19 @@ exports[`Storyshots renderers/DropdownFilter with choices 1`] = `
</option>
<option
aria-selected={false}
value="Item One"
value="1"
>
Item One
</option>
<option
aria-selected={false}
value="Item Two"
value="2"
>
Item Two
</option>
<option
aria-selected={false}
value="Item Three"
value="3"
>
Item Three
</option>
@ -82,19 +82,19 @@ exports[`Storyshots renderers/DropdownFilter with choices and new value 1`] = `
</option>
<option
aria-selected={false}
value="Item One"
value="1"
>
Item One
</option>
<option
aria-selected={false}
value="Item Two"
value="2"
>
Item Two
</option>
<option
aria-selected={false}
value="Item Three"
value="3"
>
Item Three
</option>
@ -124,19 +124,19 @@ exports[`Storyshots renderers/DropdownFilter with choices and value 1`] = `
</option>
<option
aria-selected={false}
value="Item One"
value="1"
>
Item One
</option>
<option
aria-selected={true}
value="Item Two"
aria-selected={false}
value="2"
>
Item Two
</option>
<option
aria-selected={false}
value="Item Three"
value="3"
>
Item Three
</option>

View file

@ -10,7 +10,11 @@ import { storiesOf } from '@storybook/react';
import React from 'react';
import { DropdownFilter } from '../dropdown_filter';
const choices = ['Item One', 'Item Two', 'Item Three'];
const choices: Array<[string, string]> = [
['1', 'Item One'],
['2', 'Item Two'],
['3', 'Item Three'],
];
storiesOf('renderers/DropdownFilter', module)
.add('default', () => <DropdownFilter onChange={action('onChange')} commit={action('commit')} />)

View file

@ -17,7 +17,7 @@ export interface Props {
* A collection of choices to display in the dropdown
* @default []
*/
choices?: string[];
choices?: Array<[string, string]>;
/**
* Optional value for the component. If the value is not present in the
* choices collection, it will be discarded.
@ -38,7 +38,7 @@ export const DropdownFilter: FunctionComponent<Props> = ({
let options = [
{ value: '%%CANVAS_MATCH_ALL%%', text: `-- ${strings.getMatchAllOptionLabel()} --` },
];
options = options.concat(choices.map((choice) => ({ value: choice, text: choice })));
options = options.concat(choices.map((choice) => ({ value: choice[0], text: choice[1] })));
const changeHandler = (e: FocusEvent<HTMLSelectElement> | ChangeEvent<HTMLSelectElement>) => {
if (e && e.target) {

View file

@ -21,6 +21,9 @@ export const help: FunctionHelp<FunctionFactory<typeof dropdownControl>> = {
defaultMessage: 'The column or field that you want to filter.',
}
),
labelColumn: i18n.translate('xpack.canvas.functions.dropdownControl.args.labelColumnHelpText', {
defaultMessage: 'The column or field to use as the label in the dropdown control',
}),
valueColumn: i18n.translate('xpack.canvas.functions.dropdownControl.args.valueColumnHelpText', {
defaultMessage:
'The column or field from which to extract the unique values for the dropdown control.',