Skip to content

Commit

Permalink
Fix the cx() function's class collection at runtime as it generated…
Browse files Browse the repository at this point in the history
… class instances rather than strings. (#1697)

* Fix the `cx()` function's class collection at runtime as it generated class instances rather than strings

Migrate from `ac()` which generates `AtomicGroups` classes to `ax()` which strictly collects strings.

This is because passing `<Component xcss={cx({ … })}>` around resulted in errors, `'cls.split is not a function'` trying to iterate over non-strings.

* Add a test for pass-through props to properly utilize the cx function
  • Loading branch information
kylorhall-atlassian authored Jul 21, 2024
1 parent 18024be commit 25a4bed
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/friendly-eels-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@compiled/react': patch
---

Fix the `cx()` function's class collection at runtime as it generated class instances rather than strings
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ interface CSSPropertiesSchema {
color: 'var(--ds-text-hover)';
background: 'var(--ds-surface-hover)' | 'var(--ds-surface-sunken-hover)';
};
color: 'var(--ds-text)' | 'var(--ds-text-bold)';
background: 'var(--ds-surface)' | 'var(--ds-surface-sunken)';
color: 'var(--ds-text)' | 'var(--ds-text-bold)' | 'var(--ds-text-error)';
background: 'var(--ds-surface)' | 'var(--ds-surface-sunken)' | 'var(--ds-surface-overlay)';
bkgrnd: 'red' | 'green';
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/** @jsxImportSource @compiled/react */
import { render } from '@testing-library/react';

import { cssMap, type XCSSProp, cx } from './__fixtures__/strict-api';

const styles = cssMap({
rootNative: {
color: 'var(--ds-text)',
background: 'var(--ds-surface)',
},
rootComponent: {
color: 'var(--ds-text-error)',
background: 'var(--ds-surface-overlay)',
},
bold: {
color: 'var(--ds-text-bold)',
},
sunken: {
background: 'var(--ds-surface-sunken)',
},
});

function ComponentPassThrough({
xcss,
}: {
xcss?: ReturnType<typeof XCSSProp<'background' | 'color', '&:hover'>>;
}) {
return <NativePassThrough xcss={cx(styles.rootComponent, xcss)} />;
}

function NativePassThrough({
xcss,
}: {
xcss?: ReturnType<typeof XCSSProp<'background' | 'color', '&:hover'>>;
}) {
return <button data-testid="button" className={xcss} css={styles.rootNative} />;
}

describe('pass-through props.xcss directly to DOM', () => {
it('works with no props.xcss', () => {
const { getByTestId } = render(<NativePassThrough />);

expect(getByTestId('button')).toHaveCompiledCss({
color: 'var(--ds-text)',
background: 'var(--ds-surface)',
});
});

it('works with pass-through props.xcss', () => {
const { getByTestId } = render(<NativePassThrough xcss={styles.bold} />);

expect(getByTestId('button')).toHaveCompiledCss({
color: 'var(--ds-text-bold)',
background: 'var(--ds-surface)', // rootNative styles
});
});

it('works with pass-through multiple props.xcss via cx', () => {
const { getByTestId } = render(<NativePassThrough xcss={cx(styles.bold, styles.sunken)} />);

expect(getByTestId('button')).toHaveCompiledCss({
color: 'var(--ds-text-bold)',
background: 'var(--ds-surface-sunken)',
});
});
});

describe('pass-through props.xcss via another component', () => {
it('works with no props.xcss', () => {
const { getByTestId } = render(<ComponentPassThrough />);

expect(getByTestId('button')).toHaveCompiledCss({
color: 'var(--ds-text-error)',
background: 'var(--ds-surface-overlay)',
});
});

it('works with pass-through props.xcss', () => {
const { getByTestId } = render(<ComponentPassThrough xcss={styles.bold} />);

expect(getByTestId('button')).toHaveCompiledCss({
color: 'var(--ds-text-bold)',
background: 'var(--ds-surface-overlay)', // rootComponent styles
});
});

it('works with pass-through multiple props.xcss via cx', () => {
const { getByTestId } = render(<ComponentPassThrough xcss={cx(styles.bold, styles.sunken)} />);

expect(getByTestId('button')).toHaveCompiledCss({
color: 'var(--ds-text-bold)',
background: 'var(--ds-surface-sunken)',
});
});
});
4 changes: 2 additions & 2 deletions packages/react/src/xcss-prop/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type * as CSS from 'csstype';

import type { ApplySchemaValue } from '../create-strict-api/types';
import { ac } from '../runtime';
import { ax } from '../runtime';
import type { CSSPseudos, CSSPseudoClasses, CSSProperties, StrictCSSProperties } from '../types';

type MarkAsRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
Expand Down Expand Up @@ -208,5 +208,5 @@ export const cx = <TStyles extends [...XCSSProp<any, any>[]]>(

// The output should be a union type of passed in styles. This ensures the call
// site of xcss prop can raise violations when disallowed styles have been passed.
return ac(actualStyles) as TStyles[number];
return ax(actualStyles) as TStyles[number];
};

0 comments on commit 25a4bed

Please sign in to comment.