Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Composable template] Create / Edit wizard #70220

Merged
merged 12 commits into from
Jul 3, 2020
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 thanks for fixing this

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?`,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cjcenizal for info this is how you get the memory router to provide the "templateName" to the component.

},
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