From a167d081f5e7320eb22282f3d343705ed8cbcafa Mon Sep 17 00:00:00 2001
From: Jean-Louis Leysens
Date: Fri, 16 Aug 2019 09:04:22 +0200
Subject: [PATCH] [Rollup] Metrics multiple selection (#42927) (#43353)
* remove duplicate columns declaration
* First iteration of select all menu working
* A lot of bugfixes and started on row-level selection using flatMap
* Wip on row selection
* Fix formatting
* More bug fixes and and first iteration of row selection and column selection working
* Minor refactors, cleaned up logging. Still has nasty UX bug on Popover menu
* Added translations and empty tests
* Tests are WiP
* Move checkboxes out of popover
* Implemented first iteration of select all functionality
* Move row select all into checkbox area
* Remove unused code and simplify select all if-else
* First iteration of tests
* fix tests and update name data-test-subj name
* Move checkboxes into popover with button
* - Fix tests
- Refactor test helpers for metric step
- Cleanup some JS style nits
- Make popover button disabled if all metric checkboxes are disabled
* Remove extra newline
* - Moved constants from metrics to own file so that it's more re-usable
- Removed unnecessary styling
- Fixed typos
- Updated comments
- i18n
---
.../helpers/job_create.helpers.js | 32 +-
.../job_create_metrics.test.js | 163 ++++++-
.../rollup/public/crud_app/constants/index.js | 4 +
.../crud_app/constants/metrics_config.js | 45 ++
.../components/field_list/field_list.js | 2 +-
.../steps/components/field_chooser.js | 2 -
.../sections/job_create/steps/step_metrics.js | 445 +++++++++++++-----
7 files changed, 557 insertions(+), 136 deletions(-)
create mode 100644 x-pack/legacy/plugins/rollup/public/crud_app/constants/metrics_config.js
diff --git a/x-pack/legacy/plugins/rollup/__jest__/client_integration/helpers/job_create.helpers.js b/x-pack/legacy/plugins/rollup/__jest__/client_integration/helpers/job_create.helpers.js
index 57b551cad595b..e97f060306e52 100644
--- a/x-pack/legacy/plugins/rollup/__jest__/client_integration/helpers/job_create.helpers.js
+++ b/x-pack/legacy/plugins/rollup/__jest__/client_integration/helpers/job_create.helpers.js
@@ -14,7 +14,7 @@ const initTestBed = registerTestBed(JobCreate, { store: rollupJobsStore });
export const setup = (props) => {
const testBed = initTestBed(props);
- const { component, form } = testBed;
+ const { component, form, table } = testBed;
// User actions
const clickNextStep = () => {
@@ -68,6 +68,30 @@ export const setup = (props) => {
}
};
+ // Helpers for the metrics step in job creation.
+ // Mostly placed here for now to make test file a bit smaller and specific
+ // to tests.
+ const getFieldListTableRows = () => {
+ const { rows } = table.getMetaData('rollupJobMetricsFieldList');
+ return rows;
+ };
+
+ const getFieldListTableRow = (row) => {
+ const rows = getFieldListTableRows();
+ return rows[row];
+ };
+
+ const getFieldChooserColumnForRow = (row) => {
+ const selectedRow = getFieldListTableRow(row);
+ const [,, fieldChooserColumn] = selectedRow.columns;
+ return fieldChooserColumn;
+ };
+
+ const getSelectAllInputForRow = (row) => {
+ const fieldChooser = getFieldChooserColumnForRow(row);
+ return fieldChooser.reactWrapper.find('input').first();
+ };
+
// Misc
const getEuiStepsHorizontalActive = () => component.find('.euiStepHorizontal-isSelected').text();
@@ -84,5 +108,11 @@ export const setup = (props) => {
...testBed.form,
fillFormFields,
},
+ metrics: {
+ getFieldListTableRows,
+ getFieldListTableRow,
+ getFieldChooserColumnForRow,
+ getSelectAllInputForRow,
+ }
};
};
diff --git a/x-pack/legacy/plugins/rollup/__jest__/client_integration/job_create_metrics.test.js b/x-pack/legacy/plugins/rollup/__jest__/client_integration/job_create_metrics.test.js
index f54ba7f85ae99..cfd4901e3a8be 100644
--- a/x-pack/legacy/plugins/rollup/__jest__/client_integration/job_create_metrics.test.js
+++ b/x-pack/legacy/plugins/rollup/__jest__/client_integration/job_create_metrics.test.js
@@ -36,6 +36,7 @@ describe('Create Rollup Job, step 5: Metrics', () => {
let getEuiStepsHorizontalActive;
let goToStep;
let table;
+ let metrics;
beforeAll(() => {
({ server, httpRequestsMockHelpers } = setupEnvironment());
@@ -56,6 +57,7 @@ describe('Create Rollup Job, step 5: Metrics', () => {
getEuiStepsHorizontalActive,
goToStep,
table,
+ metrics,
} = setup());
});
@@ -175,6 +177,9 @@ describe('Create Rollup Job, step 5: Metrics', () => {
}
};
+ const numericTypeMetrics = ['avg', 'max', 'min', 'sum', 'value_count'];
+ const dateTypeMetrics = ['max', 'min', 'value_count'];
+
it('should have an empty field list', async () => {
await goToStep(5);
@@ -189,7 +194,6 @@ describe('Create Rollup Job, step 5: Metrics', () => {
});
it('should have "avg", "max", "min", "sum" & "value count" metrics for *numeric* fields', () => {
- const numericTypeMetrics = ['avg', 'max', 'min', 'sum', 'value_count'];
addFieldToList('numeric');
numericTypeMetrics.forEach(type => {
try {
@@ -203,11 +207,10 @@ describe('Create Rollup Job, step 5: Metrics', () => {
const { rows: [firstRow] } = table.getMetaData('rollupJobMetricsFieldList');
const columnWithMetricsCheckboxes = 2;
const metricsCheckboxes = firstRow.columns[columnWithMetricsCheckboxes].reactWrapper.find('input');
- expect(metricsCheckboxes.length).toBe(numericTypeMetrics.length);
+ expect(metricsCheckboxes.length).toBe(numericTypeMetrics.length + 1 /* add one for select all */);
});
it('should have "max", "min", & "value count" metrics for *date* fields', () => {
- const dateTypeMetrics = ['max', 'min', 'value_count'];
addFieldToList('date');
dateTypeMetrics.forEach(type => {
@@ -222,7 +225,7 @@ describe('Create Rollup Job, step 5: Metrics', () => {
const { rows: [firstRow] } = table.getMetaData('rollupJobMetricsFieldList');
const columnWithMetricsCheckboxes = 2;
const metricsCheckboxes = firstRow.columns[columnWithMetricsCheckboxes].reactWrapper.find('input');
- expect(metricsCheckboxes.length).toBe(dateTypeMetrics.length);
+ expect(metricsCheckboxes.length).toBe(dateTypeMetrics.length + 1 /* add one for select all */);
});
it('should not allow to go to the next step if at least one metric type is not selected', () => {
@@ -247,12 +250,162 @@ describe('Create Rollup Job, step 5: Metrics', () => {
const columnsFirstRow = fieldListRows[0].columns;
// The last column is the eui "actions" column
- const deleteButton = columnsFirstRow[columnsFirstRow.length - 1].reactWrapper.find('button');
+ const deleteButton = columnsFirstRow[columnsFirstRow.length - 1].reactWrapper.find('button').last();
deleteButton.simulate('click');
({ rows: fieldListRows } = table.getMetaData('rollupJobMetricsFieldList'));
expect(fieldListRows[0].columns[0].value).toEqual('No metrics fields added');
});
});
+
+ describe('when using multi-selectors', () => {
+ let getSelectAllInputForRow;
+ let getFieldChooserColumnForRow;
+ let getFieldListTableRows;
+
+ beforeEach(async () => {
+ httpRequestsMockHelpers.setIndexPatternValidityResponse({ numericFields, dateFields });
+ await goToStep(5);
+ await addFieldToList('numeric');
+ await addFieldToList('date');
+ find('rollupJobSelectAllMetricsPopoverButton').simulate('click');
+ ({ getSelectAllInputForRow, getFieldChooserColumnForRow, getFieldListTableRows } = metrics);
+ });
+
+ const expectAllFieldChooserInputs = (fieldChooserColumn, expected) => {
+ const inputs = fieldChooserColumn.reactWrapper.find('input');
+ inputs.forEach((input) => {
+ expect(input.props().checked).toBe(expected);
+ });
+ };
+
+ it('should select all of the fields in a row', async () => {
+ // The last column is the eui "actions" column
+ const selectAllCheckbox = getSelectAllInputForRow(0);
+ selectAllCheckbox.simulate('change', { checked: true });
+ const fieldChooserColumn = getFieldChooserColumnForRow(0);
+ expectAllFieldChooserInputs(fieldChooserColumn, true);
+ });
+
+ it('should deselect all of the fields in a row ', async () => {
+ const selectAllCheckbox = getSelectAllInputForRow(0);
+ selectAllCheckbox.simulate('change', { checked: true });
+
+ let fieldChooserColumn = getFieldChooserColumnForRow(0);
+ expectAllFieldChooserInputs(fieldChooserColumn, true);
+
+ selectAllCheckbox.simulate('change', { checked: false });
+ fieldChooserColumn = getFieldChooserColumnForRow(0);
+ expectAllFieldChooserInputs(fieldChooserColumn, false);
+ });
+
+ it('should select all of the metric types across rows (column-wise)', () => {
+ const selectAllAvgCheckbox = find('rollupJobMetricsSelectAllCheckbox-avg');
+ selectAllAvgCheckbox.first().simulate('change', { checked: true });
+
+ const rows = getFieldListTableRows();
+
+ rows
+ .filter((row) => {
+ const [, metricTypeCol ] = row.columns;
+ return metricTypeCol.value === 'numeric';
+ })
+ .forEach((row, idx) => {
+ const fieldChooser = getFieldChooserColumnForRow(idx);
+ fieldChooser.reactWrapper.find('input').forEach(input => {
+ const props = input.props();
+ if (props['data-test-subj'].endsWith('avg')) {
+ expect(props.checked).toBe(true);
+ } else {
+ expect(props.checked).toBe(false);
+ }
+ });
+ });
+ });
+
+ it('should deselect all of the metric types across rows (column-wise)', () => {
+ const selectAllAvgCheckbox = find('rollupJobMetricsSelectAllCheckbox-avg');
+
+ // Select first, which adds metrics column-wise, and then de-select column-wise to ensure
+ // that we correctly remove/undo our adding action.
+ selectAllAvgCheckbox.last().simulate('change', { checked: true });
+ selectAllAvgCheckbox.last().simulate('change', { checked: false });
+
+ const rows = getFieldListTableRows();
+
+ rows.forEach((row, idx) => {
+ const [, metricTypeCol ] = row.columns;
+ if (metricTypeCol.value !== 'numeric') {
+ return;
+ }
+ const fieldChooser = getFieldChooserColumnForRow(idx);
+ fieldChooser.reactWrapper.find('input').forEach(input => {
+ expect(input.props().checked).toBe(false);
+ });
+ });
+ });
+
+ it('should correctly select across rows and columns', async () => {
+ /**
+ * Tricky test case where we want to determine that the column-wise and row-wise
+ * selections are interacting correctly with each other.
+ *
+ * We will select avg (numeric) and max (numeric + date) to ensure that we are
+ * testing across metrics types too. The plan is:
+ *
+ * 1. Select all avg column-wise
+ * 2. Select all max column-wise
+ * 3. Select and deselect row-wise the first numeric metric row. Because some items will
+ * have been selected by the previous column-wise selection we want to test that row-wise
+ * select all followed by de-select can effectively undo the column-wise selections.
+ * 4. Expect the avg and max select all checkboxes to be unchecked
+ * 5. Select all on the last date metric row-wise
+ * 6. Select then deselect all max column-wise
+ * 7. Expect everything but all and max to be selected on the last date metric row
+ *
+ * Let's a go!
+ */
+
+ // 1.
+ find('rollupJobMetricsSelectAllCheckbox-avg').first().simulate('change', { checked: true });
+ // 2.
+ find('rollupJobMetricsSelectAllCheckbox-max').first().simulate('change', { checked: true });
+
+ const selectAllCheckbox = getSelectAllInputForRow(0);
+
+ // 3.
+ // Select all (which should check all checkboxes)
+ selectAllCheckbox.simulate('change', { checked: true });
+ // Deselect all (which should deselect all checkboxes)
+ selectAllCheckbox.simulate('change', { checked: false });
+
+ // 4.
+ expect(find('rollupJobMetricsSelectAllCheckbox-avg').first().props().checked).toBe(false);
+ expect(find('rollupJobMetricsSelectAllCheckbox-max').first().props().checked).toBe(false);
+
+ let rows = getFieldListTableRows();
+ // 5.
+ getSelectAllInputForRow(rows.length - 1).simulate('change', { checked: true });
+
+ // 6.
+ find('rollupJobMetricsSelectAllCheckbox-max').first().simulate('change', { checked: true });
+ find('rollupJobMetricsSelectAllCheckbox-max').first().simulate('change', { checked: false });
+
+ rows = getFieldListTableRows();
+ const lastRowFieldChooserColumn = getFieldChooserColumnForRow(rows.length - 1);
+ // 7.
+ lastRowFieldChooserColumn.reactWrapper.find('input').forEach((input) => {
+ const props = input.props();
+ if (
+ props['data-test-subj'].endsWith('max') ||
+ props['data-test-subj'].endsWith('All')
+ ) {
+ expect(props.checked).toBe(false);
+ } else {
+ expect(props.checked).toBe(true);
+ }
+ });
+ });
+ });
});
});
diff --git a/x-pack/legacy/plugins/rollup/public/crud_app/constants/index.js b/x-pack/legacy/plugins/rollup/public/crud_app/constants/index.js
index f71725d23b4e0..9b2b10d3ae742 100644
--- a/x-pack/legacy/plugins/rollup/public/crud_app/constants/index.js
+++ b/x-pack/legacy/plugins/rollup/public/crud_app/constants/index.js
@@ -7,3 +7,7 @@
export {
CRUD_APP_BASE_PATH,
} from './paths';
+
+export {
+ METRICS_CONFIG
+} from './metrics_config';
diff --git a/x-pack/legacy/plugins/rollup/public/crud_app/constants/metrics_config.js b/x-pack/legacy/plugins/rollup/public/crud_app/constants/metrics_config.js
new file mode 100644
index 0000000000000..f0931252a2d1e
--- /dev/null
+++ b/x-pack/legacy/plugins/rollup/public/crud_app/constants/metrics_config.js
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const METRICS_CONFIG = [
+ {
+ type: 'avg',
+ label: i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.checkboxAverageLabel',
+ { defaultMessage: 'Average' },
+ ),
+ },
+ {
+ type: 'max',
+ label: i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.checkboxMaxLabel',
+ { defaultMessage: 'Maximum' },
+ ),
+ },
+ {
+ type: 'min',
+ label: i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.checkboxMinLabel',
+ { defaultMessage: 'Minimum' },
+ ),
+ },
+ {
+ type: 'sum',
+ label: i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.checkboxSumLabel',
+ { defaultMessage: 'Sum' },
+ ),
+ },
+ {
+ type: 'value_count',
+ label: i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.checkboxValueCountLabel',
+ { defaultMessage: 'Value count' },
+ ),
+ },
+];
diff --git a/x-pack/legacy/plugins/rollup/public/crud_app/sections/components/field_list/field_list.js b/x-pack/legacy/plugins/rollup/public/crud_app/sections/components/field_list/field_list.js
index 5734cdb1ec0a6..6f3834971917f 100644
--- a/x-pack/legacy/plugins/rollup/public/crud_app/sections/components/field_list/field_list.js
+++ b/x-pack/legacy/plugins/rollup/public/crud_app/sections/components/field_list/field_list.js
@@ -45,7 +45,7 @@ export const FieldList = ({
type: 'icon',
color: 'danger',
onClick: (field) => onRemoveField(field),
- }]
+ }],
});
} else {
extendedColumns = columns;
diff --git a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_create/steps/components/field_chooser.js b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_create/steps/components/field_chooser.js
index 6f56b9a204588..6d6b77ab2e047 100644
--- a/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_create/steps/components/field_chooser.js
+++ b/x-pack/legacy/plugins/rollup/public/crud_app/sections/job_create/steps/components/field_chooser.js
@@ -25,7 +25,6 @@ export class FieldChooser extends Component {
fields: PropTypes.array.isRequired,
selectedFields: PropTypes.array.isRequired,
onSelectField: PropTypes.func.isRequired,
- columns: PropTypes.array.isRequired,
prompt: PropTypes.string,
dataTestSubj: PropTypes.string,
}
@@ -136,7 +135,6 @@ export class FieldChooser extends Component {
);
};
-
return (
{
+ return !!get(whiteListedMetricByFieldType, [fieldType, metricType]);
+};
+
+// We use an IFFE to associate metricType configs with their
+// associated field types. After processing each of these
+// objects should have a fieldTypes: { date: true, numeric: true }
+// like object.
+const metricTypesConfig = (function () {
+ return METRICS_CONFIG.map(config => {
+ const fieldTypes = {};
+ for (const [fieldType, metrics] of Object.entries(whiteListedMetricByFieldType)) {
+ fieldTypes[fieldType] = !!metrics[config.type];
+ }
+ return {
+ ...config,
+ fieldTypes,
+ };
+ });
+}());
+
export class StepMetricsUi extends Component {
static propTypes = {
fields: PropTypes.object.isRequired,
@@ -54,99 +72,218 @@ export class StepMetricsUi extends Component {
fieldErrors: PropTypes.object.isRequired,
areStepErrorsVisible: PropTypes.bool.isRequired,
metricsFields: PropTypes.array.isRequired,
- }
+ };
constructor(props) {
super(props);
- this.chooserColumns = [{
- field: 'name',
- name: 'Field',
- sortable: true,
- width: '240px',
- }, {
- field: 'type',
- name: 'Type',
- truncateText: true,
- sortable: true,
- width: '100px',
- }];
-
- const metricTypesConfig = [
- {
- type: 'avg',
- label: (
-
- ),
- }, {
- type: 'max',
- label: (
-
- ),
- }, {
- type: 'min',
- label: (
-
- ),
- }, {
- type: 'sum',
- label: (
-
- ),
- }, {
- type: 'value_count',
- label: (
-
- ),
- },
- ];
+ this.state = {
+ metricsPopoverOpen: false,
+ listColumns: [],
+ selectedMetricsMap: [],
+ };
+ }
- this.listColumns = this.chooserColumns.concat({
- type: 'metrics',
- name: 'Metrics',
- render: ({ name: fieldName, type: fieldType, types }) => {
- const checkboxes = metricTypesConfig.map(({ type, label }) => {
- const isAllowed = whiteListedMetricByFieldType[fieldType][type];
+ openMetricsPopover = () => {
+ this.setState({
+ metricsPopoverOpen: true,
+ });
+ };
+
+ closeMetricsPopover = () => {
+ this.setState({
+ metricsPopoverOpen: false,
+ });
+ };
- if (!isAllowed) {
- return;
+ renderMetricsSelectAllCheckboxes() {
+ const {
+ fields: { metrics },
+ onFieldsChange,
+ } = this.props;
+
+ let disabledCheckboxesCount = 0;
+
+ /**
+ * Look at all the metric configs and include the special "All" checkbox which adds the ability
+ * to select all the checkboxes across columns and rows.
+ */
+ const checkboxElements = [{
+ label: i18n.translate('xpack.rollupJobs.create.stepMetrics.allCheckbox', { defaultMessage: 'All' }),
+ type: 'all',
+ }].concat(metricTypesConfig).map(({ label, type: metricType, fieldTypes }, idx) => {
+ const isAllMetricTypes = metricType === 'all';
+ // For this config we are either considering all user selected metrics or a subset.
+ const applicableMetrics = isAllMetricTypes
+ ? metrics
+ : metrics
+ .filter(({ type }) => {
+ return fieldTypes[type];
+ });
+
+ let checkedCount = 0;
+ let isChecked = false;
+ let isDisabled = false;
+
+ if (isAllMetricTypes) {
+ applicableMetrics.forEach(({ types, type }) => {
+ const whiteListedSubset = Object.keys(whiteListedMetricByFieldType[type]);
+ if (whiteListedSubset.every(metricName => types.some(type => type === metricName))) {
+ ++checkedCount;
}
+ });
+
+ isDisabled = metrics.length === 0;
+ } else {
+ applicableMetrics.forEach(({ types }) => {
+ const metricSelected = types.some(type => type === metricType);
+ if (metricSelected) {
+ ++checkedCount;
+ }
+ });
+
+ isDisabled = !metrics.some(({ type: fieldType }) =>
+ checkWhiteListedMetricByFieldType(fieldType, metricType),
+ );
+ }
+
+ // Determine if a select all checkbox is checked.
+ isChecked = checkedCount === applicableMetrics.length;
+
+ if (isDisabled) ++disabledCheckboxesCount;
+
+ return (
+ {
+ if (isAllMetricTypes) {
+ const newMetrics = metricTypesConfig
+ .reduce((acc, { type }) => this.setMetrics(type, !isChecked), null);
+ onFieldsChange({ metrics: newMetrics });
+ } else {
+ onFieldsChange({ metrics: this.setMetrics(metricType, !isChecked) });
+ }
+ }}
+ />
+ );
+ });
- const isSelected = types.includes(type);
+ return {
+ checkboxElements,
+ allCheckboxesDisabled: checkboxElements.length === disabledCheckboxesCount,
+ };
+ }
+
+ getMetricsSelectAllMenu() {
+ const {
+ checkboxElements,
+ allCheckboxesDisabled
+ } = this.renderMetricsSelectAllCheckboxes();
- return (
-
+
- this.setMetric(fieldName, type, !isSelected)}
- />
-
- );
- }).filter(checkbox => checkbox !== undefined);
+ {
+ i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.selectAllPopoverButtonLabel',
+ { defaultMessage: 'Select metrics' })
+ }
+
+ }
+ >
+
+ {
+ checkboxElements
+ .map((item, idx) => {item})
+ }
+
+
+
+ );
+ }
+
+ renderRowSelectAll({ fieldName, fieldType, types }) {
+ const { onFieldsChange } = this.props;
+ const hasSelectedItems = Boolean(types.length);
+ const maxItemsToBeSelected = Object.keys(whiteListedMetricByFieldType[fieldType]).length;
+ const allSelected = maxItemsToBeSelected === types.length;
+
+ const label = i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.selectAllRowLabel',
+ { defaultMessage: 'All' },
+ );
+
+ const onChange = () => {
+ const isSelected = hasSelectedItems ? types.length !== maxItemsToBeSelected : true;
+ const newMetrics = metricTypesConfig
+ .filter(config => config.fieldTypes[fieldType])
+ .reduce((acc, { type: typeConfig }) => {
+ return this.setMetric(fieldName, typeConfig, isSelected);
+ }, null);
+ onFieldsChange({ metric: newMetrics });
+ };
+
+ return (
+
+ );
+ }
+
+ getListColumns() {
+ return StepMetricsUi.chooserColumns.concat({
+ type: 'metrics',
+ name: i18n.translate('xpack.rollupJobs.create.stepMetrics.metricsColumnHeader', { defaultMessage: 'Metrics' }),
+ render: ({ name: fieldName, type: fieldType, types }) => {
+ const { onFieldsChange } = this.props;
+ const checkboxes = metricTypesConfig
+ .map(({ type, label }) => {
+ const isAllowed = checkWhiteListedMetricByFieldType(fieldType, type);
+
+ if (!isAllowed) {
+ return;
+ }
+
+ const isSelected = types.includes(type);
+
+ return (
+
+
+ onFieldsChange({ metrics: this.setMetric(fieldName, type, !isSelected) })
+ }
+ />
+
+ );
+ })
+ .filter(checkbox => checkbox !== undefined);
return (
+
+ {this.renderRowSelectAll({ fieldName, fieldType, types })}
+
{checkboxes}
);
@@ -154,7 +291,7 @@ export class StepMetricsUi extends Component {
});
}
- onSelectField = (field) => {
+ onSelectField = field => {
const {
fields: { metrics },
onFieldsChange,
@@ -168,7 +305,7 @@ export class StepMetricsUi extends Component {
onFieldsChange({ metrics: newMetrics });
};
- onRemoveField = (field) => {
+ onRemoveField = field => {
const {
fields: { metrics },
onFieldsChange,
@@ -179,33 +316,42 @@ export class StepMetricsUi extends Component {
onFieldsChange({ metrics: newMetrics });
};
+ setMetrics(metricType, isSelected) {
+ const {
+ fields: { metrics: fields },
+ } = this.props;
+
+ return fields
+ .filter(field => checkWhiteListedMetricByFieldType(field.type, metricType))
+ .reduce((acc, metric) => {
+ return this.setMetric(metric.name, metricType, isSelected);
+ }, []);
+ }
+
setMetric = (fieldName, metricType, isSelected) => {
const {
fields: { metrics },
- onFieldsChange,
} = this.props;
- const newMetrics = [ ...metrics ];
- const { types: updatedTypes } = newMetrics.find(({ name }) => name === fieldName);
+ const newMetrics = [...metrics];
+ const newMetric = newMetrics.find(({ name }) => name === fieldName);
+ // Update copied object by reference
if (isSelected) {
- updatedTypes.push(metricType);
+ // Don't add duplicates.
+ if (newMetric.types.indexOf(metricType) === -1) {
+ newMetric.types.push(metricType);
+ }
} else {
- updatedTypes.splice(updatedTypes.indexOf(metricType), 1);
+ newMetric.types.splice(newMetric.types.indexOf(metricType), 1);
}
-
- onFieldsChange({ metrics: newMetrics });
+ return newMetrics;
};
render() {
- const {
- fields,
- metricsFields,
- } = this.props;
+ const { fields, metricsFields } = this.props;
- const {
- metrics,
- } = fields;
+ const { metrics } = fields;
return (
@@ -220,7 +366,7 @@ export class StepMetricsUi extends Component {
-
+
@@ -250,28 +396,51 @@ export class StepMetricsUi extends Component {
-
+
No metrics fields added
}
- addButton={(
-
+ {
+ i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.emptyListLabel',
+ { defaultMessage: 'No metrics fields added' },
+ )
+ }
+
+ }
+
+ addButton={
+
+
+
+ }
+ columns={StepMetricsUi.chooserColumns}
+ fields={metricsFields}
+ selectedFields={metrics}
+ onSelectField={this.onSelectField}
+ dataTestSubj="rollupJobMetricsFieldChooser"
/>
- )}
- columns={this.chooserColumns}
- fields={metricsFields}
- selectedFields={metrics}
- onSelectField={this.onSelectField}
- dataTestSubj="rollupJobMetricsFieldChooser"
- />
- )}
+
+
+ {this.getMetricsSelectAllMenu()}
+
+
+ }
+
dataTestSubj="rollupJobMetricsFieldList"
/>
@@ -290,8 +459,30 @@ export class StepMetricsUi extends Component {
return null;
}
- return ;
- }
+ return ;
+ };
+
+ static chooserColumns = [
+ {
+ field: 'name',
+ name: i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.fieldColumnLabel',
+ { defaultMessage: 'Field' }
+ ),
+ sortable: true,
+ width: '240px',
+ },
+ {
+ field: 'type',
+ name: i18n.translate(
+ 'xpack.rollupJobs.create.stepMetrics.typeColumnLabel',
+ { defaultMessage: 'Type' }
+ ),
+ truncateText: true,
+ sortable: true,
+ width: '100px',
+ },
+ ];
}
export const StepMetrics = injectI18n(StepMetricsUi);