chore: reworking how we do the review step
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { ReviewState } from './ReviewState';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
describe('ReviewState', () => {
|
||||
it('should render the text as normal with no options', () => {
|
||||
const formState = {
|
||||
name: 'John Doe',
|
||||
test: 'bob',
|
||||
};
|
||||
|
||||
const { getByRole } = render(
|
||||
<ReviewState formState={formState} schemas={[]} />,
|
||||
);
|
||||
|
||||
expect(getByRole('row', { name: 'Name John Doe' })).toBeInTheDocument();
|
||||
expect(getByRole('row', { name: 'Test bob' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should mask password ui:fields', () => {
|
||||
const formState = {
|
||||
name: 'John Doe',
|
||||
test: 'bob',
|
||||
};
|
||||
|
||||
const schemas = [
|
||||
{
|
||||
mergedSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
'ui:widget': 'password',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const { getByRole } = render(
|
||||
<ReviewState formState={formState} schemas={schemas} />,
|
||||
);
|
||||
|
||||
expect(getByRole('row', { name: 'Name ******' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { StructuredMetadataTable } from '@backstage/core-components';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { ParsedTemplateSchema } from './useTemplateSchema';
|
||||
import { Draft07 as JSONSchema } from 'json-schema-library';
|
||||
|
||||
interface ReviewStateProps {
|
||||
schemas: ParsedTemplateSchema[];
|
||||
formState: JsonObject;
|
||||
}
|
||||
|
||||
export const ReviewState = (props: ReviewStateProps) => {
|
||||
const reviewData = Object.fromEntries(
|
||||
Object.entries(props.formState).map(([key, value]) => {
|
||||
for (const step of props.schemas) {
|
||||
const parsedSchema = new JSONSchema(step.mergedSchema);
|
||||
const definitionInSchema = parsedSchema.getSchema(
|
||||
`#/${key}`,
|
||||
props.formState,
|
||||
);
|
||||
if (definitionInSchema) {
|
||||
if (definitionInSchema['ui:widget'] === 'password') {
|
||||
return [key, '******'];
|
||||
}
|
||||
|
||||
const backstageReviewOptions =
|
||||
definitionInSchema['ui:backstage']?.review;
|
||||
|
||||
if (backstageReviewOptions) {
|
||||
if (backstageReviewOptions.mask) {
|
||||
return [key, backstageReviewOptions.mask];
|
||||
}
|
||||
if (!backstageReviewOptions.show) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [key, value];
|
||||
}),
|
||||
);
|
||||
return <StructuredMetadataTable metadata={reviewData} />;
|
||||
};
|
||||
@@ -29,6 +29,7 @@ import { FieldExtensionOptions } from '../../../extensions';
|
||||
import { TemplateParameterSchema } from '../../../types';
|
||||
import { createAsyncValidators } from './createAsyncValidators';
|
||||
import { useTemplateSchema } from './useTemplateSchema';
|
||||
import { ReviewState } from './ReviewState';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
backButton: {
|
||||
@@ -74,7 +75,7 @@ export const Stepper = (props: StepperProps) => {
|
||||
}, [props.extensions]);
|
||||
|
||||
const validation = useMemo(() => {
|
||||
const { mergedSchema } = steps[activeStep];
|
||||
const { mergedSchema } = steps[activeStep] ?? {};
|
||||
return createAsyncValidators(mergedSchema, validators, {
|
||||
apiHolder,
|
||||
});
|
||||
@@ -112,30 +113,37 @@ export const Stepper = (props: StepperProps) => {
|
||||
<MuiStepLabel>{step.title}</MuiStepLabel>
|
||||
</MuiStep>
|
||||
))}
|
||||
<MuiStep>
|
||||
<MuiStepLabel>Review</MuiStepLabel>
|
||||
</MuiStep>
|
||||
</MuiStepper>
|
||||
<div className={styles.formWrapper}>
|
||||
<Form
|
||||
extraErrors={errors}
|
||||
formData={formState}
|
||||
schema={steps[activeStep].schema}
|
||||
uiSchema={steps[activeStep].uiSchema}
|
||||
onSubmit={handleNext}
|
||||
fields={extensions}
|
||||
showErrorList={false}
|
||||
>
|
||||
<div className={styles.footer}>
|
||||
<Button
|
||||
onClick={handleBack}
|
||||
className={styles.backButton}
|
||||
disabled={activeStep < 1}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button variant="contained" color="primary" type="submit">
|
||||
{activeStep === steps.length - 1 ? 'Review' : 'Next'}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
{activeStep < steps.length ? (
|
||||
<Form
|
||||
extraErrors={errors}
|
||||
formData={formState}
|
||||
schema={steps[activeStep].schema}
|
||||
uiSchema={steps[activeStep].uiSchema}
|
||||
onSubmit={handleNext}
|
||||
fields={extensions}
|
||||
showErrorList={false}
|
||||
>
|
||||
<div className={styles.footer}>
|
||||
<Button
|
||||
onClick={handleBack}
|
||||
className={styles.backButton}
|
||||
disabled={activeStep < 1}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button variant="contained" color="primary" type="submit">
|
||||
{activeStep === steps.length - 1 ? 'Review' : 'Next'}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
) : (
|
||||
<ReviewState formState={formState} schemas={steps} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -19,17 +19,16 @@ import { UiSchema } from '@rjsf/core';
|
||||
import { TemplateParameterSchema } from '../../../types';
|
||||
import { extractSchemaFromStep } from './schema';
|
||||
|
||||
export interface ParsedTemplateSchema {
|
||||
uiSchema: UiSchema;
|
||||
mergedSchema: JsonObject;
|
||||
schema: JsonObject;
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
export const useTemplateSchema = (
|
||||
manifest: TemplateParameterSchema,
|
||||
): {
|
||||
steps: {
|
||||
uiSchema: UiSchema;
|
||||
mergedSchema: JsonObject;
|
||||
schema: JsonObject;
|
||||
title: string;
|
||||
description?: string;
|
||||
}[];
|
||||
} => {
|
||||
): { steps: ParsedTemplateSchema } => {
|
||||
const featureFlags = useApi(featureFlagsApiRef);
|
||||
const steps = manifest.steps.map(({ title, description, schema }) => ({
|
||||
title,
|
||||
|
||||
Reference in New Issue
Block a user