Storybook migration of `CountryInput` component (https://github.com/woocommerce/woocommerce-blocks/pull/5312)
* Enable `exactOptionalPropertyTypes` in TS config This option will create more strict types out of optional properties. Read more: https://devblogs.microsoft.com/typescript/announcing-typescript-4-4-beta/#exact-optional-property-types * Extract `ComboBoxProps` as own interface This way we can export it to use it somewhere else. In this specific case, I'll need to use it as a base to extend the `CountryInputProps`. Also fixed some types to be optional, as the component requires. * Fix stories and props for `CountryInput` (https://github.com/woocommerce/woocommerce-blocks/pull/5252)
This commit is contained in:
parent
de068d2fc3
commit
77c237d6a2
|
@ -22,6 +22,20 @@ export interface ComboboxControlOption {
|
|||
value: string;
|
||||
}
|
||||
|
||||
export interface ComboboxProps {
|
||||
autoComplete?: string;
|
||||
className?: string;
|
||||
errorId: string | null;
|
||||
errorMessage?: string;
|
||||
id: string;
|
||||
instanceId?: string;
|
||||
label: string;
|
||||
onChange: ( filterValue: string ) => void;
|
||||
options: ComboboxControlOption[];
|
||||
required?: boolean;
|
||||
value: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the WordPress ComboboxControl which supports validation.
|
||||
*/
|
||||
|
@ -40,19 +54,7 @@ const Combobox = ( {
|
|||
errorId: incomingErrorId,
|
||||
instanceId = '0',
|
||||
autoComplete = 'off',
|
||||
}: {
|
||||
id: string;
|
||||
className: string;
|
||||
label: string;
|
||||
onChange: ( filterValue: string ) => void;
|
||||
options: ComboboxControlOption[];
|
||||
value: string;
|
||||
required: boolean;
|
||||
errorMessage: string;
|
||||
errorId: string;
|
||||
instanceId: string;
|
||||
autoComplete: string;
|
||||
} ): JSX.Element => {
|
||||
}: ComboboxProps ): JSX.Element => {
|
||||
const {
|
||||
getValidationError,
|
||||
setValidationErrors,
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
export interface CountryInputProps {
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ComboboxProps } from '../combobox';
|
||||
import { countries } from './stories/countries-filler';
|
||||
|
||||
export interface CountryInputProps extends Omit< ComboboxProps, 'options' > {
|
||||
/**
|
||||
* Classes to assign to the wrapper component of the input
|
||||
*/
|
||||
className?: string;
|
||||
label: string;
|
||||
id: string;
|
||||
/**
|
||||
* Whether input elements can by default have their values automatically completed by the browser.
|
||||
*
|
||||
* This value gets assigned to both the wrapper `Combobox` and the wrapped input element.
|
||||
*/
|
||||
autoComplete?: string;
|
||||
value: string;
|
||||
onChange: ( value: string ) => void;
|
||||
required?: boolean;
|
||||
errorMessage?: string;
|
||||
errorId: null | 'shipping-missing-country';
|
||||
}
|
||||
|
||||
export type CountryInputWithCountriesProps = CountryInputProps & {
|
||||
countries: Record< string, string >;
|
||||
};
|
||||
export interface CountryInputWithCountriesProps extends CountryInputProps {
|
||||
/**
|
||||
* List of countries to allow in the selection
|
||||
*
|
||||
* Object shape should be: `{ [Alpha-2 Country Code]: 'Full country name' }`
|
||||
*/
|
||||
countries: Partial< typeof countries >;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import Combobox from '../combobox';
|
|||
import './style.scss';
|
||||
import type { CountryInputWithCountriesProps } from './CountryInputProps';
|
||||
|
||||
const CountryInput = ( {
|
||||
export const CountryInput = ( {
|
||||
className,
|
||||
countries,
|
||||
id,
|
||||
|
@ -30,10 +30,12 @@ const CountryInput = ( {
|
|||
}: CountryInputWithCountriesProps ): JSX.Element => {
|
||||
const options = useMemo(
|
||||
() =>
|
||||
Object.keys( countries ).map( ( key ) => ( {
|
||||
value: key,
|
||||
label: decodeEntities( countries[ key ] ),
|
||||
} ) ),
|
||||
Object.entries( countries ).map(
|
||||
( [ countryCode, countryName ] ) => ( {
|
||||
value: countryCode,
|
||||
label: decodeEntities( countryName ),
|
||||
} )
|
||||
),
|
||||
[ countries ]
|
||||
);
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
export { default as CountryInput } from './country-input';
|
||||
export type {
|
||||
CountryInputProps,
|
||||
CountryInputWithCountriesProps,
|
||||
} from './CountryInputProps';
|
||||
export { CountryInput } from './country-input';
|
||||
export { default as BillingCountryInput } from './billing-country-input';
|
||||
export { default as ShippingCountryInput } from './shipping-country-input';
|
||||
|
|
|
@ -248,4 +248,4 @@ export const countries = {
|
|||
YE: 'Yemen',
|
||||
ZM: 'Zambia',
|
||||
ZW: 'Zimbabwe',
|
||||
};
|
||||
} as const;
|
|
@ -1,53 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { text } from '@storybook/addon-knobs';
|
||||
import { useState, useEffect } from '@wordpress/element';
|
||||
import {
|
||||
ValidationContextProvider,
|
||||
useValidationContext,
|
||||
} from '@woocommerce/base-context';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { CountryInput } from '../';
|
||||
import { countries as exampleCountries } from './countries-filler';
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Blocks/@base-components/CountryInput',
|
||||
component: CountryInput,
|
||||
};
|
||||
|
||||
const StoryComponent = ( { label, errorMessage } ) => {
|
||||
const [ selectedCountry, selectCountry ] = useState();
|
||||
const {
|
||||
setValidationErrors,
|
||||
clearValidationError,
|
||||
} = useValidationContext();
|
||||
useEffect( () => {
|
||||
setValidationErrors( { country: errorMessage } );
|
||||
}, [ errorMessage, setValidationErrors ] );
|
||||
const updateCountry = ( country ) => {
|
||||
clearValidationError( 'country' );
|
||||
selectCountry( country );
|
||||
};
|
||||
return (
|
||||
<CountryInput
|
||||
countries={ exampleCountries }
|
||||
label={ label }
|
||||
value={ selectedCountry }
|
||||
onChange={ updateCountry }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
const label = text( 'Input Label', 'Countries:' );
|
||||
const errorMessage = text( 'Error Message', '' );
|
||||
return (
|
||||
<ValidationContextProvider>
|
||||
<StoryComponent label={ label } errorMessage={ errorMessage } />
|
||||
</ValidationContextProvider>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Story, Meta } from '@storybook/react';
|
||||
import {
|
||||
useValidationContext,
|
||||
ValidationContextProvider,
|
||||
} from '@woocommerce/base-context';
|
||||
import { useState, useEffect } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { CountryInput, CountryInputWithCountriesProps } from '..';
|
||||
import { countries } from './countries-filler';
|
||||
|
||||
type CountryCode = keyof typeof countries;
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Blocks/@base-components/CountryInput',
|
||||
component: CountryInput,
|
||||
args: {
|
||||
countries,
|
||||
autoComplete: 'off',
|
||||
id: 'country',
|
||||
label: 'Countries: ',
|
||||
required: false,
|
||||
},
|
||||
argTypes: {
|
||||
countries: { control: false },
|
||||
options: { table: { disable: true } },
|
||||
value: { control: false },
|
||||
},
|
||||
decorators: [
|
||||
( StoryComponent ) => (
|
||||
<ValidationContextProvider>
|
||||
<StoryComponent />
|
||||
</ValidationContextProvider>
|
||||
),
|
||||
],
|
||||
} as Meta< CountryInputWithCountriesProps >;
|
||||
|
||||
const Template: Story< CountryInputWithCountriesProps > = ( args ) => {
|
||||
const [ selectedCountry, selectCountry ] = useState< CountryCode | '' >(
|
||||
''
|
||||
);
|
||||
const {
|
||||
clearValidationError,
|
||||
showValidationError,
|
||||
} = useValidationContext();
|
||||
|
||||
useEffect( () => {
|
||||
showValidationError( 'country' );
|
||||
}, [ showValidationError ] );
|
||||
|
||||
function updateCountry( country: CountryCode ) {
|
||||
clearValidationError( 'country' );
|
||||
selectCountry( country );
|
||||
}
|
||||
|
||||
return (
|
||||
<CountryInput
|
||||
{ ...args }
|
||||
onChange={ ( value ) => updateCountry( value as CountryCode ) }
|
||||
value={ selectedCountry }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = Template.bind( {} );
|
||||
|
||||
export const WithError = Template.bind( {} );
|
||||
WithError.args = {
|
||||
errorId: 'country',
|
||||
errorMessage: 'Please select a country',
|
||||
required: true,
|
||||
};
|
|
@ -7,6 +7,7 @@
|
|||
"module": "esnext",
|
||||
"emitDeclarationOnly": true,
|
||||
"esModuleInterop": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "preserve",
|
||||
"target": "esnext",
|
||||
|
|
Loading…
Reference in New Issue