Add Slot/Fill to discounts area in cart sidebar (https://github.com/woocommerce/woocommerce-blocks/pull/4248)

* Move text-input to checkout package

* Pass validation props directly to ValidatedTextInput

* Import label relatively instead of from package

* Pass validation functions to ValidatedTextInput

This is so it doesn't need to get them from useValidationContext.

* Add InputProps to ValidatedTextInput

This will be used to control additional props on the input element of TextInput

* Spread inputProps onto <input> element of TextInput

* Export TextInput from @woocommerce/blocks-checkout

* Add @woocommerce/blocks-checkout package to tsconfig

* Allow styling to be applied to number inputs and when value is 0

* Make style order consistent

* Remove inputProps to rely on rest in TextInput

* Add specific prop for the inputErrorComponent

* Only disallow active state if value is 0 AND type is number

* Change all uses of ValidatedTextInput to also pass inputErrorComponent

* Revert "Change all uses of ValidatedTextInput to also pass inputErrorComponent"

This reverts commit ec734b99c20c4d29fcf778714246fc406ee37eaf.

* Revert "Remove inputProps to rely on rest in TextInput"

This reverts commit 1fc64cca4002206423d1fa443ff2d60130ba1ea0.

* Revert "Revert "Change all uses of ValidatedTextInput to also pass inputErrorComponent""

This reverts commit 110e3606a996668be5a32698b634b7706d16cddc.

* Revert "Revert "Remove inputProps to rely on rest in TextInput""

This reverts commit aeb03526c44b3fcc97a719a18930d08157a80baf.

* Don't pass errorMessage to ValidatedTextInput

* Add DiscountsMetaSlot

* Add ExperimentalDiscountsMeta.Slot to Cart sidebar

* Add extra styles for Button and Panel components

* Export ExperimentalDiscountsMeta from checkout package

* Add updateCartFromApi util to @woocommerce/blocks-checkout

* Add comment to updateCartFromApi

* Change updateCartFromApi to TypeScript

* Revert "Move `TextInput` to checkout package and allow it to be used for input type=number (https://github.com/woocommerce/woocommerce-blocks/pull/4238)"

This reverts commit ee9b2d20e0.

* Stop passing contexts through the discounts slot fill

* Allow ValidatedTextInput to be used for type=number

* Remove contexts from Discounts slot fill

* Update snapshots

* Stop `errorMessage` being spread onto input fields in checkout

* Add paths to tsconfig

* Remove contexts from Discounts slot

* Accept step min and max on ValidatedTextInput

* Remove "no-margin" option on buttons

* Remove spinners from input type number

* Remove `no-top-border` style from panel

* Prevent text in buttons from breaking in the middle of words

* Add checkout package to tsconfig file list

* Stop passing components through DiscountsMetaSlot
This commit is contained in:
Thomas Roberts 2021-06-04 09:44:26 +01:00 committed by GitHub
parent 749406ecb9
commit ffa77f26f7
12 changed files with 136 additions and 5 deletions

View File

@ -40,6 +40,7 @@
input[type="tel"],
input[type="url"],
input[type="text"],
input[type="number"],
input[type="email"] {
@include font-size(regular);
background-color: #fff;
@ -72,9 +73,20 @@
}
}
input[type="number"] {
-moz-appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
appearance: none;
margin: 0;
}
}
&.is-active input[type="tel"],
&.is-active input[type="url"],
&.is-active input[type="text"],
&.is-active input[type="number"],
&.is-active input[type="email"] {
padding: em($gap-large) 0 em($gap-smallest) $gap;
}

View File

