Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datefield split inputs #457

Merged
merged 7 commits into from
Jun 28, 2023
Merged
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: 2 additions & 0 deletions bin/find_untranslated_js.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def main():
# skip translated messages
if trans_object["defaultMessage"] != trans_object["originalDefault"]:
continue
if trans_object.get("isTranslated", False):
continue

print(
f"ID '{unique_id}' appears untranslated, defaultMessage: {trans_object['originalDefault']}"
Expand Down
2 changes: 1 addition & 1 deletion design-tokens
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@floating-ui/react": "^0.24.2",
"@formio/protected-eval": "^1.2.1",
"@fortawesome/fontawesome-free": "^6.1.1",
"@open-formulieren/design-tokens": "^0.34.0",
"@open-formulieren/design-tokens": "^0.35.0",
"@sentry/react": "^6.13.2",
"@sentry/tracing": "^6.13.2",
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
Expand Down
28 changes: 14 additions & 14 deletions scripts/i18n-formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ const fs = require('fs');
const argv = require('yargs').argv;

// load the existing catalog to prevent overwriting messages
const existingCatalog = JSON.parse(
fs.readFileSync(argv.outFile, 'utf-8')
);
const existingCatalog = JSON.parse(fs.readFileSync(argv.outFile, 'utf-8'));

const format = messages => {
Object.entries(messages).forEach(([id, msg]) => {
// always store the original (english) default message as a reference
msg.originalDefault = msg.defaultMessage;

const format = (messages) => {
Object.entries(messages).forEach(([id, msg]) => {
// always store the original (english) default message as a reference
msg.originalDefault = msg.defaultMessage;

// if the message with the ID is already in the catalog, re-use it
const existingMsg = existingCatalog[id];
if (!existingMsg) return;
msg.defaultMessage = existingMsg.defaultMessage;
});
return messages;
// if the message with the ID is already in the catalog, re-use it
const existingMsg = existingCatalog[id];
if (!existingMsg) return;
msg.defaultMessage = existingMsg.defaultMessage;
if (existingMsg.isTranslated) {
msg.isTranslated = true;
}
});
return messages;
};

exports.format = format;
1 change: 1 addition & 0 deletions src/components/appointments/DateSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const DateSelect = () => {
return (
<DateField
name="date"
widget="datepicker"
disabled={loading || !values.location}
isRequired
label={
Expand Down
49 changes: 49 additions & 0 deletions src/components/forms/DateField/DateField-datepicker.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {ArgTypes, Canvas, Meta} from '@storybook/blocks';

import DateField from './DateField';
import * as DatepickerStories from './DateField-datepicker.stories';
import DatePicker from './DatePicker';

<Meta of={DatepickerStories} />

# DateField - datepicker

## Date picker widget

The date picker widget is suitable for nearby dates (in the future or past).

A calendar component is appropriate because:

- you can display occupied dates and/or date ranges
- you can indicate the current date
- it is not expected that the user needs to click the year/month button too many times

We aim to make it accessible to assistive technology and keyboard-only navigation.

### Known issues/enhancements:

- Use arrow keys to navigate inside calendar
- Selecting a date in the next/previous month has some odd behaviour at the moment

## Widget prop

Use the prop `widget="datepicker"` to use this widget.

<Canvas of={DatepickerStories.Datepicker} story={{height: '60ex'}} />

## Props

<ArgTypes of={DateField} exclude={['showFormattedDate']} />

**DatePicker props**

Internally, the `DateField` uses the `DatePicker` widget.

<ArgTypes of={DatePicker} />

## No asterisks

The backend can be configured to treat fields as required by default and instead mark optional
fields explicitly.

<Canvas of={DatepickerStories.NoAsterisks} story={{height: '60ex'}} />
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ import {ConfigDecorator, FormikDecorator} from 'story-utils/decorators';
import DateField from './DateField';

export default {
title: 'Pure React Components / Forms / DateField',
title: 'Pure React Components / Forms / DateField / Datepicker',
component: DateField,
decorators: [FormikDecorator],
args: {
widget: 'datepicker',
},
argTypes: {
showFormattedDate: {table: {disable: true}},
},
parameters: {
formik: {
initialValues: {
Expand Down
39 changes: 39 additions & 0 deletions src/components/forms/DateField/DateField-inputgroup.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {ArgTypes, Canvas, Meta} from '@storybook/blocks';

import DateField from './DateField';
import * as InputGroupStories from './DateField-inputgroup.stories';
import DateInputGroup from './DateInputGroup';

<Meta of={InputGroupStories} />

# DateField - input group

## Memorable dates

Memorable dates are dates that users can easily remember, such as a date of birth or marriage.

To avoid users having to scroll/click a lot in calendar widgets, it's recommended to use the input
group widget where they can enter numbers for day, month and year.

## Widget prop

Use the prop `widget="inputGroup"` to use this widget.

<Canvas of={InputGroupStories.InputGroup} />

## Props

<ArgTypes of={DateField} exclude={['minDate', 'maxDate', 'disabledDates']} />

**DateInputGroup props**

Internally, the `DateField` uses the `DateInputGroup` widget.

<ArgTypes of={DateInputGroup} />

## No asterisks

The backend can be configured to treat fields as required by default and instead mark optional
fields explicitly.

<Canvas of={InputGroupStories.NoAsterisks} />
119 changes: 119 additions & 0 deletions src/components/forms/DateField/DateField-inputgroup.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import {expect, jest} from '@storybook/jest';
import {userEvent, waitFor, within} from '@storybook/testing-library';
import {Form, Formik} from 'formik';

import {ConfigDecorator, FormikDecorator} from 'story-utils/decorators';

import DateField from './DateField';

export default {
title: 'Pure React Components / Forms / DateField / Input group',
component: DateField,
decorators: [FormikDecorator],
args: {
widget: 'inputGroup',
showFormattedDate: false,
description: '',
id: '',
disabled: false,
},
argTypes: {
minDate: {table: {disable: true}},
maxDate: {table: {disable: true}},
disabledDates: {table: {disable: true}},
showFormattedDate: {control: 'boolean'},
},
parameters: {
formik: {
initialValues: {
test: '2023-06-16',
},
},
},
};

export const InputGroup = {
name: 'Input group',
args: {
name: 'test',
label: 'A memorable date',
description: "What's your favourite date?",
isRequired: false,
},
};

export const ISO8601 = {
name: 'Value normalizes to ISO-8601',
render: ({onSubmit}) => (
<Formik
initialValues={{test: ''}}
onSubmit={(values, actions) => {
console.log(onSubmit, values);
onSubmit(values);
actions.setSubmitting(false);
}}
>
<Form>
<DateField name="test" label="Test normalization" />
<button type="submit">Submit</button>
</Form>
</Formik>
),
parameters: {
formik: {disable: true},
},
argTypes: {
onSubmit: {action: true},
widget: {table: {disable: true}},
showFormattedDate: {table: {disable: true}},
description: {table: {disable: true}},
id: {table: {disable: true}},
disabled: {table: {disable: true}},
name: {table: {disable: true}},
label: {table: {disable: true}},
isRequired: {table: {disable: true}},
},
play: async ({canvasElement, args, step}) => {
const canvas = within(canvasElement);
await step('Fill out inputs', async () => {
await userEvent.type(canvas.getByLabelText('Dag'), '9');
await userEvent.type(canvas.getByLabelText('Maand'), '6');
await userEvent.type(canvas.getByLabelText('Jaar'), '2023');
});

await step('Submit form and inspect data', async () => {
await userEvent.click(canvas.getByRole('button'));
await waitFor(() => expect(args.onSubmit).toHaveBeenCalledWith({test: '2023-06-09'}));
});
},
};

export const NoAsterisks = {
name: 'No asterisk for required',
decorators: [ConfigDecorator],
parameters: {
config: {
requiredFieldsWithAsterisk: false,
},
},
args: {
name: 'test',
label: 'Default required',
isRequired: true,
},
};

export const WithValidationError = {
name: 'With validation error',
args: {
name: 'test',
label: 'When was the battle of Hastings?',
isRequired: true,
},
parameters: {
formik: {
initialValues: {test: '1066-10-34'},
initialErrors: {test: 'Invalid date entered'},
},
},
};
Loading