Skip to content

Commit b50c90e

Browse files
[7.x] [Index template] Add filters to simulate preview (#74497) (#75432)
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 24fdac2 commit b50c90e

File tree

13 files changed

+177
-54
lines changed

13 files changed

+177
-54
lines changed

x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ $heightHeader: $euiSizeL * 2;
88

99
.componentTemplates {
1010
border: $euiBorderThin;
11+
border-radius: $euiBorderRadius;
1112
border-top: none;
1213
height: 100%;
1314

x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.scss

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,31 @@
77

88
&__selection {
99
border: $euiBorderThin;
10+
border-radius: $euiBorderRadius;
1011

11-
padding: 0 $euiSize $euiSize;
12-
color: $euiColorDarkShade;
12+
padding: 0 $euiSize $euiSize;
13+
color: $euiColorDarkShade;
1314

14-
&--is-empty {
15-
align-items: center;
16-
justify-content: center;
17-
}
15+
&--is-empty {
16+
align-items: center;
17+
justify-content: center;
18+
}
1819

19-
&__header {
20-
background-color: $euiColorLightestShade;
21-
border-bottom: $euiBorderThin;
22-
color: $euiColorInk;
23-
height: $euiSizeXXL; // [1]
24-
line-height: $euiSizeXXL; // [1]
25-
font-size: $euiSizeM;
26-
margin-bottom: $euiSizeS;
27-
margin-left: $euiSize * -1;
28-
margin-right: $euiSize * -1;
29-
padding-left: $euiSize;
20+
&__header {
21+
background-color: $euiColorLightestShade;
22+
border-bottom: $euiBorderThin;
23+
color: $euiColorInk;
24+
height: $euiSizeXXL; // [1]
25+
line-height: $euiSizeXXL; // [1]
26+
font-size: $euiSizeM;
27+
margin-bottom: $euiSizeS;
28+
margin-left: $euiSize * -1;
29+
margin-right: $euiSize * -1;
30+
padding-left: $euiSize;
3031

31-
&__count {
32-
font-weight: 600;
33-
}
32+
&__count {
33+
font-weight: 600;
34+
}
3435
}
3536

3637
&__content {

x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,19 @@ export const ComponentTemplatesSelector = ({
200200
</div>
201201
</>
202202
) : (
203-
<div>
204-
<FormattedMessage
205-
id="xpack.idxMgmt.componentTemplatesSelector.noComponentSelectedLabel"
206-
defaultMessage="No component template selected."
207-
/>
208-
</div>
203+
<EuiText textAlign="center">
204+
<p>
205+
<FormattedMessage
206+
id="xpack.idxMgmt.componentTemplatesSelector.noComponentSelectedLabel-1"
207+
defaultMessage="Add component template building blocks to this template."
208+
/>
209+
<br />
210+
<FormattedMessage
211+
id="xpack.idxMgmt.componentTemplatesSelector.noComponentSelectedLabel-2"
212+
defaultMessage="Component templates are applied in the order specified."
213+
/>
214+
</p>
215+
</EuiText>
209216
)}
210217
</EuiFlexItem>
211218

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
export {
7+
useForm,
8+
Form,
9+
getUseField,
10+
FormDataProvider,
11+
} from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
12+
13+
export { CheckBoxField } from '../../../../../../../src/plugins/es_ui_shared/static/forms/components';

x-pack/plugins/index_management/public/application/components/index_templates/simulate_template/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ export {
1010
Props as SimulateTemplateProps,
1111
} from './simulate_template_flyout';
1212

13-
export { SimulateTemplate } from './simulate_template';
13+
export { SimulateTemplate, Filters as SimulateTemplateFilters } from './simulate_template';

x-pack/plugins/index_management/public/application/components/index_templates/simulate_template/simulate_template.tsx

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
*/
66
import React, { useState, useCallback, useEffect } from 'react';
77
import uuid from 'uuid';
8-
import { EuiCodeBlock } from '@elastic/eui';
8+
import { FormattedMessage } from '@kbn/i18n/react';
9+
import { EuiCodeBlock, EuiCallOut } from '@elastic/eui';
910

1011
import { serializers } from '../../../../shared_imports';
1112
import { TemplateDeserialized } from '../../../../../common';
@@ -14,12 +15,18 @@ import { simulateIndexTemplate } from '../../../services';
1415

1516
const { stripEmptyFields } = serializers;
1617

18+
export interface Filters {
19+
mappings: boolean;
20+
settings: boolean;
21+
aliases: boolean;
22+
}
23+
1724
interface Props {
1825
template: { [key: string]: any };
19-
minHeightCodeBlock?: string;
26+
filters?: Filters;
2027
}
2128

22-
export const SimulateTemplate = React.memo(({ template, minHeightCodeBlock }: Props) => {
29+
export const SimulateTemplate = React.memo(({ template, filters }: Props) => {
2330
const [templatePreview, setTemplatePreview] = useState('{}');
2431

2532
const updatePreview = useCallback(async () => {
@@ -34,26 +41,60 @@ export const SimulateTemplate = React.memo(({ template, minHeightCodeBlock }: Pr
3441
indexTemplate.index_patterns = [uuid.v4()];
3542

3643
const { data, error } = await simulateIndexTemplate(indexTemplate);
44+
let filteredTemplate = data;
3745

3846
if (data) {
3947
// "Overlapping" info is only useful when simulating against an index
4048
// which we don't do here.
4149
delete data.overlapping;
50+
51+
if (data.template && data.template.mappings === undefined) {
52+
// Adding some extra logic to return an empty object for "mappings" as ES does not
53+
// return one in that case (empty objects _are_ returned for "settings" and "aliases")
54+
// Issue: https://github.com/elastic/elasticsearch/issues/60968
55+
data.template.mappings = {};
56+
}
57+
58+
if (filters) {
59+
filteredTemplate = Object.entries(filters).reduce(
60+
(acc, [key, value]) => {
61+
if (!value) {
62+
delete acc[key];
63+
}
64+
return acc;
65+
},
66+
{ ...data.template } as any
67+
);
68+
}
4269
}
4370

44-
setTemplatePreview(JSON.stringify(data ?? error, null, 2));
45-
}, [template]);
71+
setTemplatePreview(JSON.stringify(filteredTemplate ?? error, null, 2));
72+
}, [template, filters]);
4673

4774
useEffect(() => {
4875
updatePreview();
4976
}, [updatePreview]);
5077

51-
return templatePreview === '{}' ? null : (
52-
<EuiCodeBlock
53-
style={{ minHeight: minHeightCodeBlock }}
54-
lang="json"
55-
data-test-subj="simulateTemplatePreview"
56-
>
78+
const isEmpty = templatePreview === '{}';
79+
const hasFilters = Boolean(filters);
80+
81+
if (isEmpty && hasFilters) {
82+
return (
83+
<EuiCallOut
84+
title={
85+
<FormattedMessage
86+
id="xpack.idxMgmt.simulateTemplate.noFilterSelected"
87+
defaultMessage="Select at least one option to preview."
88+
/>
89+
}
90+
iconType="pin"
91+
size="s"
92+
/>
93+
);
94+
}
95+
96+
return isEmpty ? null : (
97+
<EuiCodeBlock lang="json" data-test-subj="simulateTemplatePreview">
5798
{templatePreview}
5899
</EuiCodeBlock>
59100
);

x-pack/plugins/index_management/public/application/components/index_templates/simulate_template/simulate_template_flyout.tsx

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
import React, { useState, useCallback, useEffect, useRef } from 'react';
77
import { FormattedMessage } from '@kbn/i18n/react';
8+
import { i18n } from '@kbn/i18n';
89
import {
910
EuiFlyoutHeader,
1011
EuiTitle,
@@ -19,28 +20,56 @@ import {
1920
EuiSpacer,
2021
} from '@elastic/eui';
2122

22-
import { SimulateTemplate } from './simulate_template';
23+
import { useForm, Form, getUseField, CheckBoxField, FormDataProvider } from '../shared_imports';
24+
import { SimulateTemplate, Filters } from './simulate_template';
25+
26+
const CheckBox = getUseField({ component: CheckBoxField });
2327

2428
export interface Props {
2529
onClose(): void;
2630
getTemplate: () => { [key: string]: any };
31+
filters: Filters;
32+
onFiltersChange: (filters: Filters) => void;
2733
}
2834

2935
export const defaultFlyoutProps = {
3036
'data-test-subj': 'simulateTemplateFlyout',
3137
'aria-labelledby': 'simulateTemplateFlyoutTitle',
3238
};
3339

34-
export const SimulateTemplateFlyoutContent = ({ onClose, getTemplate }: Props) => {
40+
const i18nTexts = {
41+
filters: {
42+
label: i18n.translate('xpack.idxMgmt.simulateTemplate.filters.label', {
43+
defaultMessage: 'Include:',
44+
}),
45+
mappings: i18n.translate('xpack.idxMgmt.simulateTemplate.filters.mappings', {
46+
defaultMessage: 'Mappings',
47+
}),
48+
indexSettings: i18n.translate('xpack.idxMgmt.simulateTemplate.filters.indexSettings', {
49+
defaultMessage: 'Index settings',
50+
}),
51+
aliases: i18n.translate('xpack.idxMgmt.simulateTemplate.filters.aliases', {
52+
defaultMessage: 'Aliases',
53+
}),
54+
},
55+
};
56+
57+
export const SimulateTemplateFlyoutContent = ({
58+
onClose,
59+
getTemplate,
60+
filters,
61+
onFiltersChange,
62+
}: Props) => {
3563
const isMounted = useRef(false);
36-
const [heightCodeBlock, setHeightCodeBlock] = useState(0);
3764
const [template, setTemplate] = useState<{ [key: string]: any }>({});
65+
const { form } = useForm<Filters>({ defaultValue: filters });
66+
const { subscribe } = form;
3867

3968
useEffect(() => {
40-
setHeightCodeBlock(
41-
document.getElementsByClassName('euiFlyoutBody__overflow')[0].clientHeight - 96
42-
);
43-
}, []);
69+
subscribe((formState) => {
70+
onFiltersChange(formState.data.format());
71+
});
72+
}, [subscribe, onFiltersChange]);
4473

4574
const updatePreview = useCallback(async () => {
4675
const indexTemplate = await getTemplate();
@@ -71,16 +100,37 @@ export const SimulateTemplateFlyoutContent = ({ onClose, getTemplate }: Props) =
71100
<p>
72101
<FormattedMessage
73102
id="xpack.idxMgmt.simulateTemplate.descriptionText"
74-
defaultMessage="This is the final template that will be applied to your indices based on the
75-
components templates you have selected and any overrides you've added."
103+
defaultMessage="This is the final template that will be applied to matching indices based on the
104+
component templates you have selected and any overrides you've added."
76105
/>
77106
</p>
78107
</EuiText>
79108
</EuiTextColor>
80109
</EuiFlyoutHeader>
81110

82111
<EuiFlyoutBody data-test-subj="content">
83-
<SimulateTemplate template={template} minHeightCodeBlock={`${heightCodeBlock}px`} />
112+
<Form form={form}>
113+
<EuiFlexGroup alignItems="center">
114+
<EuiFlexItem grow={false}>{i18nTexts.filters.label}</EuiFlexItem>
115+
<EuiFlexItem grow={false}>
116+
<CheckBox path="mappings" config={{ label: i18nTexts.filters.mappings }} />
117+
</EuiFlexItem>
118+
<EuiFlexItem grow={false}>
119+
<CheckBox path="settings" config={{ label: i18nTexts.filters.indexSettings }} />
120+
</EuiFlexItem>
121+
<EuiFlexItem grow={false}>
122+
<CheckBox path="aliases" config={{ label: i18nTexts.filters.aliases }} />
123+
</EuiFlexItem>
124+
</EuiFlexGroup>
125+
126+
<EuiSpacer />
127+
128+
<FormDataProvider>
129+
{(formData) => {
130+
return <SimulateTemplate template={template} filters={formData as Filters} />;
131+
}}
132+
</FormDataProvider>
133+
</Form>
84134
</EuiFlyoutBody>
85135

86136
<EuiFlyoutFooter>

x-pack/plugins/index_management/public/application/components/template_form/steps/step_components.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const i18nTexts = {
3434
description: (
3535
<FormattedMessage
3636
id="xpack.idxMgmt.formWizard.stepComponents.componentsDescription"
37-
defaultMessage="Components templates let you save index settings, mappings and aliases and inherit from them in index templates."
37+
defaultMessage="Component templates let you save index settings, mappings and aliases and inherit from them in index templates."
3838
/>
3939
),
4040
};

x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const PreviewTab = ({ template }: { template: { [key: string]: any } }) => {
6767
<p>
6868
<FormattedMessage
6969
id="xpack.idxMgmt.templateForm.stepReview.previewTab.descriptionText"
70-
defaultMessage="This is the final template that will be applied to your indices."
70+
defaultMessage="This is the final template that will be applied to matching indices. Component templates are applied in the order specified. Explicit mappings, settings, and aliases override the component templates."
7171
/>
7272
</p>
7373
</EuiText>

x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* or more contributor license agreements. Licensed under the Elastic License;
44
* you may not use this file except in compliance with the Elastic License.
55
*/
6-
import React, { useState, useCallback } from 'react';
6+
import React, { useState, useCallback, useRef } from 'react';
77
import { i18n } from '@kbn/i18n';
88
import { FormattedMessage } from '@kbn/i18n/react';
99
import { EuiSpacer, EuiButton } from '@elastic/eui';
@@ -15,6 +15,7 @@ import {
1515
SimulateTemplateFlyoutContent,
1616
SimulateTemplateProps,
1717
simulateTemplateFlyoutProps,
18+
SimulateTemplateFilters,
1819
} from '../index_templates';
1920
import { StepLogisticsContainer, StepComponentContainer, StepReviewContainer } from './steps';
2021
import {
@@ -98,6 +99,11 @@ export const TemplateForm = ({
9899
}: Props) => {
99100
const [wizardContent, setWizardContent] = useState<Forms.Content<WizardContent> | null>(null);
100101
const { addContent: addContentToGlobalFlyout, closeFlyout } = useGlobalFlyout();
102+
const simulateTemplateFilters = useRef<SimulateTemplateFilters>({
103+
mappings: true,
104+
settings: true,
105+
aliases: true,
106+
});
101107

102108
const indexTemplate = defaultValue ?? {
103109
name: '',
@@ -234,13 +240,19 @@ export const TemplateForm = ({
234240
return template;
235241
}, [buildTemplateObject, indexTemplate, wizardContent]);
236242

243+
const onSimulateTemplateFiltersChange = useCallback((filters: SimulateTemplateFilters) => {
244+
simulateTemplateFilters.current = filters;
245+
}, []);
246+
237247
const showPreviewFlyout = () => {
238248
addContentToGlobalFlyout<SimulateTemplateProps>({
239249
id: 'simulateTemplate',
240250
Component: SimulateTemplateFlyoutContent,
241251
props: {
242252
getTemplate: getSimulateTemplate,
243253
onClose: closeFlyout,
254+
filters: simulateTemplateFilters.current,
255+
onFiltersChange: onSimulateTemplateFiltersChange,
244256
},
245257
flyoutProps: simulateTemplateFlyoutProps,
246258
});

0 commit comments

Comments
 (0)