@ -11,6 +11,13 @@ import { Label } from '@woocommerce/blocks-checkout';
*/
import './style.scss';
interface TextInputPropsWithNumberType {
type: 'number';
step?: number;
min?: number;
max?: number;
}
interface TextInputProps
extends Omit<
InputHTMLAttributes< HTMLInputElement >,
@ -28,7 +35,10 @@ interface TextInputProps
onBlur?: ( newValue: string ) => void;
}
const TextInput = forwardRef< HTMLInputElement, TextInputProps >(
const TextInput = forwardRef<
HTMLInputElement,
TextInputProps & ( Record< string, never > | TextInputPropsWithNumberType )
>(
(
{
className,
@ -44,6 +54,9 @@ const TextInput = forwardRef< HTMLInputElement, TextInputProps >(
autoComplete = 'off',
value = '',
onChange,
min,
max,
step,
required = false,
onBlur = () => {
/* Do nothing */
@ -54,6 +67,28 @@ const TextInput = forwardRef< HTMLInputElement, TextInputProps >(
) => {
const [ isActive, setIsActive ] = useState( false );
const numberAttributesFromProps: {
[ prop: string ]: string | number | undefined;
} =
type === 'number'
? {
step,
min,
max,
}
: {};
const numberProps: {
[ prop: string ]: string | number | undefined;
} = {};
Object.keys( numberAttributesFromProps ).forEach( ( key ) => {
if ( typeof numberAttributesFromProps[ key ] === 'undefined' ) {
return;
}
numberProps[ key ] = numberAttributesFromProps[ key ];
} );
return (
<div
className={ classnames(
@ -87,6 +122,7 @@ const TextInput = forwardRef< HTMLInputElement, TextInputProps >(
: ariaDescribedBy
}
required={ required }
{ ...numberProps }
/>
<Label
label={ label }

View File

@ -9,6 +9,7 @@ import {
useValidationContext,
} from '@woocommerce/base-context';
import { withInstanceId } from '@woocommerce/base-hocs/with-instance-id';
import { isString } from '@woocommerce/types';
/**
* Internal dependencies
@ -36,6 +37,7 @@ type ValidatedTextInputProps = (
validateOnMount?: boolean;
focusOnMount?: boolean;
showError?: boolean;
errorMessage?: string;
onChange: ( newValue: string ) => void;
};
@ -49,6 +51,7 @@ const ValidatedTextInput = ( {
focusOnMount = false,
onChange,
showError = true,
errorMessage: passedErrorMessage = '',
...rest
}: ValidatedTextInputProps ) => {
const [ isPristine, setIsPristine ] = useState( true );
@ -123,6 +126,9 @@ const ValidatedTextInput = ( {
message?: string;
hidden?: boolean;
};
if ( isString( passedErrorMessage ) && passedErrorMessage !== '' ) {
errorMessage.message = passedErrorMessage;
}
const hasError = errorMessage.message && ! errorMessage.hidden;
const describedBy =
showError && hasError && getValidationErrorId( errorIdString )

View File

@ -1,6 +1,6 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {},
"include": [ ".", "../../../../packages/prices", "../context/hooks", "../../type-defs" ],
"include": [ ".", "../../../../packages/prices", "../../../../packages/checkout", "../context", "../../type-defs", "../hocs" ],
"exclude": [ "**/test/**" ]
}

View File

@ -13,8 +13,8 @@ import {
TotalsFees,
TotalsTaxes,
ExperimentalOrderMeta,
ExperimentalDiscountsMeta,
} from '@woocommerce/blocks-checkout';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import {
useStoreCartCoupons,
@ -40,7 +40,6 @@ import CheckoutButton from '../checkout-button';
import CartLineItemsTitle from './cart-line-items-title';
import CartLineItemsTable from './cart-line-items-table';
import { CartExpressPayment } from '../../payment-methods';
import './style.scss';
interface CartAttributes {
@ -114,6 +113,11 @@ const Cart = ( { attributes }: CartProps ) => {
cart,
};
const discountsSlotFillProps = {
extensions,
cart,
};
return (
<>
<CartLineItemsTitle itemCount={ cartItemsCount } />
@ -152,6 +156,10 @@ const Cart = ( { attributes }: CartProps ) => {
isLoading={ isApplyingCoupon }
/>
) }
<ExperimentalDiscountsMeta.Slot
{ ...discountsSlotFillProps }
/>
{ cartNeedsShipping && (
<TotalsShipping
showCalculator={ isShippingCalculatorEnabled }

View File

@ -455,6 +455,9 @@ exports[`Testing cart Contains a Taxes section if Core options are set to show i
</div>
</div>
</div>
<div
class="wc-block-components-discounts-meta"
/>
<div
class="wc-block-components-totals-shipping"
>
@ -1146,6 +1149,9 @@ exports[`Testing cart Shows individual tax lines if the store is set to do so 1`
</div>
</div>
</div>
<div
class="wc-block-components-discounts-meta"
/>
<div
class="wc-block-components-totals-shipping"
>
@ -1842,6 +1848,9 @@ exports[`Testing cart Shows rate percentages after tax lines if the block is set
</div>
</div>
</div>
<div
class="wc-block-components-discounts-meta"
/>
<div
class="wc-block-components-totals-shipping"
>

View File

@ -0,0 +1,32 @@
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* Internal dependencies
*/
import { createSlotFill } from '../slot';
const slotName = '__experimentalDiscountsMeta';
const {
Fill: ExperimentalDiscountsMeta,
Slot: DiscountsMetaSlot,
} = createSlotFill( slotName );
const Slot = ( { className, extensions, cart } ) => {
return (
<DiscountsMetaSlot
className={ classnames(
className,
'wc-block-components-discounts-meta'
) }
fillProps={ { extensions, cart } }
/>
);
};
ExperimentalDiscountsMeta.Slot = Slot;
export default ExperimentalDiscountsMeta;

View File

@ -3,6 +3,7 @@ export * from './utils';
export * from './slot';
export * from './registry';
export { default as ExperimentalOrderMeta } from './order-meta';
export { default as ExperimentalDiscountsMeta } from './discounts-meta';
export { default as ExperimentalOrderShippingPackages } from './order-shipping-packages';
export { default as Panel } from './panel';
export { SlotFillProvider } from 'wordpress-components';

View File

@ -18,6 +18,7 @@
position: relative;
text-align: left;
width: 100%;
word-break: break-word;
&,
&:hover,

View File

@ -1 +1,2 @@
export * from './validation';
export { updateCartFromApi } from './update-cart-from-api';

View File

@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { CART_STORE_KEY } from '@woocommerce/block-data';
import { select, dispatch } from '@wordpress/data';
import type { CartResponse } from '@woocommerce/type-defs/cart-response';
/**
* When executed, this will invalidate the getCartData selector, causing a request to be made
* to the API. This is in place to allow extensions to signal that they have modified the cart,
* and that it needs to be reloaded in the client.
*/
export const updateCartFromApi = (): void => {
const { getCartData } = select( CART_STORE_KEY );
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - Can't figure out why invalidateResolutionForStoreSelector isn't available
// but it's a standard action dispatched by @wordpress/data.
const { invalidateResolutionForStoreSelector, receiveCart } = dispatch(
CART_STORE_KEY
);
invalidateResolutionForStoreSelector( 'getCartData' );
const cartData = ( getCartData() as unknown ) as CartResponse;
receiveCart( cartData );
};

View File

@ -10,7 +10,8 @@
"../assets/js/base/hooks",
"../settings/shared/index.ts",
"../settings/blocks/index.ts",
"../type-defs"
"../type-defs",
"../assets/js/data"
],
"exclude": [ "**/test/**" ]
}