Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Convert validation context to data store #6402

Merged
merged 66 commits into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
d01dea1
Add validation reducers, actions, and action types
opr Jan 5, 2022
454525b
Add selector for getValidationErrors
opr Jan 5, 2022
742291b
Export store key and register store
opr Jan 5, 2022
8553b8b
Export validation store key
opr Jan 5, 2022
6196d5f
Move TextInput files to checkout package
opr Jan 5, 2022
50dd283
Export ValidatedTextInput from blocks-checkout package
opr Jan 5, 2022
dafe1ae
Update imports of ValidatedTextInput to reflect new location
opr Jan 5, 2022
38137d0
Use the validation wp-data store for showing error messages
opr Jan 6, 2022
5fd836c
Export getValidationError in checkout package
opr Jan 6, 2022
534555f
Move validation store to checkout package
opr Jan 6, 2022
ab6cb8f
Move ValidationInputError to blocks-checkout package
opr Jan 6, 2022
cb72301
Only export "exposedSelectors" from validation
opr Jan 6, 2022
90fc97d
Convert validation context to data store
May 11, 2022
92a2cbe
Fixed linting error
May 11, 2022
47c50ed
Fixed linting error
May 11, 2022
6c8f82c
Change the validation selectors to return a function
May 12, 2022
2ccb4fd
Convert reducer and selectors to TS
opr Jun 6, 2022
a9d361b
Remove superfluous comments and improve test titles
opr Jun 6, 2022
975c857
Test to ensure visible errors remain visible
opr Jun 6, 2022
4320c39
Make test for hasValidationErrors more robust
opr Jun 6, 2022
53e6e97
Augment the wp-data module to include our selectors and actions
opr Jun 6, 2022
7cdcac0
Removed unused `exposedSelectors` variable
opr Jun 6, 2022
0caf437
Remove TS error because of `instanceId` on props
opr Jun 6, 2022
5df2d68
Remove unnecessary as const
opr Jun 6, 2022
b09d95a
Use function returned by getValidationError
opr Jun 6, 2022
a5db7a6
Use correct selector/action names now context has been decoupled
opr Jun 6, 2022
49d1d4c
hide validation error when input value changes
opr Jun 6, 2022
cd0e185
Add correct aria-describedBy now we can get error id from store
opr Jun 6, 2022
838c0f3
Clear validation error from store when component unmounts
opr Jun 6, 2022
a1eacb7
Clear validation error if input is valid
opr Jun 6, 2022
e258a15
convert ValidationInputError to TS and get correct id/error from store
opr Jun 6, 2022
cee0130
Ensure checkout block doesn't break when there are no errors
opr Jun 6, 2022
6c03fd2
Get validation data from the store instead of context
opr Jun 6, 2022
2f47042
Update country input to remove validation context
opr Jun 6, 2022
f59cb2e
Move validation store out of checkout package
opr Jun 6, 2022
bb6190b
Move TextInput and ValidationInputError back out of the checkout package
opr Jun 6, 2022
5f7ac4c
Remove duplicate internal styles comment
opr Jun 6, 2022
0eada75
Remove exports that no longer exist
opr Jun 6, 2022
afbaad3
Get validation store key from block-data
opr Jun 6, 2022
2aeee07
Make attribute-select-control use validation data store
opr Jun 6, 2022
4c8d7b7
Export FieldValidationStatus type
opr Jun 6, 2022
0e86dfb
Make combobox use validation store not context
opr Jun 6, 2022
ab93db7
Make Address use validation store not context
opr Jun 6, 2022
f8bdcf0
Make Address use validation store not context
opr Jun 6, 2022
9ae7dd8
Use hasValidationErrors selector as a function in shipping calculator
opr Jun 6, 2022
52a51ad
Remove validation context from coupon story
opr Jun 6, 2022
1136f55
Import VALIDATION_STORE_KEY from correct location
opr Jun 6, 2022
28dfa92
Stop coupon story from erroring
opr Jun 6, 2022
bb04b9e
Update useStoreCartCoupons to use validation store not context
opr Jun 6, 2022
420c1fb
Make TotalsCoupon use validation store instead of context
opr Jun 6, 2022
b2ce39b
Make AddToCartFormContext use validation store not context
opr Jun 6, 2022
4a2d095
Remove ValidationContext
opr Jun 7, 2022
c5b1939
Import FieldValidationStatus from correct location
opr Jun 7, 2022
22b218c
Import ValidatedTextInput and ValidatedTextInput from correct location
opr Jun 7, 2022
f65243f
Remove ValidationContextProvider
opr Jun 7, 2022
1600164
Update components to use validation store not context
opr Jun 7, 2022
1cc0985
Update useValidation to use the data store
opr Jun 7, 2022
1a6df24
Merge branch 'feature/data-store-refactor' into fix/wpdata-validation
tarhi-saad Jun 30, 2022
8184066
Replace the validation context in checkout-events file
tarhi-saad Jun 30, 2022
0d9ac27
Use the re-mapped path for the store key import
tarhi-saad Jul 1, 2022
9420abb
Use "register" instead of the deprecated "registerStore"
tarhi-saad Jul 1, 2022
694274a
Fix import error of the "FieldValidationStatus" type
tarhi-saad Jul 1, 2022
7a8a7ab
Use TS instead of React's "PropTypes"
tarhi-saad Jul 1, 2022
b603ab0
Fix the type of "ValidationInputError" in the "payment-method-interface"
tarhi-saad Jul 1, 2022
1f0f9be
Fix error not showing on the first place order click bug
tarhi-saad Jul 1, 2022
570455d
Fix state mutation issue in the Validation reducer
tarhi-saad Jul 1, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import { decodeEntities } from '@wordpress/html-entities';
import { SelectControl } from 'wordpress-components';
import { useEffect } from 'react';
import classnames from 'classnames';
import {
ValidationInputError,
useValidationContext,
} from '@woocommerce/base-context';
import { ValidationInputError } from '@woocommerce/base-components/validation-input-error';
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
import { useDispatch, useSelect } from '@wordpress/data';

