diff --git a/awx/ui_next/src/components/LaunchPrompt/steps/PreviewStep.jsx b/awx/ui_next/src/components/LaunchPrompt/steps/PreviewStep.jsx index 835fbba18d..14e26f4d10 100644 --- a/awx/ui_next/src/components/LaunchPrompt/steps/PreviewStep.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/steps/PreviewStep.jsx @@ -1,11 +1,29 @@ -import React from 'react'; +import React, { Fragment } from 'react'; +import styled from 'styled-components'; +import { ExclamationCircleIcon as PFExclamationCircleIcon } from '@patternfly/react-icons'; +import { Tooltip } from '@patternfly/react-core'; +import { t } from '@lingui/macro'; import { useFormikContext } from 'formik'; +import { withI18n } from '@lingui/react'; import yaml from 'js-yaml'; -import PromptDetail from '../../PromptDetail'; import mergeExtraVars, { maskPasswords } from '../mergeExtraVars'; import getSurveyValues from '../getSurveyValues'; +import PromptDetail from '../../PromptDetail'; -function PreviewStep({ resource, config, survey, formErrors }) { +const ExclamationCircleIcon = styled(PFExclamationCircleIcon)` + margin-left: 10px; + margin-top: -2px; +`; + +const ErrorMessageWrapper = styled.div` + align-items: center; + color: var(--pf-global--danger-color--200); + display: flex; + font-weight: var(--pf-global--FontWeight--bold); + margin-bottom: 10px; +`; + +function PreviewStep({ resource, config, survey, formErrors, i18n }) { const { values } = useFormikContext(); const surveyValues = getSurveyValues(values); @@ -29,21 +47,26 @@ function PreviewStep({ resource, config, survey, formErrors }) { } return ( - <> + + {formErrors.length > 0 && ( + + {i18n._(t`Some of the previous step(s) have errors`)} + + + + + )} - {formErrors && ( - - )} - + ); } -export default PreviewStep; +export default withI18n()(PreviewStep); diff --git a/awx/ui_next/src/components/LaunchPrompt/steps/PreviewStep.test.jsx b/awx/ui_next/src/components/LaunchPrompt/steps/PreviewStep.test.jsx index 71a33b2fec..b596e866ea 100644 --- a/awx/ui_next/src/components/LaunchPrompt/steps/PreviewStep.test.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/steps/PreviewStep.test.jsx @@ -24,6 +24,10 @@ const survey = { ], }; +const formErrors = { + inventory: 'An inventory must be selected', +}; + describe('PreviewStep', () => { test('should render PromptDetail', async () => { let wrapper; @@ -37,6 +41,7 @@ describe('PreviewStep', () => { survey_enabled: true, }} survey={survey} + formErrors={formErrors} /> ); @@ -62,6 +67,7 @@ describe('PreviewStep', () => { config={{ ask_limit_on_launch: true, }} + formErrors={formErrors} /> ); @@ -85,6 +91,7 @@ describe('PreviewStep', () => { config={{ ask_variables_on_launch: true, }} + formErrors={formErrors} /> ); diff --git a/awx/ui_next/src/components/LaunchPrompt/steps/useCredentialsStep.jsx b/awx/ui_next/src/components/LaunchPrompt/steps/useCredentialsStep.jsx index f63d85599b..85299a08c1 100644 --- a/awx/ui_next/src/components/LaunchPrompt/steps/useCredentialsStep.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/steps/useCredentialsStep.jsx @@ -19,7 +19,8 @@ export default function useCredentialsStep( initialValues: getInitialValues(config, resource), validate, isReady: true, - error: null, + contentError: null, + formError: null, setTouched: setFieldsTouched => { setFieldsTouched({ credentials: true, diff --git a/awx/ui_next/src/components/LaunchPrompt/steps/useInventoryStep.jsx b/awx/ui_next/src/components/LaunchPrompt/steps/useInventoryStep.jsx index cd1deb76f7..aa8acbd6f6 100644 --- a/awx/ui_next/src/components/LaunchPrompt/steps/useInventoryStep.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/steps/useInventoryStep.jsx @@ -27,7 +27,8 @@ export default function useInventoryStep(config, resource, visitedSteps, i18n) { initialValues: getInitialValues(config, resource), validate, isReady: true, - error: null, + contentError: null, + formError: stepErrors, setTouched: setFieldsTouched => { setFieldsTouched({ inventory: true, diff --git a/awx/ui_next/src/components/LaunchPrompt/steps/useOtherPromptsStep.jsx b/awx/ui_next/src/components/LaunchPrompt/steps/useOtherPromptsStep.jsx index 516238ca7a..1d33987c92 100644 --- a/awx/ui_next/src/components/LaunchPrompt/steps/useOtherPromptsStep.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/steps/useOtherPromptsStep.jsx @@ -24,7 +24,8 @@ export default function useOtherPrompt(config, resource, visitedSteps, i18n) { initialValues: getInitialValues(config, resource), validate, isReady: true, - error: null, + contentError: null, + formError: stepErrors, setTouched: setFieldsTouched => { setFieldsTouched({ job_type: true, diff --git a/awx/ui_next/src/components/LaunchPrompt/steps/useSurveyStep.jsx b/awx/ui_next/src/components/LaunchPrompt/steps/useSurveyStep.jsx index 5ee623dd14..ac0fbe0c3c 100644 --- a/awx/ui_next/src/components/LaunchPrompt/steps/useSurveyStep.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/steps/useSurveyStep.jsx @@ -54,7 +54,8 @@ export default function useSurveyStep(config, resource, visitedSteps, i18n) { validate, survey, isReady: !isLoading && !!survey, - error, + contentError: error, + formError: stepErrors, setTouched: setFieldsTouched => { if (!survey || !survey.spec) { return; diff --git a/awx/ui_next/src/components/LaunchPrompt/useSteps.js b/awx/ui_next/src/components/LaunchPrompt/useSteps.js index ed61a01804..e8519c58a4 100644 --- a/awx/ui_next/src/components/LaunchPrompt/useSteps.js +++ b/awx/ui_next/src/components/LaunchPrompt/useSteps.js @@ -13,14 +13,13 @@ export default function useSteps(config, resource, i18n) { useOtherPromptsStep(config, resource, visited, i18n), useSurveyStep(config, resource, visited, i18n), ]; + + const formErrorsContent = steps + .filter(s => s?.formError && Object.keys(s.formError).length > 0) + .map(({ formError }) => formError); + steps.push( - usePreviewStep( - config, - resource, - steps[3].survey, - {}, // TODO: formErrors ? - i18n - ) + usePreviewStep(config, resource, steps[3].survey, formErrorsContent, i18n) ); const pfSteps = steps.map(s => s.step).filter(s => s != null); @@ -31,8 +30,9 @@ export default function useSteps(config, resource, i18n) { }; }, {}); const isReady = !steps.some(s => !s.isReady); - const stepWithError = steps.find(s => s.error); - const contentError = stepWithError ? stepWithError.error : null; + + const stepWithError = steps.find(s => s.contentError); + const contentError = stepWithError ? stepWithError.contentError : null; const validate = values => { const errors = steps.reduce((acc, cur) => {