Skip to content

Commit

Permalink
[7.x] [Composable template] Create / Edit wizard (#70220) (#70698)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebelga authored Jul 3, 2020
1 parent fbc8875 commit bfce6e8
Show file tree
Hide file tree
Showing 66 changed files with 1,901 additions and 333 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { WithMultiContent, useMultiContentContext, HookProps } from '../multi_co

export interface Props<T extends object> {
onSave: (data: T) => void | Promise<void>;
children: JSX.Element | JSX.Element[];
children: JSX.Element | Array<JSX.Element | null | false>;
isEditing?: boolean;
defaultActiveStep?: number;
defaultValue?: HookProps<T>['defaultValue'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function useMultiContentContext<T extends object = { [key: string]: any }
*
* @param contentId The content id to be added to the "contents" map
*/
export function useContent<T extends object = { [key: string]: any }>(contentId: keyof T) {
export function useContent<T extends object, K extends keyof T>(contentId: K) {
const { updateContentAt, saveSnapshotAndRemoveContent, getData } = useMultiContentContext<T>();

const updateContent = useCallback(
Expand All @@ -71,8 +71,11 @@ export function useContent<T extends object = { [key: string]: any }>(contentId:
};
}, [contentId, saveSnapshotAndRemoveContent]);

const data = getData();
const defaultValue = data[contentId];

return {
defaultValue: getData()[contentId]!,
defaultValue,
updateContent,
getData,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ export function useMultiContent<T extends object>({
* Validate the multi-content active content(s) in the DOM
*/
const validate = useCallback(async () => {
if (Object.keys(contents.current).length === 0) {
return Boolean(validation.isValid);
}

const updatedValidation = {} as { [key in keyof T]?: boolean | undefined };

for (const [id, _content] of Object.entries(contents.current)) {
Expand Down
7 changes: 2 additions & 5 deletions src/plugins/es_ui_shared/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* In the future, each top level folder should be exported like that to avoid naming collision
*/
import * as Forms from './forms';
import * as Monaco from './monaco';

export { JsonEditor, OnJsonEditorUpdateHandler } from './components/json_editor';

Expand Down Expand Up @@ -53,10 +54,6 @@ export {
expandLiteralStrings,
} from './console_lang';

import * as Monaco from './monaco';

export { Monaco };

export {
AuthorizationContext,
AuthorizationProvider,
Expand All @@ -69,7 +66,7 @@ export {
useAuthorizationContext,
} from './authorization';

export { Forms };
export { Monaco, Forms };

/** dummy plugin, we just want esUiShared to have its own bundle */
export function plugin() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import { ValidationFunc } from '../../hook_form_lib';
import { isJSON } from '../../../validators/string';
import { ERROR_CODE } from './types';

export const isJsonField = (message: string) => (
...args: Parameters<ValidationFunc>
): ReturnType<ValidationFunc<any, ERROR_CODE>> => {
export const isJsonField = (
message: string,
{ allowEmptyString = false }: { allowEmptyString?: boolean } = {}
) => (...args: Parameters<ValidationFunc>): ReturnType<ValidationFunc<any, ERROR_CODE>> => {
const [{ value }] = args;

if (typeof value !== 'string') {
if (typeof value !== 'string' || (allowEmptyString && value.trim() === '')) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils';

import {
Expand All @@ -13,44 +12,21 @@ import {
TestBedConfig,
findTestSubject,
} from '../../../../../test_utils';
// NOTE: We have to use the Home component instead of the TemplateList component because we depend
// upon react router to provide the name of the template to load in the detail panel.
import { IndexManagementHome } from '../../../public/application/sections/home'; // eslint-disable-line @kbn/eslint/no-restricted-paths
import { indexManagementStore } from '../../../public/application/store'; // eslint-disable-line @kbn/eslint/no-restricted-paths
import { TemplateList } from '../../../public/application/sections/home/template_list'; // eslint-disable-line @kbn/eslint/no-restricted-paths
import { TemplateDeserialized } from '../../../common';
import { WithAppDependencies, services, TestSubjects } from '../helpers';
import { WithAppDependencies, TestSubjects } from '../helpers';

const testBedConfig: TestBedConfig = {
store: () => indexManagementStore(services as any),
memoryRouter: {
initialEntries: [`/indices`],
componentRoutePath: `/:section(indices|templates)`,
initialEntries: [`/templates`],
componentRoutePath: `/templates/:templateName?`,
},
doMountAsync: true,
};

const initTestBed = registerTestBed(WithAppDependencies(IndexManagementHome), testBedConfig);

export interface IndexTemplatesTabTestBed extends TestBed<TestSubjects> {
findAction: (action: 'edit' | 'clone' | 'delete') => ReactWrapper;
actions: {
goToTemplatesList: () => void;
selectDetailsTab: (tab: 'summary' | 'settings' | 'mappings' | 'aliases') => void;
clickReloadButton: () => void;
clickTemplateAction: (
name: TemplateDeserialized['name'],
action: 'edit' | 'clone' | 'delete'
) => void;
clickTemplateAt: (index: number) => void;
clickCloseDetailsButton: () => void;
clickActionMenu: (name: TemplateDeserialized['name']) => void;
toggleViewItem: (view: 'composable' | 'system') => void;
};
}

export const setup = async (): Promise<IndexTemplatesTabTestBed> => {
const testBed = await initTestBed();
const initTestBed = registerTestBed<TestSubjects>(WithAppDependencies(TemplateList), testBedConfig);

const createActions = (testBed: TestBed<TestSubjects>) => {
/**
* Additional helpers
*/
Expand All @@ -64,11 +40,6 @@ export const setup = async (): Promise<IndexTemplatesTabTestBed> => {
/**
* User Actions
*/

const goToTemplatesList = () => {
testBed.find('templatesTab').simulate('click');
};

const selectDetailsTab = (tab: 'summary' | 'settings' | 'mappings' | 'aliases') => {
const tabs = ['summary', 'settings', 'mappings', 'aliases'];

Expand Down Expand Up @@ -136,10 +107,8 @@ export const setup = async (): Promise<IndexTemplatesTabTestBed> => {
};

return {
...testBed,
findAction,
actions: {
goToTemplatesList,
selectDetailsTab,
clickReloadButton,
clickTemplateAction,
Expand All @@ -150,3 +119,14 @@ export const setup = async (): Promise<IndexTemplatesTabTestBed> => {
},
};
};

export const setup = async (): Promise<IndexTemplatesTabTestBed> => {
const testBed = await initTestBed();

return {
...testBed,
...createActions(testBed),
};
};

export type IndexTemplatesTabTestBed = TestBed<TestSubjects> & ReturnType<typeof createActions>;
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,15 @@ describe('Index Templates tab', () => {
server.restore();
});

beforeEach(async () => {
httpRequestsMockHelpers.setLoadIndicesResponse([]);

await act(async () => {
testBed = await setup();
});
});

describe('when there are no index templates', () => {
beforeEach(async () => {
const { actions, component } = testBed;

test('should display an empty prompt', async () => {
httpRequestsMockHelpers.setLoadTemplatesResponse({ templates: [], legacyTemplates: [] });

await act(async () => {
actions.goToTemplatesList();
testBed = await setup();
});
const { exists, component } = testBed;
component.update();
});

test('should display an empty prompt', async () => {
const { exists } = testBed;

expect(exists('sectionLoading')).toBe(false);
expect(exists('emptyPrompt')).toBe(true);
Expand Down Expand Up @@ -119,14 +106,12 @@ describe('Index Templates tab', () => {
const legacyTemplates = [template4, template5, template6];

beforeEach(async () => {
const { actions, component } = testBed;

httpRequestsMockHelpers.setLoadTemplatesResponse({ templates, legacyTemplates });

await act(async () => {
actions.goToTemplatesList();
testBed = await setup();
});
component.update();
testBed.component.update();
});

test('should list them in the table', async () => {
Expand All @@ -151,6 +136,7 @@ describe('Index Templates tab', () => {
composedOfString,
priorityFormatted,
'M S A', // Mappings Settings Aliases badges
'', // Column of actions
]);
});

Expand Down Expand Up @@ -192,8 +178,10 @@ describe('Index Templates tab', () => {
);
});

test('should have a button to create a new template', () => {
test('should have a button to create a template', () => {
const { exists } = testBed;
// Both composable and legacy templates
expect(exists('createTemplateButton')).toBe(true);
expect(exists('createLegacyTemplateButton')).toBe(true);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import React from 'react';
import { act } from 'react-dom/test-utils';

import { CREATE_LEGACY_TEMPLATE_BY_DEFAULT } from '../../../common';
import { setupEnvironment, nextTick } from '../helpers';

import {
Expand Down Expand Up @@ -369,7 +368,7 @@ describe.skip('<TemplateCreate />', () => {
aliases: ALIASES,
},
_kbnMeta: {
isLegacy: CREATE_LEGACY_TEMPLATE_BY_DEFAULT,
isLegacy: false,
isManaged: false,
},
};
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/index_management/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export { BASE_PATH } from './base_path';
export { API_BASE_PATH } from './api_base_path';
export { INVALID_INDEX_PATTERN_CHARS, INVALID_TEMPLATE_NAME_CHARS } from './invalid_characters';
export * from './index_statuses';
export { CREATE_LEGACY_TEMPLATE_BY_DEFAULT } from './index_templates';

export {
UIM_APP_NAME,
Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion x-pack/plugins/index_management/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

export { PLUGIN, API_BASE_PATH, CREATE_LEGACY_TEMPLATE_BY_DEFAULT, BASE_PATH } from './constants';
export { PLUGIN, API_BASE_PATH, BASE_PATH } from './constants';

export { getTemplateParameter } from './lib';

Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/index_management/common/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
export { deserializeDataStream, deserializeDataStreamList } from './data_stream_serialization';

export {
deserializeLegacyTemplateList,
deserializeTemplate,
deserializeTemplateList,
deserializeLegacyTemplate,
deserializeLegacyTemplateList,
serializeTemplate,
serializeLegacyTemplate,
} from './template_serialization';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import {
const hasEntries = (data: object = {}) => Object.entries(data).length > 0;

export function serializeTemplate(templateDeserialized: TemplateDeserialized): TemplateSerialized {
const { version, priority, indexPatterns, template, composedOf } = templateDeserialized;
const { version, priority, indexPatterns, template, composedOf, _meta } = templateDeserialized;

return {
version,
priority,
template,
index_patterns: indexPatterns,
composed_of: composedOf,
_meta,
};
}

Expand All @@ -34,6 +35,7 @@ export function deserializeTemplate(
index_patterns: indexPatterns,
template = {},
priority,
_meta,
composed_of: composedOf,
} = templateEs;
const { settings } = template;
Expand All @@ -46,6 +48,7 @@ export function deserializeTemplate(
template,
ilmPolicy: settings?.index?.lifecycle,
composedOf,
_meta,
_kbnMeta: {
isManaged: Boolean(managedTemplatePrefix && name.startsWith(managedTemplatePrefix)),
},
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/index_management/common/types/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface TemplateSerialized {
composed_of?: string[];
version?: number;
priority?: number;
_meta?: { [key: string]: any };
}

/**
Expand All @@ -43,6 +44,7 @@ export interface TemplateDeserialized {
ilmPolicy?: {
name: string;
};
_meta?: { [key: string]: any };
_kbnMeta: {
isManaged: boolean;
isLegacy?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@


/**
* [1] Will center vertically the empty search result
*/

$heightHeader: $euiSizeL * 2;

.componentTemplates {
@include euiBottomShadowFlat;
height: 100%;

&__header {
height: $heightHeader;

.euiFormControlLayout {
max-width: initial;
}
}

&__searchBox {
border-bottom: $euiBorderThin;
box-shadow: none;
max-width: initial;
}

&__listWrapper {
height: calc(100% - #{$heightHeader});

&--is-empty {
display: flex; // [1]
}
}
}
Loading

0 comments on commit bfce6e8

Please sign in to comment.