import fs from 'fs'; import path from 'path'; /** * Part styling only works by INHERITANCE, and inheritance loses to any * declaration on the element itself. * * A checkout part rule lands on the element the part is stamped on: * * [data-block-id="…"] [data-part="payment-option"] { color: #fff !important } * * If the text inside that element sits in a child that declares its own * `text-…` colour, size or weight, the inherited value never reaches it — * `!important` does not help, because the child is not competing for the same * declaration, it simply has one of its own. Every typography control the * panel offers for that part is then a silent no-op: the designer sets Text * Color to white, nothing moves, and there is no error to explain it. * * That is exactly how a starter template painted a near-black selected * payment card behind a near-black method name (contrast ≈ 1:1), and how * "Font Weight" on the grand total row did nothing. * * The rule this file locks: on these elements the typography lives on the * STAMPED element, and a child either carries none of its own or is a part in * its own right. Where two children genuinely differ (a totals row's label and * its amount), each gets its own part instead of the row pretending to control * both. */ const checkoutDir = path.resolve(__dirname, '..'); const read = (relative: string): string => fs.readFileSync(path.join(checkoutDir, relative), 'utf8'); /** Utilities that would out-declare an inherited part rule. */ const TYPOGRAPHY_UTILITY = /\btext-(?:xs|sm|base|lg|xl|\d?xl|black-\d+|gray-\d+|primary-\d+|secondary)\b|\bfont-(?:light|normal|medium|semibold|bold)\b/; describe('one-page payment cards keep their typography on the stamped button', () => { const source = read('variants/one-page/payment-options-grid.tsx'); it('declares the text styles on the button that carries the part', () => { const button = source.slice( source.indexOf(' { expect(source).toContain('{option.name}'); }); it('never re-colours the icon with fill utilities', () => { // `Icon` renders an icon FONT, so `fill-*` coloured nothing at all; the // glyph follows `color`, which now comes from the button. expect(source).not.toMatch(/\bfill-(?:black|gray)-\d+\b/); }); }); describe('multi-step payment options keep their typography on the stamped element', () => { const source = read('steps/payment/payment-option-buttons.tsx'); it('puts the mobile row styles on the label that carries the part', () => { const label = source.slice( source.indexOf('') ); expect(label).toContain("partAttrs('payment-option'"); expect(label).toMatch(/text-primary-800 font-light text-lg/); }); it('leaves the method name span with layout classes only', () => { expect(source).toContain('{option.name}'); }); }); describe('summary rows expose their label and amount as parts', () => { it.each([ ['variants/one-page/one-page-summary.tsx'], ['summary.tsx'] ])('%s stamps both cells of every totals row and the grand total', file => { const source = read(file); for (const part of [ 'summary-row-label', 'summary-row-value', 'summary-total-label', 'summary-total-value' ]) { expect(source).toContain(`partAttrs('${part}')`); } }); it('gives the one-page grand total row a baseline its label can inherit', () => { const source = read('variants/one-page/one-page-summary.tsx'); const total = source.slice( source.indexOf("partAttrs('summary-total')") - 400, source.indexOf("partAttrs('summary-total-value')") ); // The row owns the label's treatment; the label span declares nothing. expect(total).toMatch(/text-sm font-medium uppercase/); expect(total).toContain( "" ); }); it('keeps a stamped cell free to declare its own look', () => { // The amount is allowed to differ — it answers to summary-*-value. const source = read('variants/one-page/one-page-summary.tsx'); expect(source).toMatch(TYPOGRAPHY_UTILITY); expect(source).toContain("partAttrs('summary-total-value')"); }); });