Skip to content

Commit 0743e30

Browse files
committed
Polish form UX in Rollup Job Wizard and list (#23196)
* Polish job list by adding loading and empty states. * Refine layout of Summary tab and use tables in Terms, Histogram, and Metrics tabs. * Persist terms, metrics, and histogram tables with an empty prompt when no fields are selected. * Include JSON tab in Review step. * Re-label docs links so it's clear that they pertain specifically to the section they're in. * Use trash icon for removing fields. Fix 'histgogram' typo. * Position field chooser on right side of the section header. - Keep field chooser open as user selects fields. - Update SearchSelect to allow consumer to specify anchorPosition and make row text blue on hover. * Present timezones in a select. * Prompt user to opt-in to customizing Data Storage section. * Change ID section to Name and add explanatory text. * Dynamically update index pattern help text and errors to emphasize to the user that it's async validation. * Link to cron syntax and validate expression. * Suggest valid interval values and suggest fixed intervals when calendar intervals are used for Date Histogram interval. * Validate that page size and histogram interval are greater than 0. * Validate that types have been selected for chosen metrics in Metrics step. * Fix bugs: - Fix bug in serialization logic. - Fix bug in which refresh interval caused an open detail panel to re-open. - Fix bug with string value passed to page size number input. - Fix bug with auto refresh interval resetting the detail panel's selected tab.
1 parent 1d573af commit 0743e30

38 files changed

+1056
-592
lines changed

src/ui/public/utils/parse_es_interval/parse_es_interval.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function parseEsInterval(interval: string): { value: number; unit: string
5959
const type = unit && dateMath.unitsMap[unit].type;
6060

6161
if (type === 'calendar' && value !== 1) {
62-
throw new ParseEsIntervalInvalidCalendarIntervalError(interval);
62+
throw new ParseEsIntervalInvalidCalendarIntervalError(interval, value, unit, type);
6363
}
6464

6565
return {

src/ui/public/utils/parse_es_interval/parse_es_interval_invalid_calendar_interval_error.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,18 @@
1818
*/
1919

2020
export class ParseEsIntervalInvalidCalendarIntervalError extends Error {
21-
constructor(public readonly interval: string) {
21+
constructor(
22+
public readonly interval: string,
23+
public readonly value: number,
24+
public readonly unit: string,
25+
public readonly type: string
26+
) {
2227
super(`Invalid calendar interval: ${interval}, value must be 1`);
28+
2329
this.name = 'ParseEsIntervalInvalidCalendarIntervalError';
30+
this.value = value;
31+
this.unit = unit;
32+
this.type = type;
2433

2534
// captureStackTrace is only available in the V8 engine, so any browser using
2635
// a different JS engine won't have access to this method.

x-pack/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
"classnames": "2.2.5",
115115
"concat-stream": "1.5.1",
116116
"copy-to-clipboard": "^3.0.8",
117+
"cronstrue": "^1.51.0",
117118
"d3": "3.5.6",
118119
"d3-scale": "1.0.6",
119120
"dedent": "^0.7.0",

x-pack/plugins/rollup/public/crud_app/index.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@
55
max-width: 1000px !important; /* 1 */
66
width: 100% !important; /* 1 */
77
}
8+
9+
.rollupJobWizardStepActions {
10+
align-items: flex-end;
11+
}
Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,49 @@ import PropTypes from 'prop-types';
99

1010
import {
1111
EuiInMemoryTable,
12-
EuiSpacer,
12+
EuiEmptyPrompt,
1313
} from '@elastic/eui';
1414

1515
export const FieldList = ({
1616
columns,
1717
fields,
1818
onRemoveField,
19+
addButton,
20+
emptyMessage,
1921
}) => {
22+
let message;
23+
2024
if (!fields.length) {
21-
return null;
25+
message = (
26+
<EuiEmptyPrompt
27+
title={emptyMessage}
28+
titleSize="xs"
29+
/>
30+
);
2231
}
2332

24-
const extendedColumns = columns.concat({
25-
name: 'Remove',
26-
width: '80px',
27-
actions: [{
33+
let extendedColumns;
34+
35+
if (onRemoveField) {
36+
extendedColumns = columns.concat({
2837
name: 'Remove',
29-
isPrimary: true,
30-
description: 'Remove this field',
31-
icon: 'cross',
32-
type: 'icon',
33-
color: 'danger',
34-
onClick: (field) => onRemoveField(field),
35-
}]
36-
});
38+
width: '80px',
39+
actions: [{
40+
name: 'Remove',
41+
isPrimary: true,
42+
description: 'Remove this field',
43+
icon: 'trash',
44+
type: 'icon',
45+
color: 'danger',
46+
onClick: (field) => onRemoveField(field),
47+
}]
48+
});
49+
} else {
50+
extendedColumns = columns;
51+
}
3752

3853
const search = {
54+
toolsRight: addButton ? addButton : undefined,
3955
box: {
4056
incremental: true,
4157
},
@@ -48,15 +64,14 @@ export const FieldList = ({
4864

4965
return (
5066
<Fragment>
51-
<EuiSpacer />
52-
5367
<EuiInMemoryTable
5468
items={fields}
5569
itemId="name"
5670
columns={extendedColumns}
5771
search={search}
5872
pagination={pagination}
5973
sorting={true}
74+
message={message}
6075
/>
6176
</Fragment>
6277
);
@@ -65,5 +80,7 @@ export const FieldList = ({
6580
FieldList.propTypes = {
6681
columns: PropTypes.array.isRequired,
6782
fields: PropTypes.array.isRequired,
68-
onRemoveField: PropTypes.func.isRequired,
83+
onRemoveField: PropTypes.func,
84+
addButton: PropTypes.node,
85+
emptyMessage: PropTypes.node,
6986
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export { FieldList } from './field_list';

x-pack/plugins/rollup/public/crud_app/sections/components/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7+
export { FieldList } from './field_list';
8+
79
export { JobActionMenu } from './job_action_menu';
810

911
export {

x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_histogram.js

Lines changed: 35 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,60 +8,43 @@ import React, { Fragment } from 'react';
88
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
99

1010
import {
11-
EuiTitle,
11+
EuiDescriptionList,
12+
EuiDescriptionListTitle,
13+
EuiDescriptionListDescription,
1214
EuiSpacer,
13-
EuiText,
14-
EuiFlexGroup,
15-
EuiFlexItem,
1615
} from '@elastic/eui';
1716

18-
export const TabHistogramUi = ({ histogram, histogramInterval }) => {
19-
// TODO: Render a table if there are more than 20 fields.
20-
21-
const renderedTerms = histogram.map(({ name }) => {
22-
return (
23-
<li key={name}>
24-
{name}
25-
</li>
26-
);
27-
});
28-
29-
return (
30-
<Fragment>
31-
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
32-
<EuiFlexItem grow={false}>
33-
<EuiTitle size="s">
34-
<h3>
35-
<FormattedMessage
36-
id="xpack.rollupJobs.jobDetails.tabHistogram.sectionHistogram.title"
37-
defaultMessage="Histogram"
38-
/>
39-
</h3>
40-
</EuiTitle>
41-
</EuiFlexItem>
42-
43-
<EuiFlexItem grow={false}>
44-
<EuiText>
45-
<p>
46-
<FormattedMessage
47-
id="xpack.rollupJobs.jobDetails.tabHistogram.interval.label"
48-
defaultMessage="Interval: {histogramInterval}"
49-
values={{ histogramInterval }}
50-
/>
51-
</p>
52-
</EuiText>
53-
</EuiFlexItem>
54-
</EuiFlexGroup>
55-
56-
<EuiSpacer size="s" />
57-
58-
<EuiText>
59-
<ul>
60-
{renderedTerms}
61-
</ul>
62-
</EuiText>
63-
</Fragment>
64-
);
65-
};
17+
import { FieldList } from '../../field_list';
18+
19+
const columns = [{
20+
field: 'name',
21+
name: 'Field',
22+
truncateText: true,
23+
sortable: true,
24+
}];
25+
26+
export const TabHistogramUi = ({ histogram, histogramInterval }) => (
27+
<Fragment>
28+
<EuiDescriptionList textStyle="reverse">
29+
<EuiDescriptionListTitle>
30+
<FormattedMessage
31+
id="xpack.rollupJobs.jobDetails.tabHistogram.interval.label"
32+
defaultMessage="Histogram interval"
33+
/>
34+
</EuiDescriptionListTitle>
35+
36+
<EuiDescriptionListDescription>
37+
{histogramInterval}
38+
</EuiDescriptionListDescription>
39+
</EuiDescriptionList>
40+
41+
<EuiSpacer size="l" />
42+
43+
<FieldList
44+
columns={columns}
45+
fields={histogram}
46+
/>
47+
</Fragment>
48+
);
6649

6750
export const TabHistogram = injectI18n(TabHistogramUi);

x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_json.js

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import React, { Fragment } from 'react';
8-
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
7+
import React from 'react';
8+
import { injectI18n } from '@kbn/i18n/react';
99

1010
import {
11-
EuiTitle,
12-
EuiSpacer,
1311
EuiCodeEditor,
1412
} from '@elastic/eui';
1513

@@ -19,29 +17,16 @@ export const TabJsonUi = ({
1917
const jsonString = JSON.stringify(json, null, 2);
2018

2119
return (
22-
<Fragment>
23-
<EuiTitle size="s">
24-
<h3>
25-
<FormattedMessage
26-
id="xpack.rollupJobs.jobDetails.tabJson.sectionJson.title"
27-
defaultMessage="JSON"
28-
/>
29-
</h3>
30-
</EuiTitle>
31-
32-
<EuiSpacer size="s" />
33-
34-
<EuiCodeEditor
35-
mode="json"
36-
theme="textmate"
37-
isReadOnly
38-
setOptions={{ maxLines: Infinity }}
39-
value={jsonString}
40-
editorProps={{
41-
$blockScrolling: Infinity
42-
}}
43-
/>
44-
</Fragment>
20+
<EuiCodeEditor
21+
mode="json"
22+
theme="textmate"
23+
isReadOnly
24+
setOptions={{ maxLines: Infinity }}
25+
value={jsonString}
26+
editorProps={{
27+
$blockScrolling: Infinity
28+
}}
29+
/>
4530
);
4631
};
4732

x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_metrics.js

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,26 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import React, { Fragment } from 'react';
8-
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
7+
import React from 'react';
8+
import { injectI18n } from '@kbn/i18n/react';
99

10-
import {
11-
EuiDescriptionList,
12-
EuiTitle,
13-
EuiSpacer,
14-
} from '@elastic/eui';
10+
import { FieldList } from '../../field_list';
1511

16-
export const TabMetricsUi = ({ metrics }) => {
17-
// TODO: Render a table if there are more than 20 metrics.
18-
const listMetrics = metrics.map(({ name, types }) => {
19-
return {
20-
title: name,
21-
description: types.join(', '),
22-
};
23-
});
12+
const columns = [{
13+
field: 'name',
14+
name: 'Field',
15+
truncateText: true,
16+
sortable: true,
17+
}, {
18+
name: 'Types',
19+
render: ({ types }) => types.join(', '),
20+
}];
2421

25-
return (
26-
<Fragment>
27-
<EuiTitle size="s">
28-
<h3>
29-
<FormattedMessage
30-
id="xpack.rollupJobs.jobDetails.tabMetrics.sectionMetrics.title"
31-
defaultMessage="Metrics"
32-
/>
33-
</h3>
34-
</EuiTitle>
35-
36-
<EuiSpacer size="s" />
37-
38-
<EuiDescriptionList listItems={listMetrics} />
39-
</Fragment>
40-
);
41-
};
22+
export const TabMetricsUi = ({ metrics }) => (
23+
<FieldList
24+
columns={columns}
25+
fields={metrics}
26+
/>
27+
);
4228

4329
export const TabMetrics = injectI18n(TabMetricsUi);

0 commit comments

Comments
 (0)