Skip to content

Commit

Permalink
feat: create select dropdown patterns for form builders and form user…
Browse files Browse the repository at this point in the history
…s tckt-360 (#370)

* feat: create select dropdown pattern tckt-360

* feat: create select dropdown pattern edit for form builder tckt-360

* feat: refactor rendering default select option via the template tckt-360

* fix: parseUserInput hook for select dropdown tckt-360

* test: add unit tests for select dropdown pattern tckt-360

* refactor: restructure file folder for select-dropdown

* feat: refactor parseUserInput and update tests

* feat: remoce pdf proccessing code for select dropdown tckt-360

* test: refactor tests tckt-360

---------

Co-authored-by: Khayal Alasgarov <[email protected]>
  • Loading branch information
kalasgarov and Khayal Alasgarov authored Nov 1, 2024
1 parent 10bd089 commit 8a7f982
Show file tree
Hide file tree
Showing 15 changed files with 696 additions and 0 deletions.
6 changes: 6 additions & 0 deletions packages/common/src/locales/en/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,11 @@ export const en = {
fieldLabel: 'Radio group label',
errorTextMustContainChar: 'String must contain at least 1 character(s)',
},
selectDropdown: {
...defaults,
displayName: 'Select Dropdown label',
fieldLabel: 'Select Dropdown label',
errorTextMustContainChar: 'String must contain at least 1 character(s)',
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { type Meta, type StoryObj } from '@storybook/react';

import { SelectDropdownPattern } from './SelectDropdown.js';

const meta: Meta<typeof SelectDropdownPattern> = {
title: 'patterns/SelectPattern',
component: SelectDropdownPattern,
decorators: [
(Story, args) => {
const FormDecorator = () => {
const formMethods = useForm();
return (
<FormProvider {...formMethods}>
<Story {...args} />
</FormProvider>
);
};
return <FormDecorator />;
},
],
tags: ['autodocs'],
};

export default meta;
export const Default: StoryObj<typeof SelectDropdownPattern> = {
args: {
_patternId: '',
type: 'select-dropdown',
selectId: 'select-1',
label: 'Select an option',
required: false,
options: [
{ value: 'value1', label: 'Option-1' },
{ value: 'value2', label: 'Option-2' },
{ value: 'value3', label: 'Option-3' },
],
},
};

export const WithError: StoryObj<typeof SelectDropdownPattern> = {
args: {
_patternId: '',
type: 'select-dropdown',
selectId: 'select-with-error',
label: 'Select an option with error',
required: false,
options: [
{ value: 'value1', label: 'Option-1' },
{ value: 'value2', label: 'Option-2' },
{ value: 'value3', label: 'Option-3' },
],
error: {
type: 'custom',
message: 'This field has an error',
},
},
};

export const Required: StoryObj<typeof SelectDropdownPattern> = {
args: {
_patternId: '',
type: 'select-dropdown',
selectId: 'select-required',
label: 'Select a required option',
required: true,
options: [
{ value: 'value1', label: 'Option-1' },
{ value: 'value2', label: 'Option-2' },
{ value: 'value3', label: 'Option-3' },
],
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @vitest-environment jsdom
*/
import { describeStories } from '../../../test-helper.js';
import meta, * as stories from './SelectDropdown.stories.js';

describeStories(meta, stories);
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { useFormContext } from 'react-hook-form';

import { type SelectDropdownProps } from '@atj/forms';
import { type PatternComponent } from '../../index.js';

export const SelectDropdownPattern: PatternComponent<SelectDropdownProps> = ({
selectId,
label,
required,
options,
error,
}) => {
const { register } = useFormContext();
return (
<div className="usa-fieldset padding-top-2">
<form className="usa-form">
<label className="usa-label" htmlFor={selectId}>
{label}
{required && <span className="required-indicator">*</span>}
</label>
<select className="usa-select" id={selectId} {...register(selectId)}>
<option key="default" value="">
- Select -
</option>
{options.map((option, index) => (
<option key={index} value={option.value}>
{option.label}
</option>
))}
</select>
{error && (
<span className="error-message" style={{ color: 'red' }}>
{error.message}
</span>
)}
</form>
</div>
);
};
3 changes: 3 additions & 0 deletions packages/design/src/Form/components/SelectDropdown/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { SelectDropdownPattern } from './SelectDropdown.js';

export default SelectDropdownPattern;
2 changes: 2 additions & 0 deletions packages/design/src/Form/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Paragraph from './Paragraph/index.js';
import RadioGroup from './RadioGroup/index.js';
import RichText from './RichText/index.js';
import Sequence from './Sequence/index.js';
import SelectDropdown from './SelectDropdown/index.js';
import SubmissionConfirmation from './SubmissionConfirmation/index.js';
import TextInput from './TextInput/index.js';

Expand All @@ -26,6 +27,7 @@ export const defaultPatternComponents: ComponentForPattern = {
paragraph: Paragraph as PatternComponent,
'radio-group': RadioGroup as PatternComponent,
'rich-text': RichText as PatternComponent,
'select-dropdown': SelectDropdown as PatternComponent,
sequence: Sequence as PatternComponent,
'submission-confirmation': SubmissionConfirmation as PatternComponent,
};
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ const sidebarPatterns: DropdownPattern[] = [
['rich-text', defaultFormConfig.patterns['rich-text']],
['radio-group', defaultFormConfig.patterns['radio-group']],
['package-download', defaultFormConfig.patterns['package-download']],
['select-dropdown', defaultFormConfig.patterns['select-dropdown']],
] as const;
export const fieldsetPatterns: DropdownPattern[] = [
['form-summary', defaultFormConfig.patterns['form-summary']],
Expand All @@ -104,6 +105,7 @@ export const fieldsetPatterns: DropdownPattern[] = [
['rich-text', defaultFormConfig.patterns['rich-text']],
['radio-group', defaultFormConfig.patterns['radio-group']],
['package-download', defaultFormConfig.patterns['package-download']],
['select-dropdown', defaultFormConfig.patterns['select-dropdown']],
] as const;

export const SidebarAddPatternMenuItem = ({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import type { Meta, StoryObj } from '@storybook/react';

import { type SelectDropdownPattern } from '@atj/forms';

import { createPatternEditStoryMeta } from './common/story-helper.js';
import FormEdit from '../index.js';
import CheckboxPatternEdit from './CheckboxPatternEdit.js';
import { enLocale as message } from '@atj/common';
import { expect, userEvent } from '@storybook/test';
import { within } from '@testing-library/react';

const pattern: SelectDropdownPattern = {
id: 'select-dropdown-1',
type: 'select-dropdown',
data: {
label: message.patterns.selectDropdown.displayName,
required: false,
options: [
{ value: 'value1', label: 'Option-1' },
{ value: 'value2', label: 'Option-2' },
{ value: 'value3', label: 'Option-3' },
],
},
};

const storyConfig: Meta = {
title: 'Edit components/SelectDropdownPattern',
...createPatternEditStoryMeta({
pattern,
}),
} as Meta<typeof FormEdit>;
export default storyConfig;

export const Basic: StoryObj<typeof FormEdit> = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const updatedLabel = 'Select Dropdown update';

await userEvent.click(
canvas.getByText(message.patterns.selectDropdown.displayName)
);
const input = canvas.getByLabelText(
message.patterns.selectDropdown.fieldLabel
);
const optionLabel = canvas.getByLabelText('Option 1 label');

// Enter new text for the field
await userEvent.clear(input);
await userEvent.type(input, updatedLabel);
await userEvent.clear(optionLabel);
await userEvent.type(optionLabel, '- Select an option -');

const form = input?.closest('form');
/**
* The <enter> key behavior outside of Storybook submits the form, which commits the pending edit.
* Here, we want to simulate the <enter> keypress in the story since Storybook manipulates
* the default behavior and does not register the enter key if it's in the `userEvent.type` function arg.
*/
form?.requestSubmit();

await expect(await canvas.findByText(updatedLabel)).toBeInTheDocument();
await expect(await canvas.findByText('- Select an option -')).toBeVisible();
},
};

export const AddField: StoryObj<typeof FormEdit> = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

await userEvent.click(
canvas.getByText(message.patterns.selectDropdown.displayName)
);

await userEvent.click(
canvas.getByRole('button', {
name: /add new/i,
})
);

await expect(
await canvas.findByLabelText('Option 3 label')
).toBeInTheDocument();
},
};

export const Error: StoryObj<typeof CheckboxPatternEdit> = {
play: async ({ canvasElement }) => {
userEvent.setup();

const canvas = within(canvasElement);

await userEvent.click(
canvas.getByText(message.patterns.selectDropdown.displayName)
);

const input = canvas.getByLabelText(
message.patterns.selectDropdown.fieldLabel
);
const optionLabel = canvas.getByLabelText('Option 1 label');

// Clear input, remove focus, and wait for error
await userEvent.clear(input);
input.blur();

await expect(
await canvas.findByText(
message.patterns.selectDropdown.errorTextMustContainChar
)
).toBeInTheDocument();

/*
Repopulate the input value since the error text is indistinguishable from
the error text from the option label below
*/
await userEvent.type(input, message.patterns.selectDropdown.fieldLabel);

await userEvent.clear(optionLabel);
optionLabel.blur();

await expect(
await canvas.findByText(
message.patterns.selectDropdown.errorTextMustContainChar
)
).toBeInTheDocument();

await userEvent.clear(input);
input.blur();
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @vitest-environment jsdom
*/
import { describeStories } from '../../../test-helper.js';
import meta, * as stories from './SelectDropdownPatternEdit.stories.js';

describeStories(meta, stories);
Loading

0 comments on commit 8a7f982

Please sign in to comment.