Skip to content

Commit

Permalink
[Ingest Pipeline] Allow to provide a redirect path and a tab in compo…
Browse files Browse the repository at this point in the history
…nent template edit UI (#134910)
  • Loading branch information
nchaulet authored Jun 29, 2022
1 parent 32ba728 commit 27c04bd
Show file tree
Hide file tree
Showing 15 changed files with 258 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function FormWizard<T extends object = { [key: string]: any }, S extends
isSaving,
onSave,
onChange,
onStepChange,
children,
rightContentNav,
}: Props<T, S>) {
Expand All @@ -41,6 +42,7 @@ export function FormWizard<T extends object = { [key: string]: any }, S extends
isEditing={isEditing}
onSave={onSave}
onChange={onChange}
onStepChange={onStepChange}
defaultActiveStep={defaultActiveStep}
>
<FormWizardConsumer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface Props<T extends object> {
defaultActiveStep?: number;
defaultValue?: HookProps<T>['defaultValue'];
onChange?: HookProps<T>['onChange'];
onStepChange?: (id: string) => void;
}

interface State {
Expand Down Expand Up @@ -48,7 +49,7 @@ const formWizardContext = createContext<Context>({} as Context);

export const FormWizardProvider = WithMultiContent<Props<any>>(function FormWizardProvider<
T extends object = { [key: string]: any }
>({ children, defaultActiveStep = 0, isEditing, onSave }: Props<T>) {
>({ children, defaultActiveStep = 0, isEditing, onSave, onStepChange }: Props<T>) {
const { getData, validate, validation } = useMultiContentContext<T>();

const [state, setState] = useState<State>({
Expand Down Expand Up @@ -135,8 +136,12 @@ export const FormWizardProvider = WithMultiContent<Props<any>>(function FormWiza

return nextState;
});
// Trigger onStepChange
if (onStepChange) {
onStepChange(Object.values(state.steps)[getStepIndex(stepId)]?.id);
}
},
[getStepIndex, validate, onSave, getData, lastStep]
[getStepIndex, validate, onSave, onStepChange, getData, lastStep, state.steps]
);

const value: Context = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface AppDependencies {
fatalErrors: FatalErrorsStart;
getUrlForApp: ApplicationStart['getUrlForApp'];
executionContext: ExecutionContextStart;
application: ApplicationStart;
};
plugins: {
usageCollection: UsageCollectionSetup;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ describe('<ComponentTemplateEdit />', () => {
expect(nameInput.props().disabled).toEqual(true);
});

it('should allow to go directly to a step', async () => {
await act(async () => {
testBed = await setup(httpSetup, '?step=mappings');
});

testBed.component.update();

expect(testBed.exists('mappingsEditor')).toBe(true);
});

describe('form payload', () => {
it('should send the correct payload with changed values', async () => {
const { actions, component, form } = testBed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@ export type ComponentTemplateEditTestBed = TestBed<ComponentTemplateFormTestSubj
actions: ReturnType<typeof getFormActions>;
};

const testBedConfig: AsyncTestBedConfig = {
memoryRouter: {
initialEntries: [`${BASE_PATH}/edit_component_template/comp-1`],
componentRoutePath: `${BASE_PATH}/edit_component_template/:name`,
},
doMountAsync: true,
};
export const setup = async (
httpSetup: HttpSetup,
queryParams: string = ''
): Promise<ComponentTemplateEditTestBed> => {
const testBedConfig: AsyncTestBedConfig = {
memoryRouter: {
initialEntries: [`${BASE_PATH}/edit_component_template/comp-1${queryParams}`],
componentRoutePath: `${BASE_PATH}/edit_component_template/:name`,
},
doMountAsync: true,
};

export const setup = async (httpSetup: HttpSetup): Promise<ComponentTemplateEditTestBed> => {
const initTestBed = registerTestBed(
WithAppDependencies(ComponentTemplateEdit, httpSetup),
testBedConfig
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,6 @@ export type ComponentTemplateFormTestSubjects =
| 'stepReview.requestTab'
| 'versionField'
| 'aliasesEditor'
| 'mappingsEditor'
| 'settingsEditor'
| 'versionField.input';
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import { EmptyPrompt } from './empty_prompt';
import { ComponentTable } from './table';
import { ComponentTemplatesDeleteModal } from './delete_modal';
import { useRedirectPath } from '../../../hooks/redirect_path';

interface Props {
componentTemplateName?: string;
Expand All @@ -45,16 +46,17 @@ export const ComponentTemplateList: React.FunctionComponent<Props> = ({
const { addContent: addContentToGlobalFlyout, removeContent: removeContentFromGlobalFlyout } =
useGlobalFlyout();
const { api, trackMetric, documentation } = useComponentTemplatesContext();
const redirectTo = useRedirectPath(history);

const { data, isLoading, error, resendRequest } = api.useLoadComponentTemplates();

const [componentTemplatesToDelete, setComponentTemplatesToDelete] = useState<string[]>([]);

const goToComponentTemplateList = useCallback(() => {
return history.push({
return redirectTo({
pathname: 'component_templates',
});
}, [history]);
}, [redirectTo]);

const goToEditComponentTemplate = useCallback(
(name: string) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { renderHook } from '@testing-library/react-hooks';
import { createMemoryHistory } from 'history';
import { useStepFromQueryString } from './component_template_edit';

describe('useStepFromQueryString', () => {
it('should return undefined if no step is set in the url', () => {
const history = createMemoryHistory();
history.push('/app/management/data/index_management/edit_component_template');

const {
result: {
current: { activeStep },
},
} = renderHook(() => useStepFromQueryString(history));

expect(activeStep).not.toBeDefined();
});

it('should return the step if set in the url', () => {
const history = createMemoryHistory();
history.push('/app/management/data/index_management/edit_component_template?step=mappings');

const {
result: {
current: { activeStep },
},
} = renderHook(() => useStepFromQueryString(history));

expect(activeStep).toBe('mappings');
});

it('should not update history on step change if no step is set in the url', () => {
const history = createMemoryHistory();
history.push('/app/management/data/index_management/edit_component_template');

const {
result: {
current: { updateStep },
},
} = renderHook(() => useStepFromQueryString(history));

updateStep('aliases');

expect(history.location.search).toBe('');
});

it('should update history on step change if a step is set in the url', () => {
const history = createMemoryHistory();
history.push('/app/management/data/index_management/edit_component_template?step=mappings');

const {
result: {
current: { updateStep },
},
} = renderHook(() => useStepFromQueryString(history));

updateStep('aliases');
expect(history.location.search).toBe('?step=aliases');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* 2.0.
*/

import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiPageContentBody, EuiPageHeader, EuiSpacer } from '@elastic/eui';
import { History } from 'history';

import { useComponentTemplatesContext } from '../../component_templates_context';
import {
Expand All @@ -19,18 +20,46 @@ import {
Error,
} from '../../shared_imports';
import { ComponentTemplateForm } from '../component_template_form';
import type { WizardSection } from '../component_template_form';
import { useRedirectPath } from '../../../../hooks/redirect_path';

interface MatchParams {
name: string;
}

export function useStepFromQueryString(history: History) {
const activeStep = useMemo(() => {
const params = new URLSearchParams(history.location.search);
if (params.has('step')) {
return params.get('step') as WizardSection;
}
}, [history.location.search]);

const updateStep = useCallback(
(stepId: string) => {
const params = new URLSearchParams(history.location.search);
if (params.has('step')) {
params.set('step', stepId);
history.push({
search: params.toString(),
});
}
},
[history]
);

return { activeStep, updateStep };
}

export const ComponentTemplateEdit: React.FunctionComponent<RouteComponentProps<MatchParams>> = ({
match: {
params: { name },
},
history,
}) => {
const { api, breadcrumbs } = useComponentTemplatesContext();
const { activeStep: defaultActiveStep, updateStep } = useStepFromQueryString(history);
const redirectTo = useRedirectPath(history);

const [isSaving, setIsSaving] = useState<boolean>(false);
const [saveError, setSaveError] = useState<any>(null);
Expand All @@ -56,7 +85,7 @@ export const ComponentTemplateEdit: React.FunctionComponent<RouteComponentProps<
return;
}

history.push({
redirectTo({
pathname: encodeURI(
`/component_templates/${encodeURIComponent(updatedComponentTemplate.name)}`
),
Expand Down Expand Up @@ -112,6 +141,8 @@ export const ComponentTemplateEdit: React.FunctionComponent<RouteComponentProps<

<ComponentTemplateForm
defaultValue={componentTemplate!}
defaultActiveWizardSection={defaultActiveStep}
onStepChange={updateStep}
onSave={onSave}
isSaving={isSaving}
saveError={saveError}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useCallback } from 'react';
import React, { useCallback, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiSpacer, EuiCallOut } from '@elastic/eui';
Expand All @@ -25,21 +25,23 @@ import { StepLogisticsContainer, StepReviewContainer } from './steps';
const { stripEmptyFields } = serializers;
const { FormWizard, FormWizardStep } = Forms;

export interface WizardContent extends CommonWizardSteps {
logistics: Omit<ComponentTemplateDeserialized, '_kbnMeta' | 'template'>;
}

export type WizardSection = keyof WizardContent | 'review';

interface Props {
onSave: (componentTemplate: ComponentTemplateDeserialized) => void;
clearSaveError: () => void;
isSaving: boolean;
saveError: any;
defaultValue?: ComponentTemplateDeserialized;
isEditing?: boolean;
defaultActiveWizardSection?: WizardSection;
onStepChange?: (stepId: string) => void;
}

export interface WizardContent extends CommonWizardSteps {
logistics: Omit<ComponentTemplateDeserialized, '_kbnMeta' | 'template'>;
}

export type WizardSection = keyof WizardContent | 'review';

const wizardSections: { [id: string]: { id: WizardSection; label: string } } = {
logistics: {
id: 'logistics',
Expand Down Expand Up @@ -87,7 +89,9 @@ export const ComponentTemplateForm = ({
isSaving,
saveError,
clearSaveError,
defaultActiveWizardSection,
onSave,
onStepChange,
}: Props) => {
const {
template: { settings, mappings, aliases },
Expand Down Expand Up @@ -194,6 +198,17 @@ export const ComponentTemplateForm = ({
[buildComponentTemplateObject, defaultValue, onSave, clearSaveError]
);

const defaultActiveStepIndex = useMemo(
() =>
Math.max(
defaultActiveWizardSection
? Object.keys(wizardSections).indexOf(defaultActiveWizardSection)
: 0,
0
),
[defaultActiveWizardSection]
);

return (
<FormWizard<WizardContent>
defaultValue={wizardDefaultValue}
Expand All @@ -202,6 +217,8 @@ export const ComponentTemplateForm = ({
isSaving={isSaving}
apiError={apiError}
texts={i18nTexts}
defaultActiveStep={defaultActiveStepIndex}
onStepChange={onStepChange}
>
<FormWizardStep
id={wizardSections.logistics.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
*/

export { ComponentTemplateForm } from './component_template_form';
export type { WizardSection } from './component_template_form';
Loading

0 comments on commit 27c04bd

Please sign in to comment.