-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: create textarea (long answer) component tckt-399 (#459)
* feat: create long answer textarea component TCKT-399 * test(storybook): add stories for textarea component TCKT-399 * feat: create long answer textarea edit component TCKT-399 * test(storybook): add stories for textarea edit component TCKT-399 * feat: add schema config and validtion logic for textarea TCKT-399 * test: add schema config and validtion tests for textarea TCKT-399 --------- Co-authored-by: Khayal Alasgarov <[email protected]>
- Loading branch information
1 parent
8e686e4
commit 8cb4833
Showing
12 changed files
with
641 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
packages/design/src/Form/components/TextArea/TextArea.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import React from 'react'; | ||
import { FormProvider, useForm } from 'react-hook-form'; | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import TextArea from './index.js'; | ||
|
||
const meta: Meta<typeof TextArea> = { | ||
title: 'patterns/TextArea', | ||
component: TextArea, | ||
decorators: [ | ||
(Story, args) => { | ||
const FormDecorator = () => { | ||
const formMethods = useForm(); | ||
return ( | ||
<div style={{ padding: '10px' }}> | ||
<FormProvider {...formMethods}> | ||
<Story {...args} /> | ||
</FormProvider> | ||
</div> | ||
); | ||
}; | ||
return <FormDecorator />; | ||
}, | ||
], | ||
tags: ['autodocs'], | ||
}; | ||
|
||
export default meta; | ||
|
||
export const Required: StoryObj<typeof TextArea> = { | ||
args: { | ||
_patternId: '', | ||
type: 'text-area', | ||
inputId: 'test-textarea', | ||
value: '', | ||
label: 'Please enter your comments', | ||
required: true, | ||
maxLength: 500, | ||
}, | ||
}; | ||
|
||
export const NotRequired: StoryObj<typeof TextArea> = { | ||
args: { | ||
_patternId: '', | ||
type: 'text-area', | ||
inputId: 'test-textarea', | ||
value: '', | ||
label: 'Please enter your comments', | ||
required: false, | ||
maxLength: 500, | ||
}, | ||
}; | ||
|
||
export const ErrorState: StoryObj<typeof TextArea> = { | ||
args: { | ||
_patternId: '', | ||
type: 'text-area', | ||
inputId: 'test-textarea', | ||
value: '', | ||
label: 'Please enter your comments', | ||
required: true, | ||
maxLength: 500, | ||
error: { | ||
type: 'required', | ||
message: 'This field is required', | ||
}, | ||
}, | ||
}; | ||
|
||
export const WithHint: StoryObj<typeof TextArea> = { | ||
args: { | ||
_patternId: '', | ||
type: 'text-area', | ||
inputId: 'test-textarea', | ||
value: '', | ||
label: 'Please enter your comments', | ||
required: false, | ||
maxLength: 500, | ||
hint: 'This is a hint that provides additional context to the user.', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import React from 'react'; | ||
import classNames from 'classnames'; | ||
import { useFormContext } from 'react-hook-form'; | ||
|
||
import { type TextAreaProps } from '@atj/forms'; | ||
import { type PatternComponent } from '../../../Form/index.js'; | ||
|
||
const TextArea: PatternComponent<TextAreaProps> = ({ | ||
inputId, | ||
label, | ||
required, | ||
error, | ||
value, | ||
hint, | ||
maxLength, | ||
}) => { | ||
const { register } = useFormContext(); | ||
const errorId = `input-error-message-${inputId}`; | ||
const hintId = `input-hint-${inputId}`; | ||
|
||
return ( | ||
<fieldset className="usa-fieldset"> | ||
<div | ||
className={classNames('usa-form-group margin-top-2', { | ||
'usa-form-group--error': error, | ||
})} | ||
> | ||
<div className={classNames('usa-form-group margin-top-2')}> | ||
<label | ||
className={classNames('usa-label', { | ||
'usa-label--error': error, | ||
})} | ||
htmlFor={`textarea-${inputId}`} | ||
> | ||
{label} | ||
{required && <span className="required-indicator">*</span>} | ||
</label> | ||
{hint && ( | ||
<div className="usa-hint" id={hintId}> | ||
{hint} | ||
</div> | ||
)} | ||
{error && ( | ||
<div className="usa-error-message" id={errorId} role="alert"> | ||
{error.message} | ||
</div> | ||
)} | ||
<textarea | ||
className={classNames('usa-textarea', { | ||
'usa-textarea--error': error, | ||
})} | ||
id={`textarea-${inputId}`} | ||
defaultValue={value} | ||
{...register(inputId, { required })} | ||
aria-describedby={ | ||
`${hint ? `${hintId}` : ''}${error ? ` ${errorId}` : ''}`.trim() || | ||
undefined | ||
} | ||
maxLength={maxLength} | ||
style={{ resize: 'none', overflow: 'auto', height: '100px' }} | ||
/> | ||
</div> | ||
</div> | ||
</fieldset> | ||
); | ||
}; | ||
|
||
export default TextArea; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
...n/src/FormManager/FormEdit/components/TextAreaPatternEdit/TextAreaPattermEdit.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import { enLocale as message } from '@atj/common'; | ||
import { type TextAreaPattern } from '@atj/forms'; | ||
|
||
import { | ||
createPatternEditStoryMeta, | ||
testEmptyFormLabelError, | ||
testUpdateFormFieldOnSubmit, | ||
} from '../common/story-helper.js'; | ||
import FormEdit from '../../index.js'; | ||
|
||
const pattern: TextAreaPattern = { | ||
id: '1', | ||
type: 'text-area', | ||
data: { | ||
label: message.patterns.textarea.displayName, | ||
required: false, | ||
maxLength: 256, | ||
initial: 'Initial text', | ||
}, | ||
}; | ||
|
||
const storyConfig: Meta = { | ||
title: 'Edit components/TextAreaPattern', | ||
...createPatternEditStoryMeta({ | ||
pattern, | ||
}), | ||
} as Meta<typeof FormEdit>; | ||
export default storyConfig; | ||
|
||
export const Basic: StoryObj<typeof FormEdit> = { | ||
play: async ({ canvasElement }) => { | ||
await testUpdateFormFieldOnSubmit( | ||
canvasElement, | ||
message.patterns.textarea.displayName, | ||
message.patterns.textarea.fieldLabel, | ||
'Updated textarea pattern' | ||
); | ||
}, | ||
}; | ||
|
||
export const Error: StoryObj<typeof FormEdit> = { | ||
play: async ({ canvasElement }) => { | ||
await testEmptyFormLabelError( | ||
canvasElement, | ||
message.patterns.textarea.displayName, | ||
message.patterns.textarea.fieldLabel, | ||
message.patterns.textarea.fieldLabelRequired | ||
); | ||
}, | ||
}; |
149 changes: 149 additions & 0 deletions
149
packages/design/src/FormManager/FormEdit/components/TextAreaPatternEdit/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import classNames from 'classnames'; | ||
import React from 'react'; | ||
|
||
import { PatternId, TextAreaProps } from '@atj/forms'; | ||
import { TextAreaPattern } from '@atj/forms'; | ||
|
||
import TextArea from '../../../../Form/components/TextArea/index.js'; | ||
import { useFormManagerStore } from '../../../store.js'; | ||
import { PatternEditComponent } from '../../types.js'; | ||
|
||
import { PatternEditActions } from '../common/PatternEditActions.js'; | ||
import { PatternEditForm } from '../common/PatternEditForm.js'; | ||
import { usePatternEditFormContext } from '../common/hooks.js'; | ||
import { enLocale as message } from '@atj/common'; | ||
|
||
const TextAreaPatternEdit: PatternEditComponent<TextAreaProps> = ({ | ||
context, | ||
focus, | ||
previewProps, | ||
}) => { | ||
return ( | ||
<> | ||
{focus ? ( | ||
<PatternEditForm | ||
pattern={focus.pattern} | ||
editComponent={<EditComponent patternId={focus.pattern.id} />} | ||
></PatternEditForm> | ||
) : ( | ||
<div className="padding-left-3 padding-bottom-3 padding-right-3"> | ||
<TextArea context={context} {...previewProps} /> | ||
</div> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
const EditComponent = ({ patternId }: { patternId: PatternId }) => { | ||
const pattern = useFormManagerStore<TextAreaPattern>( | ||
state => state.session.form.patterns[patternId] | ||
); | ||
const { fieldId, register, getFieldState } = | ||
usePatternEditFormContext<TextAreaPattern>(patternId); | ||
|
||
const label = getFieldState('label'); | ||
const maxLength = getFieldState('maxLength'); | ||
const hint = getFieldState('hint'); | ||
const maxLengthAttributes = | ||
pattern.data.maxLength > 0 | ||
? { | ||
defaultValue: pattern.data.maxLength, | ||
} | ||
: {}; | ||
|
||
return ( | ||
<div className="grid-row grid-gap-1"> | ||
<div className="tablet:grid-col-12 mobile-lg:grid-col-12"> | ||
<label | ||
className={classNames('usa-label', { | ||
'usa-label--error': label.error, | ||
})} | ||
htmlFor={fieldId('label')} | ||
> | ||
{message.patterns.textarea.fieldLabel} | ||
{label.error ? ( | ||
<span className="usa-error-message" role="alert"> | ||
{label.error.message} | ||
</span> | ||
) : null} | ||
<input | ||
className={classNames('usa-input bg-primary-lighter', { | ||
'usa-input--error': label.error, | ||
})} | ||
id={fieldId('label')} | ||
defaultValue={pattern.data.label} | ||
{...register('label')} | ||
type="text" | ||
autoFocus | ||
></input> | ||
</label> | ||
</div> | ||
<div className="tablet:grid-col-12 mobile-lg:grid-col-12 margin-bottom-2"> | ||
<label | ||
className={classNames('usa-label', { | ||
'usa-label--error': hint.error, | ||
})} | ||
> | ||
{message.patterns.textarea.hintLabel} | ||
{hint.error ? ( | ||
<span className="usa-error-message" role="alert"> | ||
{hint.error.message} | ||
</span> | ||
) : null} | ||
<input | ||
className="usa-input" | ||
id={fieldId('hint')} | ||
defaultValue={pattern.data.hint} | ||
{...register('hint')} | ||
type="text" | ||
/> | ||
</label> | ||
</div> | ||
<div className="tablet:grid-col-6 mobile-lg:grid-col-12"> | ||
<label | ||
className={classNames('usa-label', { | ||
'usa-label--error': maxLength.error, | ||
})} | ||
htmlFor={fieldId('maxLength')} | ||
> | ||
{maxLength.error ? ( | ||
<span className="usa-error-message" role="alert"> | ||
{maxLength.error.message} | ||
</span> | ||
) : null} | ||
{message.patterns.textarea.maxLength} | ||
<input | ||
className="usa-input bg-primary-lighter text-bold" | ||
id={fieldId('maxLength')} | ||
{...maxLengthAttributes} | ||
{...register('maxLength')} | ||
type="text" | ||
></input> | ||
</label> | ||
</div> | ||
<div className="grid-col-12"> | ||
<PatternEditActions> | ||
<span className="usa-checkbox"> | ||
<input | ||
style={{ display: 'inline-block' }} | ||
className="usa-checkbox__input bg-primary-lighter" | ||
type="checkbox" | ||
id={fieldId('required')} | ||
{...register('required')} | ||
defaultChecked={pattern.data.required} | ||
/> | ||
<label | ||
style={{ display: 'inline-block' }} | ||
className="usa-checkbox__label" | ||
htmlFor={fieldId('required')} | ||
> | ||
Required | ||
</label> | ||
</span> | ||
</PatternEditActions> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default TextAreaPatternEdit; |
Oops, something went wrong.