-
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.
Merge pull request #33 from open-formulieren/feature/31-date-component
Implement form builder for date component type
- Loading branch information
Showing
25 changed files
with
1,443 additions
and
123 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
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
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,101 @@ | ||
import {expect} from '@storybook/jest'; | ||
import {Meta, StoryObj} from '@storybook/react'; | ||
import {userEvent, within} from '@storybook/testing-library'; | ||
|
||
import {withFormik} from '@/sb-decorators'; | ||
|
||
import DateField from './datefield'; | ||
|
||
export default { | ||
title: 'Formio/Components/DateField', | ||
component: DateField, | ||
decorators: [withFormik], | ||
parameters: { | ||
modal: {noModal: true}, | ||
formik: {initialValues: {'my-datefield': '1980-01-01'}}, | ||
}, | ||
args: { | ||
name: 'my-datefield', | ||
}, | ||
} as Meta<typeof DateField>; | ||
|
||
type Story = StoryObj<typeof DateField>; | ||
|
||
export const Required: Story = { | ||
args: { | ||
required: true, | ||
label: 'A required datefield', | ||
}, | ||
}; | ||
|
||
export const WithoutLabel: Story = { | ||
args: { | ||
label: '', | ||
}, | ||
}; | ||
|
||
export const WithToolTip: Story = { | ||
args: { | ||
label: 'With tooltip', | ||
tooltip: 'Hiya!', | ||
required: false, | ||
}, | ||
}; | ||
|
||
export const Multiple: Story = { | ||
args: { | ||
label: 'Multiple inputs', | ||
description: 'Array of dates instead of a single date value', | ||
multiple: true, | ||
}, | ||
|
||
parameters: { | ||
formik: { | ||
initialValues: {'my-datefield': ['1980-01-01']}, | ||
}, | ||
}, | ||
|
||
play: async ({canvasElement}) => { | ||
const canvas = within(canvasElement); | ||
|
||
// check that new items can be added | ||
await userEvent.click(canvas.getByRole('button', {name: 'Add another'})); | ||
const input1 = canvas.getByTestId('input-my-datefield[0]'); | ||
await expect(input1).toHaveDisplayValue('1980-01-01'); | ||
|
||
await userEvent.clear(input1); | ||
await expect(input1).toHaveDisplayValue(''); | ||
|
||
// the label & description should be rendered only once, even with > 1 inputs | ||
await expect(canvas.queryAllByText('Multiple inputs')).toHaveLength(1); | ||
await expect( | ||
canvas.queryAllByText('Array of dates instead of a single date value') | ||
).toHaveLength(1); | ||
|
||
// finally, it should be possible delete rows again | ||
const removeButtons = await canvas.findAllByRole('button', {name: 'Remove item'}); | ||
await expect(removeButtons).toHaveLength(2); | ||
await userEvent.click(removeButtons[0]); | ||
await expect(canvas.getByTestId('input-my-datefield[0]')).toHaveDisplayValue(''); | ||
await expect(canvas.queryByTestId('input-my-datefield[1]')).not.toBeInTheDocument(); | ||
}, | ||
}; | ||
|
||
export const WithErrors: Story = { | ||
args: { | ||
label: 'With errors', | ||
}, | ||
|
||
parameters: { | ||
formik: { | ||
initialValues: {'my-datefield': ''}, | ||
initialErrors: {'my-datefield': 'Example error', 'other-field': 'Other error'}, | ||
}, | ||
}, | ||
|
||
play: async ({canvasElement}) => { | ||
const canvas = within(canvasElement); | ||
await expect(canvas.queryByText('Other error')).not.toBeInTheDocument(); | ||
await expect(canvas.queryByText('Example error')).toBeInTheDocument(); | ||
}, | ||
}; |
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,80 @@ | ||
import clsx from 'clsx'; | ||
import {Field, useFormikContext} from 'formik'; | ||
import {useContext} from 'react'; | ||
|
||
import {RenderContext} from '@/context'; | ||
import {ErrorList, useValidationErrors} from '@/utils/errors'; | ||
|
||
import Component from './component'; | ||
import Description from './description'; | ||
import {withMultiple} from './multiple'; | ||
|
||
export interface DateFieldProps { | ||
name: string; | ||
label?: React.ReactNode; | ||
required?: boolean; | ||
tooltip?: string; | ||
description?: string; | ||
} | ||
|
||
export const DateField: React.FC<JSX.IntrinsicElements['input'] & DateFieldProps> = ({ | ||
name, | ||
label, | ||
required = false, | ||
tooltip = '', | ||
description = '', | ||
...props | ||
}) => { | ||
const {getFieldProps} = useFormikContext(); | ||
const {bareInput} = useContext(RenderContext); | ||
const {errors, hasErrors} = useValidationErrors(name); | ||
|
||
const htmlId = `editform-${name}`; | ||
|
||
const {value} = getFieldProps<string | undefined | null>(name); | ||
|
||
// let's not bother with date pickers - use the native browser date input instead. | ||
const inputField = ( | ||
<Field | ||
name={name} | ||
id={htmlId} | ||
as="input" | ||
type="date" | ||
className={clsx('form-control', {'is-invalid': hasErrors})} | ||
data-testid={`input-${name}`} | ||
// text fallback - use ISO-8601 | ||
pattern="\d{4}-\d{2}-\d{2}" | ||
value={value ?? ''} | ||
{...props} | ||
/> | ||
); | ||
|
||
// 'bare input' is actually a little bit more than just the input, looking at the | ||
// vanillay formio implementation. | ||
if (bareInput) { | ||
return ( | ||
<> | ||
{inputField} | ||
<ErrorList errors={errors} /> | ||
</> | ||
); | ||
} | ||
|
||
// default-mode, wrapping the field with label, description etc. | ||
return ( | ||
<Component | ||
type="date" | ||
field={name} | ||
required={required} | ||
htmlId={htmlId} | ||
label={label} | ||
tooltip={tooltip} | ||
> | ||
<div>{inputField}</div> | ||
{description && <Description text={description} />} | ||
</Component> | ||
); | ||
}; | ||
|
||
export const DateFieldMultiple = withMultiple(DateField, ''); | ||
export default DateFieldMultiple; |
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
Oops, something went wrong.