Add DateTimePickerControl Form stories and tests (#34964)

* Add test for DataTImePickerControl in Form
* Add DateTimePickerControl to Form story
* Export type for InputProps
This commit is contained in:
Matt Sherman 2022-10-18 15:06:04 -04:00 committed by GitHub
parent c79af1acc5
commit 0769625b72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 95 additions and 25 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Export ImportProps type. Add DateTimePickerControl to Form stories and tests.

View File

@ -8,6 +8,16 @@ export type FormErrors< Values > = {
[ P in keyof Values ]?: FormErrors< Values[ P ] > | string;
};
export type InputProps< Value > = {
value: Value;
checked: boolean;
selected?: boolean;
onChange: ( value: ChangeEvent< HTMLInputElement > | Value ) => void;
onBlur: () => void;
className: string | undefined;
help: string | null | undefined;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type FormContext< Values extends Record< string, any > > = {
values: Values;
@ -23,15 +33,7 @@ export type FormContext< Values extends Record< string, any > > = {
handleSubmit: () => Promise< Values >;
getInputProps< Value extends Values[ keyof Values ] >(
name: string
): {
value: Value;
checked: boolean;
selected?: boolean;
onChange: ( value: ChangeEvent< HTMLInputElement > | Value ) => void;
onBlur: () => void;
className: string | undefined;
help: string | null | undefined;
};
): InputProps< Value >;
isValidForm: boolean;
resetForm: (
initialValues: Values,

View File

@ -21,7 +21,7 @@ import _isEqual from 'lodash/isEqual';
/**
* Internal dependencies
*/
import { FormContext, FormErrors } from './form-context';
import { FormContext, FormErrors, InputProps } from './form-context';
type FormProps< Values > = {
/**
@ -269,17 +269,7 @@ function FormComponent< Values extends Record< string, any > >(
function getInputProps< Value = Values[ keyof Values ] >(
name: string
): {
value: Value;
checked: boolean;
selected?: boolean;
onChange: (
value: ChangeEvent< HTMLInputElement > | Values[ keyof Values ]
) => void;
onBlur: () => void;
className: string | undefined;
help: string | null | undefined;
} {
): InputProps< Value > {
const inputValue = _get( values, name );
const isTouched = touched[ name ];
const inputError = _get( errors, name );
@ -288,9 +278,8 @@ function FormComponent< Values extends Record< string, any > >(
value: inputValue,
checked: Boolean( inputValue ),
selected: inputValue,
onChange: (
value: ChangeEvent< HTMLInputElement > | Values[ keyof Values ]
) => handleChange( name, value ),
onChange: ( value: ChangeEvent< HTMLInputElement > | Value ) =>
handleChange( name, value as Values[ keyof Values ] ),
onBlur: () => handleBlur( name ),
className: isTouched && inputError ? 'has-error' : undefined,
help: isTouched ? ( inputError as string ) : null,

View File

@ -9,7 +9,8 @@ import {
TextControl,
} from '@wordpress/components';
import { useState } from '@wordpress/element';
import { Form } from '@woocommerce/components';
import { Form, DateTimePickerControl } from '@woocommerce/components';
import moment from 'moment';
const validate = ( values ) => {
const errors = {};
@ -19,6 +20,9 @@ const validate = ( values ) => {
if ( values.lastName.length < 3 ) {
errors.lastName = 'Last name must be at least 3 characters';
}
if ( ! moment( values.date, moment.ISO_8601, true ).isValid() ) {
errors.date = 'Invalid date';
}
return errors;
};
@ -28,6 +32,7 @@ const initialValues = {
firstName: '',
lastName: '',
select: '3',
date: '2014-10-24T13:02',
checkbox: true,
radio: 'one',
};
@ -68,6 +73,13 @@ export const Basic = () => {
] }
{ ...getInputProps( 'select' ) }
/>
<DateTimePickerControl
label="Date"
dateTimeFormat="YYYY-MM-DD HH:mm"
placeholder="Enter a date"
currentDate={ values.date }
{ ...getInputProps( 'date' ) }
/>
<CheckboxControl
label="Checkbox"
{ ...getInputProps( 'checkbox' ) }

View File

@ -11,6 +11,7 @@ import { TextControl } from '@wordpress/components';
*/
import { Form, useFormContext } from '../';
import type { FormContext } from '../';
import { DateTimePickerControl } from '../../date-time-picker-control';
const TestInputWithContext = () => {
const formProps = useFormContext< { foo: string } >();
@ -407,6 +408,68 @@ describe( 'Form', () => {
);
} );
// We need to bump up the timeout for this test because:
// 1. userEvent.type() is slow (see https://github.com/testing-library/user-event/issues/577)
// 2. moment.js is slow
// Otherwise, the following error can occur on slow machines (such as our CI), because Jest times out and starts
// tearing down the component while test microtasks are still being executed
// (see https://github.com/facebook/jest/issues/12670)
// TypeError: Cannot read properties of null (reading 'createEvent')
it( 'should provide props that automatically handle DateTimePickerControl changes', async () => {
const newDateTimeInputString = 'invalid input';
type TestData = { date: string };
const mockOnChange = jest.fn();
function validate(): Record< string, string > {
return { date: 'This is a bad date' };
}
const { container, queryByText } = render(
<Form< TestData > onChange={ mockOnChange } validate={ validate }>
{ ( { getInputProps, values }: FormContext< TestData > ) => {
return (
<DateTimePickerControl
label={ 'Date' }
onChangeDebounceWait={ 10 }
currentDate={ values.date }
{ ...getInputProps( 'date' ) }
/>
);
} }
</Form>
);
const controlRoot = container.querySelector(
'.woocommerce-date-time-picker-control'
);
const input = controlRoot?.querySelector( 'input' );
userEvent.type(
input!,
'{selectall}{backspace}' + newDateTimeInputString
);
fireEvent.blur( input! );
await waitFor(
() => {
expect( mockOnChange ).toHaveBeenLastCalledWith(
{ name: 'date', value: newDateTimeInputString },
{ date: newDateTimeInputString },
false
);
expect( controlRoot?.classList.contains( 'has-error' ) ).toBe(
true
);
expect(
queryByText( 'This is a bad date' )
).toBeInTheDocument();
},
{ timeout: 100 }
);
}, 10000 );
describe( 'FormContext', () => {
it( 'should allow nested field to use useFormContext to set field value', async () => {
const mockOnChange = jest.fn();