Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Floating Labels with Stripe Appearance API #9498

Merged
merged 9 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions changelog/add-stripe-floating-labels
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Using Floating Labels with Stripe Appearance API for Blocks Checkout
1 change: 1 addition & 0 deletions client/checkout/api/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const mockAppearance = {
fontFamily: undefined,
fontSizeBase: undefined,
},
labels: 'above',
};

describe( 'WCPayAPI', () => {
Expand Down
2 changes: 1 addition & 1 deletion client/checkout/blocks/payment-elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const PaymentElements = ( { api, ...props } ) => {
useEffect( () => {
async function generateUPEAppearance() {
// Generate UPE input styles.
let upeAppearance = getAppearance( 'blocks_checkout' );
let upeAppearance = getAppearance( 'blocks_checkout', false, true );
upeAppearance = await api.saveUPEAppearance(
upeAppearance,
'blocks_checkout'
Expand Down
66 changes: 45 additions & 21 deletions client/checkout/upe-styles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import {
dashedToCamelCase,
isColorLight,
getBackgroundColor,
handleAppearanceForFloatingLabel,
} from './utils.js';

export const appearanceSelectors = {
default: {
hiddenContainer: '#wcpay-hidden-div',
hiddenInput: '#wcpay-hidden-input',
hiddenInvalidInput: '#wcpay-hidden-invalid-input',
hiddenValidActiveLabel: '#wcpay-hidden-valid-active-label',
},
classicCheckout: {
appendTarget: '.woocommerce-billing-fields__field-wrapper',
Expand All @@ -40,16 +42,15 @@ export const appearanceSelectors = {
linkSelectors: [ 'a' ],
},
blocksCheckout: {
appendTarget: '#billing.wc-block-components-address-form',
upeThemeInputSelector: '#billing-first_name',
upeThemeLabelSelector:
'.wc-block-components-checkout-step__description',
appendTarget: '#contact-fields',
upeThemeInputSelector: '.wc-block-components-text-input #email',
upeThemeLabelSelector: '.wc-block-components-text-input label',
rowElement: 'div',
validClasses: [ 'wc-block-components-text-input' ],
validClasses: [ 'wc-block-components-text-input', 'is-active' ],
invalidClasses: [ 'wc-block-components-text-input', 'has-error' ],
alternateSelectors: {
appendTarget: '#shipping.wc-block-components-address-form',
upeThemeInputSelector: '#shipping-first_name',
appendTarget: '#billing.wc-block-components-address-form',
upeThemeInputSelector: '#billing-first_name',
upeThemeLabelSelector:
'.wc-block-components-checkout-step__description',
},
Expand Down Expand Up @@ -326,6 +327,13 @@ const hiddenElementsForUPE = {
selectors.hiddenInput
);

// Clone & append target label to hidden valid row.
this.appendClone(
hiddenValidRow,
selectors.upeThemeLabelSelector,
selectors.hiddenValidActiveLabel
);

// Clone & append target input to hidden invalid row.
this.appendClone(
hiddenInvalidRow,
Expand Down Expand Up @@ -489,24 +497,40 @@ export const getAppearance = ( elementsLocation, forWooPay = false ) => {
fontSizeBase: labelRules.fontSize,
};

const appearance = {
const isFloatingLabel = elementsLocation === 'blocks_checkout';

let appearance = {
variables: globalRules,
theme: isColorLight( backgroundColor ) ? 'stripe' : 'night',
rules: {
'.Input': inputRules,
'.Input--invalid': inputInvalidRules,
'.Label': labelRules,
'.Block': blockRules,
'.Tab': tabRules,
'.Tab:hover': tabHoverRules,
'.Tab--selected': selectedTabRules,
'.TabIcon:hover': tabIconHoverRules,
'.TabIcon--selected': selectedTabIconRules,
'.Text': labelRules,
'.Text--redirect': labelRules,
},
labels: isFloatingLabel ? 'floating' : 'above',
gpressutto5 marked this conversation as resolved.
Show resolved Hide resolved
// We need to clone the object to avoid modifying other rules when updating the appearance for floating labels.
rules: JSON.parse(
JSON.stringify( {
'.Input': inputRules,
'.Input--invalid': inputInvalidRules,
'.Label': labelRules,
'.Block': blockRules,
'.Tab': tabRules,
'.Tab:hover': tabHoverRules,
'.Tab--selected': selectedTabRules,
'.TabIcon:hover': tabIconHoverRules,
'.TabIcon--selected': selectedTabIconRules,
'.Text': labelRules,
'.Text--redirect': labelRules,
} )
),
};

if ( isFloatingLabel ) {
appearance = handleAppearanceForFloatingLabel(
appearance,
getFieldStyles(
selectors.hiddenValidActiveLabel,
'.Label--floating'
)
);
}

if ( forWooPay ) {
appearance.rules = {
...appearance.rules,
Expand Down
1 change: 1 addition & 0 deletions client/checkout/upe-styles/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ describe( 'Getting styles for automated theming', () => {
padding: '10px',
},
},
labels: 'above',
} );
} );

Expand Down
1 change: 1 addition & 0 deletions client/checkout/upe-styles/upe-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ const restrictedTabIconSelectedProperties = [ 'color' ];

export const upeRestrictedProperties = {
'.Label': upeSupportedProperties[ '.Label' ],
'.Label--floating': [ ...upeSupportedProperties[ '.Label' ], 'transform' ],
'.Input': [
...upeSupportedProperties[ '.Input' ],
'outlineColor',
Expand Down
76 changes: 76 additions & 0 deletions client/checkout/upe-styles/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,79 @@ export const getBackgroundColor = ( selectors ) => {
export const isColorLight = ( color ) => {
return tinycolor( color ).getBrightness() > 125;
};

/**
* Modifies the appearance object to include styles for floating label.
*
* @param {Object} appearance object to modify.
* @param {Object} floatingLabelStyles Floating label styles.
* @return {Object} Modified appearance object.
*/
export const handleAppearanceForFloatingLabel = (
appearance,
floatingLabelStyles
) => {
// Add floating label styles.
appearance.rules[ '.Label--floating' ] = floatingLabelStyles;

// Update line-height for floating label to account for scaling.
if (
appearance.rules[ '.Label--floating' ].transform &&
appearance.rules[ '.Label--floating' ].transform !== 'none'
) {
// Extract the scaling factors from the matrix
const transformMatrix =
appearance.rules[ '.Label--floating' ].transform;
const matrixValues = transformMatrix.match( /matrix\((.+)\)/ );
if ( matrixValues && matrixValues[ 1 ] ) {
const splitMatrixValues = matrixValues[ 1 ].split( ', ' );
const scaleX = parseFloat( splitMatrixValues[ 0 ] );
const scaleY = parseFloat( splitMatrixValues[ 3 ] );
const scale = ( scaleX + scaleY ) / 2;

const lineHeight = parseFloat(
appearance.rules[ '.Label--floating' ].lineHeight
);
const newLineHeight = Math.floor( lineHeight * scale );
appearance.rules[
'.Label--floating'
].lineHeight = `${ newLineHeight }px`;
appearance.rules[
'.Label--floating'
].fontSize = `${ newLineHeight }px`;
}
delete appearance.rules[ '.Label--floating' ].transform;
}

// Subtract the label's lineHeight from padding-top to account for floating label height.
// Minus 4px which is a constant value added by stripe to the padding-top.
// Minus 1px for each vertical padding to account for the unpredictable input height
// (see https://github.com/Automattic/woocommerce-payments/issues/9476#issuecomment-2374766540).
// When the result is less than 0, it will automatically use 0.
if ( appearance.rules[ '.Input' ].paddingTop ) {
appearance.rules[
'.Input'
// eslint-disable-next-line max-len
].paddingTop = `calc(${ appearance.rules[ '.Input' ].paddingTop } - ${ appearance.rules[ '.Label--floating' ].lineHeight } - 4px - 1px)`;
}
if ( appearance.rules[ '.Input' ].paddingBottom ) {
const originalPaddingBottom = parseFloat(
appearance.rules[ '.Input' ].paddingBottom
);
appearance.rules[
'.Input'
// eslint-disable-next-line max-len
].paddingBottom = `${ originalPaddingBottom - 1 }px`;

const originalLabelMarginTop =
appearance.rules[ '.Label' ].marginTop ?? '0';
appearance.rules[ '.Label' ].marginTop = `${ Math.floor(
( originalPaddingBottom - 1 ) / 3
) }px`;
appearance.rules[
'.Label--floating'
].marginTop = originalLabelMarginTop;
}

return appearance;
};
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ describe( 'WoopayExpressCheckoutButton', () => {
fontFamily: undefined,
fontSizeBase: undefined,
},
labels: 'above',
};

beforeEach( () => {
Expand Down
Loading