Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion playwright/Customizations/Disk.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ test('Create a blueprint with Disk customization', async ({
.getByRole('row', {
name: '/tmp/usb xfs 1 GiB',
})
.getByLabel('Partition name input')
.getByRole('textbox', { name: 'Partition name input' })
.fill('lv2');
await frame
.getByRole('row', {
Expand Down
55 changes: 32 additions & 23 deletions src/Components/CreateImageWizard/ValidatedInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
HelperTextItem,
TextArea,
TextAreaProps,
TextInput,
TextInputGroup,
TextInputGroupMain,
TextInputGroupMainProps,
Expand Down Expand Up @@ -44,12 +43,9 @@ type ValidationInputProp = TextInputProps &
warning?: string | undefined;
forceErrorDisplay?: boolean;
isDisabled?: boolean;
handleClear?: () => void;
};

type ErrorMessageProps = {
errorMessage: string;
};

type ValidationResult = 'default' | 'success' | 'error';

export const ValidatedInputAndTextArea = ({
Expand All @@ -65,6 +61,7 @@ export const ValidatedInputAndTextArea = ({
isDisabled = false,
warning = undefined,
forceErrorDisplay = false,
handleClear,
}: ValidationInputProp) => {
const errorMessage = stepValidation.errors[fieldName] || '';
const hasError = errorMessage !== '';
Expand Down Expand Up @@ -109,24 +106,43 @@ export const ValidatedInputAndTextArea = ({
isDisabled={isDisabled}
/>
) : (
<TextInput
value={value}
onChange={onChange}
validated={validated}
onBlur={handleBlur}
placeholder={placeholder || ''}
aria-label={ariaLabel}
data-testid={dataTestId}
<TextInputGroup
{...(validated === 'error' && { validated })}
isDisabled={isDisabled}
/>
>
<TextInputGroupMain
value={value}
type='text'
onBlur={handleBlur}
onChange={onChange}
placeholder={placeholder || ''}
aria-label={ariaLabel}
data-testid={dataTestId}
/>
{value && (
<TextInputGroupUtilities>
<Button
variant='plain'
onClick={handleClear}
aria-label={
ariaLabel ? `Clear ${ariaLabel} input` : 'Clear input'
}
icon={<TimesIcon />}
tabIndex={-1} // Remove from tab order to maintain clean keyboard navigation between fields
/>
</TextInputGroupUtilities>
)}
</TextInputGroup>
)}
{warning !== undefined && warning !== '' && (
<HelperText>
<HelperTextItem variant='warning'>{warning}</HelperTextItem>
</HelperText>
)}
{validated === 'error' && hasError && (
<ErrorMessage errorMessage={errorMessage} />
<HelperText>
<HelperTextItem variant='error'>{errorMessage}</HelperTextItem>
</HelperText>
)}
</>
);
Expand All @@ -151,14 +167,6 @@ const getValidationState = (
return validated;
};

export const ErrorMessage = ({ errorMessage }: ErrorMessageProps) => {
return (
<HelperText>
<HelperTextItem variant='error'>{errorMessage}</HelperTextItem>
</HelperText>
);
};

export const ValidatedInput = ({
helperText,
validator,
Expand Down Expand Up @@ -197,6 +205,7 @@ export const ValidatedInput = ({
ariaLabel ? `Clear ${ariaLabel} input` : 'Clear input'
}
icon={<TimesIcon />}
tabIndex={-1} // Remove from tab order to maintain clean keyboard navigation between fields
/>
</TextInputGroupUtilities>
)}
Expand Down
33 changes: 7 additions & 26 deletions src/Components/CreateImageWizard/steps/Details/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import React from 'react';

import {
Content,
Form,
FormGroup,
FormHelperText,
HelperText,
HelperTextItem,
Title,
} from '@patternfly/react-core';
import { Content, Form, FormGroup, Title } from '@patternfly/react-core';

import {
changeBlueprintDescription,
Expand Down Expand Up @@ -47,14 +39,12 @@ const DetailsStep = () => {
return (
<Form>
<Title headingLevel='h1' size='xl'>
Details
Image details
</Title>
<Content>
Enter a name to identify your blueprint. If no name is entered, the
images created from this blueprint will use the name of the parent
blueprint.
Enter a name and description to identify your deployment-ready image.
</Content>
<FormGroup isRequired label='Blueprint name' fieldId='blueprint-name'>
<FormGroup isRequired label='Name' fieldId='blueprint-name'>
<ValidatedInputAndTextArea
ariaLabel='blueprint name'
dataTestId='blueprint'
Expand All @@ -64,21 +54,11 @@ const DetailsStep = () => {
stepValidation={stepValidation}
fieldName='name'
isRequired={true}
handleClear={() => dispatch(changeBlueprintName(''))}
/>
<FormHelperText>
<HelperText>
<HelperTextItem>
The name can be 2-100 characters with at least two letters or
numbers
</HelperTextItem>
</HelperText>
</FormHelperText>
</FormGroup>

<FormGroup
label='Blueprint description'
fieldId='blueprint-description-name'
>
<FormGroup label='Description' fieldId='blueprint-description-name'>
<ValidatedInputAndTextArea
ariaLabel='blueprint description'
dataTestId='blueprint description'
Expand All @@ -87,6 +67,7 @@ const DetailsStep = () => {
placeholder='Add description'
stepValidation={stepValidation}
fieldName='description'
handleClear={() => dispatch(changeBlueprintDescription(''))}
/>
</FormGroup>
</Form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,36 +43,28 @@ describe('Details Component', () => {
renderDetailsStep();

expect(
await screen.findByRole('heading', { name: /Details/i }),
await screen.findByRole('heading', { name: /Image details/i }),
).toBeInTheDocument();
expect(
screen.getByText(/Enter a name to identify your blueprint/i),
screen.getByText(
/Enter a name and description to identify your deployment-ready image/i,
),
).toBeInTheDocument();
});

test('displays blueprint name input field', async () => {
renderDetailsStep();

expect(
await screen.findByRole('textbox', { name: /blueprint name/i }),
await screen.findByRole('textbox', { name: /name/i }),
).toBeInTheDocument();
});

test('displays blueprint description input field', async () => {
renderDetailsStep();

expect(
await screen.findByRole('textbox', { name: /blueprint description/i }),
).toBeInTheDocument();
});

test('displays helper text for name requirements', async () => {
renderDetailsStep();

expect(
await screen.findByText(
/The name can be 2-100 characters with at least two letters or numbers/i,
),
await screen.findByRole('textbox', { name: /description/i }),
).toBeInTheDocument();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const enterBlueprintName = async (
name: string,
) => {
const input = await screen.findByRole('textbox', {
name: /blueprint name/i,
name: /name/i,
});
await clearWithWait(user, input);
await typeWithWait(user, input, name);
Expand All @@ -34,22 +34,22 @@ export const enterBlueprintDescription = async (
description: string,
) => {
const input = await screen.findByRole('textbox', {
name: /blueprint description/i,
name: /description/i,
});
await clearWithWait(user, input);
await typeWithWait(user, input, description);
};

export const clearBlueprintName = async (user: UserEventInstance) => {
const input = await screen.findByRole('textbox', {
name: /blueprint name/i,
name: /name/i,
});
await clearWithWait(user, input);
};

export const clearBlueprintDescription = async (user: UserEventInstance) => {
const input = await screen.findByRole('textbox', {
name: /blueprint description/i,
name: /description/i,
});
await clearWithWait(user, input);
};
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('Step Review', () => {
await goToReview();
await clickBack();
await screen.findByRole('heading', {
name: 'Details',
name: 'Image details',
});
});

Expand Down
Loading