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

[Ingest Pipeline] Allow to provide a redirect path and a tab in component template edit UI #134910

Merged
merged 12 commits into from
Jun 29, 2022
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('useTabFromQueryString', () => {
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