// Default option for select boxes.
const selectAnOption = {
Expand All @@ -32,8 +31,16 @@ const AttributeSelectControl = ( {
'woo-gutenberg-products-block'
),
} ) => {
const { getValidationError, setValidationErrors, clearValidationError } =
useValidationContext();
const { setValidationErrors, clearValidationError } = useDispatch(
VALIDATION_STORE_KEY
);

const { getValidationError } = useSelect( ( select ) => {
const store = select( VALIDATION_STORE_KEY );
return {
getValidationError: store.getValidationError(),
};
} );
const errorId = attributeName;
const error = getValidationError( errorId ) || {};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
BillingStateInput,
ShippingStateInput,
} from '@woocommerce/base-components/state-input';
import { useValidationContext } from '@woocommerce/base-context';
import { useEffect, useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { withInstanceId } from '@wordpress/compose';
Expand All @@ -22,6 +21,11 @@ import {
defaultAddressFields,
EnteredAddress,
} from '@woocommerce/settings';
import { useSelect, useDispatch } from '@wordpress/data';
import {
VALIDATION_STORE_KEY,
FieldValidationStatus,
} from '@woocommerce/block-data';

/**
* Internal dependencies
Expand All @@ -32,7 +36,9 @@ import prepareAddressFields from './prepare-address-fields';
// values without having set the country first, show an error.
const validateShippingCountry = (
values: EnteredAddress,
setValidationErrors: ( errors: Record< string, unknown > ) => void,
setValidationErrors: (
errors: Record< string, FieldValidationStatus >
) => void,
clearValidationError: ( error: string ) => void,
hasValidationError: boolean
): void => {
Expand Down Expand Up @@ -87,8 +93,14 @@ const AddressForm = ( {
type = 'shipping',
values,
}: AddressFormProps ): JSX.Element => {
const { getValidationError, setValidationErrors, clearValidationError } =
useValidationContext();
const { setValidationErrors, clearValidationError } = useDispatch(
VALIDATION_STORE_KEY
);

const getValidationError = useSelect( ( select ) => {
const store = select( VALIDATION_STORE_KEY );
return store.getValidationError();
} );

const currentFields = useShallowEqual( fields );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { __ } from '@wordpress/i18n';
import Button from '@woocommerce/base-components/button';
import { useState } from '@wordpress/element';
import isShallowEqual from '@wordpress/is-shallow-equal';
import { useValidationContext } from '@woocommerce/base-context';
import type { EnteredAddress, AddressFields } from '@woocommerce/settings';
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
import { useDispatch, useSelect } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -25,12 +26,18 @@ const ShippingCalculatorAddress = ( {
addressFields,
}: ShippingCalculatorAddressProps ): JSX.Element => {
const [ address, setAddress ] = useState( initialAddress );
const { hasValidationErrors, showAllValidationErrors } =
useValidationContext();
const { showAllValidationErrors } = useDispatch( VALIDATION_STORE_KEY );

const { hasValidationErrors } = useSelect( ( select ) => {
const store = select( VALIDATION_STORE_KEY );
return {
hasValidationErrors: store.hasValidationErrors,
};
} );

const validateSubmit = () => {
showAllValidationErrors();
return ! hasValidationErrors;
return ! hasValidationErrors();
};

return (
Expand Down
22 changes: 15 additions & 7 deletions assets/js/base/components/cart-checkout/totals/coupon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
import { __ } from '@wordpress/i18n';
import { useState, useEffect, useRef } from '@wordpress/element';
import Button from '@woocommerce/base-components/button';
import { ValidatedTextInput } from '@woocommerce/base-components/text-input';
import { Panel } from '@woocommerce/blocks-checkout';
import Label from '@woocommerce/base-components/label';
import LoadingMask from '@woocommerce/base-components/loading-mask';
import { withInstanceId } from '@wordpress/compose';
import {
ValidationInputError,
useValidationContext,
} from '@woocommerce/base-context';
import { Panel } from '@woocommerce/blocks-checkout';
import { ValidatedTextInput } from '@woocommerce/base-components/text-input';
import ValidationInputError from '@woocommerce/base-components/validation-input-error';
import { useSelect } from '@wordpress/data';
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
Expand Down Expand Up @@ -46,7 +45,16 @@ export const TotalsCoupon = ( {
}: TotalsCouponProps ): JSX.Element => {
const [ couponValue, setCouponValue ] = useState( '' );
const currentIsLoading = useRef( false );
const { getValidationError, getValidationErrorId } = useValidationContext();
const { getValidationError, getValidationErrorId } = useSelect(
( select ) => {
const store = select( VALIDATION_STORE_KEY );
return {
getValidationError: store.getValidationError(),
getValidationErrorId: store.getValidationErrorId(),
};
}
);

const validationError = getValidationError( 'coupon' );

useEffect( () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
*/
import { useArgs } from '@storybook/client-api';
import { Story, Meta } from '@storybook/react';
import {
useValidationContext,
ValidationContextProvider,
} from '@woocommerce/base-context';
import { INTERACTION_TIMEOUT } from '@woocommerce/storybook-controls';
import { useDispatch } from '@wordpress/data';
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
Expand Down Expand Up @@ -52,7 +50,7 @@ LoadingState.args = {
};

export const ErrorState: Story< TotalsCouponProps > = ( args ) => {
const { setValidationErrors } = useValidationContext();
const { setValidationErrors } = useDispatch( VALIDATION_STORE_KEY );

setValidationErrors( { coupon: INVALID_COUPON_ERROR } );

Expand All @@ -61,10 +59,6 @@ export const ErrorState: Story< TotalsCouponProps > = ( args ) => {

ErrorState.decorators = [
( StoryComponent ) => {
return (
<ValidationContextProvider>
<StoryComponent />
</ValidationContextProvider>
);
return <StoryComponent />;
},
];
16 changes: 10 additions & 6 deletions assets/js/base/components/combobox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import { __ } from '@wordpress/i18n';
import { useEffect, useRef } from '@wordpress/element';
import { withInstanceId } from '@wordpress/compose';
import { ComboboxControl } from 'wordpress-components';
import {
ValidationInputError,
useValidationContext,
} from '@woocommerce/base-context';
import { ValidationInputError } from '@woocommerce/base-components/validation-input-error';
import { isObject } from '@woocommerce/types';
import { useDispatch, useSelect } from '@wordpress/data';
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
Expand Down Expand Up @@ -55,8 +54,13 @@ const Combobox = ( {
instanceId = '0',
autoComplete = 'off',
}: ComboboxProps ): JSX.Element => {
const { getValidationError, setValidationErrors, clearValidationError } =
useValidationContext();
const { setValidationErrors, clearValidationError } = useDispatch(
VALIDATION_STORE_KEY
);
const getValidationError = useSelect( ( select ) => {
const store = select( VALIDATION_STORE_KEY );
return store.getValidationError();
} );

const controlRef = useRef< HTMLDivElement >( null );
const controlId = id || 'control-' + instanceId;
Expand Down
19 changes: 6 additions & 13 deletions assets/js/base/components/country-input/stories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
* External dependencies
*/
import { Story, Meta } from '@storybook/react';
import {
useValidationContext,
ValidationContextProvider,
} from '@woocommerce/base-context';
import { useDispatch } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element';
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
Expand All @@ -31,21 +29,16 @@ export default {
options: { table: { disable: true } },
value: { control: false },
},
decorators: [
( StoryComponent ) => (
<ValidationContextProvider>
<StoryComponent />
</ValidationContextProvider>
),
],
decorators: [ ( StoryComponent ) => <StoryComponent /> ],
} as Meta< CountryInputWithCountriesProps >;

const Template: Story< CountryInputWithCountriesProps > = ( args ) => {
const [ selectedCountry, selectCountry ] = useState< CountryCode | '' >(
''
);
const { clearValidationError, showValidationError } =
useValidationContext();
const { clearValidationError, showValidationError } = useDispatch(
VALIDATION_STORE_KEY
);

useEffect( () => {
showValidationError( 'country' );
Expand Down
2 changes: 1 addition & 1 deletion assets/js/base/components/state-input/state-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { __ } from '@wordpress/i18n';
import { decodeEntities } from '@wordpress/html-entities';
import { useCallback, useMemo, useEffect, useRef } from '@wordpress/element';
import classnames from 'classnames';
import { ValidatedTextInput } from '@woocommerce/base-components/text-input';

/**
* Internal dependencies
*/
import { ValidatedTextInput } from '../text-input';
import Combobox from '../combobox';
import './style.scss';
import type { StateInputWithStatesProps } from './StateInputProps';
Expand Down
59 changes: 25 additions & 34 deletions assets/js/base/components/text-input/validated-text-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,21 @@
import { __ } from '@wordpress/i18n';
import { useCallback, useRef, useEffect, useState } from 'react';
import classnames from 'classnames';
import {
ValidationInputError,
useValidationContext,
} from '@woocommerce/base-context';
import { withInstanceId } from '@wordpress/compose';
import { isString } from '@woocommerce/types';
import { dispatch, useSelect } from '@wordpress/data';
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
*/
import TextInput from './text-input';
import './style.scss';
import { ValidationInputError } from '../validation-input-error';

interface ValidatedTextInputPropsWithId {
instanceId?: string;
id: string;
}

interface ValidatedTextInputPropsWithInstanceId {
instanceId: string;
interface ValidatedTextInputProps {
id?: string;
}

type ValidatedTextInputProps = (
| ValidatedTextInputPropsWithId
| ValidatedTextInputPropsWithInstanceId
) & {
instanceId: string;
className?: string;
ariaDescribedBy?: string;
errorId?: string;
Expand All @@ -39,7 +27,7 @@ type ValidatedTextInputProps = (
errorMessage?: string;
onChange: ( newValue: string ) => void;
value: string;
};
}

const ValidatedTextInput = ( {
className,
Expand All @@ -53,20 +41,26 @@ const ValidatedTextInput = ( {
errorMessage: passedErrorMessage = '',
value = '',
...rest
}: ValidatedTextInputProps ) => {
}: ValidatedTextInputProps ): JSX.Element => {
const [ isPristine, setIsPristine ] = useState( true );
const inputRef = useRef< HTMLInputElement >( null );
const {
getValidationError,
hideValidationError,
setValidationErrors,
clearValidationError,
getValidationErrorId,
} = useValidationContext();

const { setValidationErrors, hideValidationError, clearValidationError } =
dispatch( VALIDATION_STORE_KEY );
const textInputId =
typeof id !== 'undefined' ? id : 'textinput-' + instanceId;
const errorIdString = errorId !== undefined ? errorId : textInputId;

const { getValidationError, getValidationErrorId } = useSelect(
( select ) => {
const store = select( VALIDATION_STORE_KEY );
return {
getValidationError: store.getValidationError(),
getValidationErrorId: store.getValidationErrorId(),
};
}
);

const validateInput = useCallback(
( errorsHidden = true ) => {
const inputObject = inputRef.current || null;
Expand All @@ -79,7 +73,7 @@ const ValidatedTextInput = ( {
if ( inputIsValid ) {
clearValidationError( errorIdString );
} else {
setValidationErrors( {
const validationErrors = {
[ errorIdString ]: {
message:
inputObject.validationMessage ||
Expand All @@ -89,7 +83,8 @@ const ValidatedTextInput = ( {
),
hidden: errorsHidden,
},
} );
};
setValidationErrors( validationErrors );
}
},
[ clearValidationError, errorIdString, setValidationErrors ]
Expand Down Expand Up @@ -129,17 +124,13 @@ const ValidatedTextInput = ( {
};
}, [ clearValidationError, errorIdString ] );

// @todo - When useValidationContext is converted to TypeScript, remove this cast and use the correct type.
const errorMessage = ( getValidationError( errorIdString ) || {} ) as {
message?: string;
hidden?: boolean;
};
const errorMessage = getValidationError( errorIdString );

if ( isString( passedErrorMessage ) && passedErrorMessage !== '' ) {
errorMessage.message = passedErrorMessage;
}

const hasError = errorMessage.message && ! errorMessage.hidden;
const hasError = errorMessage?.message && ! errorMessage?.hidden;
const describedBy =
showError && hasError && getValidationErrorId( errorIdString )
? getValidationErrorId( errorIdString )
Expand Down
Loading