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:
parent
c79af1acc5
commit
0769625b72
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Export ImportProps type. Add DateTimePickerControl to Form stories and tests.
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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' ) }
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue