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

Add Slot/Fill to discounts area in cart sidebar #4248

Merged
merged 41 commits into from Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
0ed79c1
Move text-input to checkout package
opr May 18, 2021
453f184
Pass validation props directly to ValidatedTextInput
opr May 19, 2021
b106988
Import label relatively instead of from package
opr May 19, 2021
a6e8aa6
Pass validation functions to ValidatedTextInput
opr May 19, 2021
6e9e530
Add InputProps to ValidatedTextInput
opr May 19, 2021
cac86bc
Spread inputProps onto <input> element of TextInput
opr May 19, 2021
9cd54b2
Export TextInput from @woocommerce/blocks-checkout
opr May 19, 2021
85d9b65
Add @woocommerce/blocks-checkout package to tsconfig
opr May 19, 2021
47f0e04
Allow styling to be applied to number inputs and when value is 0
opr May 19, 2021
6683a01
Make style order consistent
opr May 19, 2021
48b86ec
Remove inputProps to rely on rest in TextInput
opr May 20, 2021
2312d73
Add specific prop for the inputErrorComponent
opr May 20, 2021
113fad0
Only disallow active state if value is 0 AND type is number
opr May 20, 2021
703bf96
Change all uses of ValidatedTextInput to also pass inputErrorComponent
opr May 20, 2021
9eef517
Revert "Change all uses of ValidatedTextInput to also pass inputError…
opr May 20, 2021
65b9388
Revert "Remove inputProps to rely on rest in TextInput"
opr May 20, 2021
0efd1f7
Revert "Revert "Change all uses of ValidatedTextInput to also pass in…
opr May 20, 2021
531fb2c
Revert "Revert "Remove inputProps to rely on rest in TextInput""
opr May 20, 2021
4b8cd1d
Don't pass errorMessage to ValidatedTextInput
opr May 20, 2021
7dab2a3
Add DiscountsMetaSlot
opr May 20, 2021
e799271
Add ExperimentalDiscountsMeta.Slot to Cart sidebar
opr May 20, 2021
a7fe16f
Add extra styles for Button and Panel components
opr May 20, 2021
78f0ca1
Export ExperimentalDiscountsMeta from checkout package
opr May 20, 2021
25d7c5f
Add updateCartFromApi util to @woocommerce/blocks-checkout
opr May 20, 2021
89b97ac
Add comment to updateCartFromApi
opr May 20, 2021
37af2a9
Change updateCartFromApi to TypeScript
opr May 20, 2021
a6b0fae
Revert "Move `TextInput` to checkout package and allow it to be used …
opr May 20, 2021
77461bf
Stop passing contexts through the discounts slot fill
opr May 31, 2021
1b4b68a
Allow ValidatedTextInput to be used for type=number
opr May 31, 2021
3e0e6ac
Remove contexts from Discounts slot fill
opr Jun 2, 2021
7d81976
Update snapshots
opr Jun 2, 2021
dface02
Stop `errorMessage` being spread onto input fields in checkout
opr Jun 2, 2021
d7bbe0f
Add paths to tsconfig
opr Jun 2, 2021
edf3cae
Remove contexts from Discounts slot
opr Jun 3, 2021
f3f4420
Accept step min and max on ValidatedTextInput
opr Jun 3, 2021
ef153b5
Remove "no-margin" option on buttons
opr Jun 3, 2021
529a36e
Remove spinners from input type number
opr Jun 3, 2021
6ebf3f1
Remove `no-top-border` style from panel
opr Jun 3, 2021
cb2967f
Prevent text in buttons from breaking in the middle of words
opr Jun 3, 2021
c04536a
Add checkout package to tsconfig file list
opr Jun 3, 2021
ef51566
Stop passing components through DiscountsMetaSlot
opr Jun 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions assets/js/base/components/text-input/style.scss
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
38 changes: 37 additions & 1 deletion assets/js/base/components/text-input/text-input.tsx
Expand Up @@ -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 >,
Expand All @@ -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,
Expand All @@ -44,6 +54,9 @@ const TextInput = forwardRef< HTMLInputElement, TextInputProps >(
autoComplete = 'off',
value = '',
onChange,
min,
max,
step,
required = false,
onBlur = () => {
/* Do nothing */
Expand All @@ -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(
Expand Down Expand Up @@ -87,6 +122,7 @@ const TextInput = forwardRef< HTMLInputElement, TextInputProps >(
: ariaDescribedBy
}
required={ required }
{ ...numberProps }
/>
<Label
label={ label }
Expand Down
6 changes: 6 additions & 0 deletions assets/js/base/components/text-input/validated-text-input.tsx
Expand Up @@ -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
Expand Down Expand Up @@ -36,6 +37,7 @@ type ValidatedTextInputProps = (
validateOnMount?: boolean;
focusOnMount?: boolean;
showError?: boolean;
errorMessage?: string;
onChange: ( newValue: string ) => void;
};

Expand All @@ -49,6 +51,7 @@ const ValidatedTextInput = ( {
focusOnMount = false,
onChange,
showError = true,
errorMessage: passedErrorMessage = '',
...rest
}: ValidatedTextInputProps ) => {
const [ isPristine, setIsPristine ] = useState( true );
Expand Down Expand Up @@ -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 )
Expand Down
2 changes: 1 addition & 1 deletion assets/js/base/components/tsconfig.json
@@ -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/**" ]
}
12 changes: 10 additions & 2 deletions assets/js/blocks/cart-checkout/cart/full-cart/index.tsx
Expand Up @@ -13,8 +13,8 @@ import {
TotalsFees,
TotalsTaxes,
ExperimentalOrderMeta,
ExperimentalDiscountsMeta,
} from '@woocommerce/blocks-checkout';

import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import {
useStoreCartCoupons,
Expand All @@ -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 {
Expand Down Expand Up @@ -114,6 +113,11 @@ const Cart = ( { attributes }: CartProps ) => {
cart,
};

const discountsSlotFillProps = {
extensions,
cart,
};

return (
<>
<CartLineItemsTitle itemCount={ cartItemsCount } />
Expand Down Expand Up @@ -152,6 +156,10 @@ const Cart = ( { attributes }: CartProps ) => {
isLoading={ isApplyingCoupon }
/>
) }
<ExperimentalDiscountsMeta.Slot
{ ...discountsSlotFillProps }
/>

{ cartNeedsShipping && (
<TotalsShipping
showCalculator={ isShippingCalculatorEnabled }
Expand Down
Expand Up @@ -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"
>
Expand Down Expand Up @@ -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"
>
Expand Down Expand Up @@ -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"
>
Expand Down
32 changes: 32 additions & 0 deletions packages/checkout/discounts-meta/index.js
@@ -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;
1 change: 1 addition & 0 deletions packages/checkout/index.js
Expand Up @@ -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';
Expand Down
1 change: 1 addition & 0 deletions packages/checkout/panel/style.scss
Expand Up @@ -18,6 +18,7 @@
position: relative;
text-align: left;
width: 100%;
word-break: break-word;

&,
&:hover,
Expand Down
1 change: 1 addition & 0 deletions packages/checkout/utils/index.js
@@ -1 +1,2 @@
export * from './validation';
export { updateCartFromApi } from './update-cart-from-api';
24 changes: 24 additions & 0 deletions packages/checkout/utils/update-cart-from-api.ts
@@ -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 );
};
3 changes: 2 additions & 1 deletion packages/tsconfig.json
Expand Up @@ -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/**" ]
}