diff --git a/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts b/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts index 3d71e360f2..d8e5c97f12 100644 --- a/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts +++ b/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts @@ -485,6 +485,56 @@ describe('handleCreateStencil', () => { ); }); + it('should handle modifiers with fallthrough keys', () => { + const program = createProgramFromSource(` + import {createStencil, px2rem} from '@workday/canvas-kit-styling'; + + const buttonStencil = createStencil({ + vars: { + padding: '' + }, + base: {}, + modifiers: { + padding: { + large: { + padding: 40, + }, + small: { + padding: 20, + } + _: ({ padding }) => ({ + padding + }) + } + } + }) + `); + + const styles = {}; + const names = {}; + const extractedNames = {}; + + const result = transform( + program, + 'test.ts', + withDefaultContext(program.getTypeChecker(), {styles, names, extractedNames}) + ); + + expect(result).toMatch(/base: { name: "[0-9a-z]+", styles: "box-sizing:border-box;" }/); + expect(result).toMatch(/large: { name: "[0-9a-z]+", styles: "padding:40px;" }/); + expect(result).toMatch( + /_: { name: "[0-9a-z]+", styles: "padding:var\(--padding-button-[a-z0-9]+\);" }/ + ); + + expect(styles['test.css']).toContainEqual(compileCSS('.css-button{box-sizing:border-box;}')); + expect(styles['test.css']).toContainEqual( + compileCSS('.css-button.padding-large{padding: 40px;}') + ); + expect(styles['test.css']).toContainEqual( + compileCSS('.css-button.padding{padding: var(--css-button-padding)}') + ); + }); + it('should add to extracted styles', () => { const program = createProgramFromSource(` import {createStencil} from '@workday/canvas-kit-styling'; diff --git a/modules/styling/lib/cs.ts b/modules/styling/lib/cs.ts index d5347b8c4e..b2786f23c5 100644 --- a/modules/styling/lib/cs.ts +++ b/modules/styling/lib/cs.ts @@ -415,8 +415,10 @@ export function createModifiers(input: M): ModifierRet const modifierFn = (modifiers: Partial>) => { return combineClassNames( Object.keys(input) - .filter(key => (input as any)[key][modifiers[key]]) - .map(key => (input as any)[key][modifiers[key]]) + .map( + key => (input as any)[key][modifiers[key]] || (modifiers[key] && (input as any)[key]._) + ) + .filter(input => input) // only return defined class names ); }; diff --git a/modules/styling/spec/cs.spec.tsx b/modules/styling/spec/cs.spec.tsx index c4b139863d..f893def599 100644 --- a/modules/styling/spec/cs.spec.tsx +++ b/modules/styling/spec/cs.spec.tsx @@ -348,6 +348,29 @@ describe('cs', () => { const myModifiers = myModifiersFactory(); expectTypeOf(myModifiers.size).toMatchTypeOf<{large: CS; small: CS}>(); }); + + describe('with a "_" modifier key', () => { + const myModifiersFactory = () => + createModifiers({ + size: { + large: createStyles({fontSize: '1.5rem'}), + small: createStyles({fontSize: '0.8rem'}), + _: createStyles({fontSize: 'var(--size)'}), + }, + }); + + it('should not return any classes when "size" is not provided', () => { + const myModifiers = myModifiersFactory(); + + expect(myModifiers({})).toEqual(''); + }); + + it('should return the CSS class of the "_" key when no other modifier matches and a matching modifier key is provided', () => { + const myModifiers = myModifiersFactory(); + + expect(myModifiers({size: 'foo' as any})).toEqual(myModifiers.size._); + }); + }); }); describe('createCompoundModifiers', () => {