@compiled/react 0.16.9 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/browser/create-strict-api/index.d.ts +19 -22
  2. package/dist/browser/create-strict-api/index.js +6 -4
  3. package/dist/browser/create-strict-api/index.js.map +1 -1
  4. package/dist/browser/create-strict-api/types.d.ts +28 -0
  5. package/dist/browser/create-strict-api/types.js +2 -0
  6. package/dist/browser/create-strict-api/types.js.map +1 -0
  7. package/dist/browser/css/index.d.ts +3 -1
  8. package/dist/browser/css/index.js.map +1 -1
  9. package/dist/browser/css-map/index.d.ts +15 -2
  10. package/dist/browser/css-map/index.js.map +1 -1
  11. package/dist/browser/types.d.ts +4 -2
  12. package/dist/browser/xcss-prop/index.d.ts +17 -14
  13. package/dist/browser/xcss-prop/index.js.map +1 -1
  14. package/dist/cjs/create-strict-api/index.d.ts +19 -22
  15. package/dist/cjs/create-strict-api/index.js +6 -4
  16. package/dist/cjs/create-strict-api/index.js.map +1 -1
  17. package/dist/cjs/create-strict-api/types.d.ts +28 -0
  18. package/dist/cjs/create-strict-api/types.js +3 -0
  19. package/dist/cjs/create-strict-api/types.js.map +1 -0
  20. package/dist/cjs/css/index.d.ts +3 -1
  21. package/dist/cjs/css/index.js.map +1 -1
  22. package/dist/cjs/css-map/index.d.ts +15 -2
  23. package/dist/cjs/css-map/index.js.map +1 -1
  24. package/dist/cjs/types.d.ts +4 -2
  25. package/dist/cjs/xcss-prop/index.d.ts +17 -14
  26. package/dist/cjs/xcss-prop/index.js.map +1 -1
  27. package/dist/esm/create-strict-api/index.d.ts +19 -22
  28. package/dist/esm/create-strict-api/index.js +6 -4
  29. package/dist/esm/create-strict-api/index.js.map +1 -1
  30. package/dist/esm/create-strict-api/types.d.ts +28 -0
  31. package/dist/esm/create-strict-api/types.js +2 -0
  32. package/dist/esm/create-strict-api/types.js.map +1 -0
  33. package/dist/esm/css/index.d.ts +3 -1
  34. package/dist/esm/css/index.js.map +1 -1
  35. package/dist/esm/css-map/index.d.ts +15 -2
  36. package/dist/esm/css-map/index.js.map +1 -1
  37. package/dist/esm/types.d.ts +4 -2
  38. package/dist/esm/xcss-prop/index.d.ts +17 -14
  39. package/dist/esm/xcss-prop/index.js.map +1 -1
  40. package/package.json +1 -1
  41. package/src/create-strict-api/__tests__/__fixtures__/strict-api-recursive.ts +6 -6
  42. package/src/create-strict-api/__tests__/__fixtures__/strict-api.ts +14 -3
  43. package/src/create-strict-api/__tests__/css-func.test.tsx +49 -0
  44. package/src/create-strict-api/__tests__/generics.test.tsx +26 -5
  45. package/src/create-strict-api/__tests__/index.test.tsx +198 -32
  46. package/src/create-strict-api/index.ts +41 -34
  47. package/src/create-strict-api/types.ts +69 -0
  48. package/src/css/index.ts +5 -1
  49. package/src/css-map/index.ts +20 -2
  50. package/src/types.ts +14 -10
  51. package/src/xcss-prop/__tests__/media-query-strict.test.tsx +179 -0
  52. package/src/xcss-prop/__tests__/media-query.test.tsx +53 -0
  53. package/src/xcss-prop/__tests__/xcss-prop.test.tsx +3 -34
  54. package/src/xcss-prop/index.ts +47 -28
@@ -0,0 +1,179 @@
1
+ /** @jsxImportSource @compiled/react */
2
+ // eslint-disable-next-line import/no-extraneous-dependencies
3
+ import { cssMap as cssMapLoose } from '@compiled/react';
4
+ import { render } from '@testing-library/react';
5
+
6
+ import { cssMap } from '../../create-strict-api/__tests__/__fixtures__/strict-api';
7
+ import type { XCSSProp } from '../../create-strict-api/__tests__/__fixtures__/strict-api';
8
+ import type { XCSSAllProperties, XCSSAllPseudos } from '../index';
9
+
10
+ function CSSPropComponent({
11
+ testId,
12
+ xcss,
13
+ }: {
14
+ testId?: string;
15
+ xcss: ReturnType<typeof XCSSProp<XCSSAllProperties, XCSSAllPseudos>>;
16
+ }) {
17
+ return (
18
+ <div data-testid={testId} className={xcss}>
19
+ foo
20
+ </div>
21
+ );
22
+ }
23
+
24
+ const styles = cssMap({
25
+ invalidMediaObject: {
26
+ // @ts-expect-error — @media at rule object is not allowed in strict cssMap
27
+ '@media': {
28
+ screen: { color: 'red' },
29
+ },
30
+ },
31
+ invalidMediaQuery: {
32
+ // @ts-expect-error — this specific @media is not in our allowed types
33
+ '@media (min-width: 100px)': {
34
+ color: 'red',
35
+ },
36
+ },
37
+ valid: {
38
+ '@media (min-width: 110rem)': {
39
+ // @ts-expect-error — color should be a value from the strict schema
40
+ color: 'red',
41
+ },
42
+ },
43
+ });
44
+
45
+ const looseStyles = cssMapLoose({
46
+ invalidMediaObject: {
47
+ '@media': {
48
+ screen: { color: 'var(--ds-text)' },
49
+ },
50
+ },
51
+ invalidMediaQuery: {
52
+ '@media (min-width: 100px)': {
53
+ color: 'red',
54
+ },
55
+ },
56
+ validMediaQueryInvalidProperty: {
57
+ '@media (min-width: 110rem)': {
58
+ color: 'red',
59
+ },
60
+ },
61
+ valid: {
62
+ '@media (min-width: 110rem)': {
63
+ color: 'var(--ds-text)',
64
+ },
65
+ },
66
+ });
67
+
68
+ describe('xcss prop', () => {
69
+ it('should block all usage of loose media queries in strict api as it is unsafe', () => {
70
+ const { getByText } = render(
71
+ <CSSPropComponent
72
+ // @ts-expect-error — Block all media queries in strict xcss prop
73
+ xcss={looseStyles.valid}
74
+ />
75
+ );
76
+
77
+ expect(getByText('foo')).toHaveCompiledCss('color', 'var(--ds-text)', {
78
+ media: '(min-width: 110rem)',
79
+ });
80
+ });
81
+
82
+ it('should type error invalid media queries from loose api', () => {
83
+ const { getByTestId } = render(
84
+ <>
85
+ <CSSPropComponent
86
+ // @ts-expect-error — Types of property '"@media"' are incompatible.
87
+ xcss={looseStyles.invalidMediaObject}
88
+ />
89
+ <CSSPropComponent testId="foobar" xcss={styles.invalidMediaObject} />
90
+ </>
91
+ );
92
+
93
+ expect(getByTestId('foobar')).toHaveCompiledCss('color', 'red', { media: 'screen' });
94
+ });
95
+
96
+ it('should allow valid media queries in inline xcss prop', () => {
97
+ const { getByText } = render(
98
+ <CSSPropComponent
99
+ xcss={{
100
+ '@media (min-width: 110rem)': {
101
+ color: 'var(--ds-text)',
102
+ },
103
+ }}
104
+ />
105
+ );
106
+
107
+ expect(getByText('foo')).toHaveCompiledCss('color', 'var(--ds-text)', {
108
+ media: '(min-width: 110rem)',
109
+ });
110
+ });
111
+
112
+ it('should allow valid media queries in inline xcss prop', () => {
113
+ const { getByText } = render(
114
+ <CSSPropComponent
115
+ xcss={{
116
+ '@media (min-width: 110rem)': {
117
+ // @ts-expect-error — color should be a value from the strict schema
118
+ color: 'red',
119
+ },
120
+ }}
121
+ />
122
+ );
123
+
124
+ expect(getByText('foo')).toHaveCompiledCss('color', 'red', { media: '(min-width: 110rem)' });
125
+ });
126
+
127
+ it('should allow valid psuedo through inline xcss prop', () => {
128
+ const { getByText } = render(
129
+ <CSSPropComponent
130
+ xcss={{ '@media (min-width: 30rem)': { '&:hover': { color: 'var(--ds-text-hover)' } } }}
131
+ />
132
+ );
133
+
134
+ expect(getByText('foo')).toHaveCompiledCss('color', 'var(--ds-text-hover)', {
135
+ media: '(min-width: 30rem)',
136
+ target: ':hover',
137
+ });
138
+ });
139
+
140
+ it('should type error for unexpected media query', () => {
141
+ const { getByTestId } = render(
142
+ <>
143
+ <CSSPropComponent
144
+ // NOTE: This doesn't currently error as the output isn't the generic value
145
+ // when the cssMap call has type supressions. While not ideal this is acceptable
146
+ // for now. Hopefully we can fix this in the future.
147
+ xcss={styles.invalidMediaQuery}
148
+ />
149
+ <CSSPropComponent
150
+ // @ts-expect-error — Passing loose media queries to XCSS prop is not supported.
151
+ xcss={looseStyles.invalidMediaQuery}
152
+ />
153
+ <CSSPropComponent
154
+ // @ts-expect-error — Passing loose media queries to XCSS prop is not supported.
155
+ xcss={looseStyles.validMediaQueryInvalidProperty}
156
+ />
157
+ <CSSPropComponent
158
+ testId="foobar"
159
+ xcss={{
160
+ // @ts-expect-error — Types of property '"@media"' are incompatible.
161
+ '@media (min-width: 100px)': {
162
+ color: 'var(--ds-text)',
163
+ },
164
+ }}
165
+ />
166
+ </>
167
+ );
168
+
169
+ expect(getByTestId('foobar')).toHaveCompiledCss('color', 'var(--ds-text)', {
170
+ media: '(min-width: 100px)',
171
+ });
172
+ });
173
+
174
+ it('should type check top level media query styles from cssMap', () => {
175
+ const { getByText } = render(<CSSPropComponent xcss={styles.valid} />);
176
+
177
+ expect(getByText('foo')).toHaveCompiledCss('color', 'red', { media: '(min-width: 110rem)' });
178
+ });
179
+ });
@@ -0,0 +1,53 @@
1
+ /** @jsxImportSource @compiled/react */
2
+ // eslint-disable-next-line import/no-extraneous-dependencies
3
+ import { cssMap } from '@compiled/react';
4
+ import { render } from '@testing-library/react';
5
+
6
+ import type { XCSSProp, XCSSAllProperties, XCSSAllPseudos } from '../index';
7
+
8
+ function CSSPropComponent({ xcss }: { xcss: XCSSProp<XCSSAllProperties, XCSSAllPseudos> }) {
9
+ return <div className={xcss}>foo</div>;
10
+ }
11
+
12
+ const styles = cssMap({
13
+ invalid: {
14
+ '@media': { screen: { color: 'red' } },
15
+ },
16
+ valid: { '@media screen': { color: 'red' } },
17
+ });
18
+
19
+ describe('xcss prop', () => {
20
+ it('should allow valid media queries in inline xcss prop', () => {
21
+ const { getByText } = render(<CSSPropComponent xcss={{ '@media screen': { color: 'red' } }} />);
22
+
23
+ expect(getByText('foo')).toHaveCompiledCss('color', 'red', { media: 'screen' });
24
+ });
25
+
26
+ it('should allow valid psuedo through inline xcss prop', () => {
27
+ const { getByText } = render(
28
+ <CSSPropComponent xcss={{ '@media screen': { '&:hover': { color: 'green' } } }} />
29
+ );
30
+
31
+ expect(getByText('foo')).toHaveCompiledCss('color', 'green', {
32
+ media: 'screen',
33
+ target: ':hover',
34
+ });
35
+ });
36
+
37
+ it('should type error for disallowed nested media query object from cssMap', () => {
38
+ const { getByText } = render(
39
+ <CSSPropComponent
40
+ // @ts-expect-error — @media object is not allowed in xcss prop
41
+ xcss={styles.invalid}
42
+ />
43
+ );
44
+
45
+ expect(getByText('foo')).toHaveCompiledCss('color', 'red', { media: 'screen' });
46
+ });
47
+
48
+ it('should type check top level media query styles from cssMap', () => {
49
+ const { getByText } = render(<CSSPropComponent xcss={styles.valid} />);
50
+
51
+ expect(getByText('foo')).toHaveCompiledCss('color', 'red', { media: 'screen' });
52
+ });
53
+ });
@@ -177,7 +177,7 @@ describe('xcss prop', () => {
177
177
  ).toBeObject();
178
178
  });
179
179
 
180
- it('should type error when passing in at rules to xcss prop', () => {
180
+ it('should type error when passing in @media property to xcss prop', () => {
181
181
  function CSSPropComponent({ xcss }: { xcss: XCSSProp<'color', '&:hover'> }) {
182
182
  return <div className={xcss}>foo</div>;
183
183
  }
@@ -228,11 +228,7 @@ describe('xcss prop', () => {
228
228
  function CSSPropComponent({
229
229
  xcss,
230
230
  }: {
231
- xcss: XCSSProp<
232
- 'color' | 'backgroundColor',
233
- '&:hover',
234
- { requiredProperties: 'color'; requiredPseudos: never }
235
- >;
231
+ xcss: XCSSProp<'color' | 'backgroundColor', '&:hover', { requiredProperties: 'color' }>;
236
232
  }) {
237
233
  return <div className={xcss}>foo</div>;
238
234
  }
@@ -249,11 +245,7 @@ describe('xcss prop', () => {
249
245
  function CSSPropComponent({
250
246
  xcss,
251
247
  }: {
252
- xcss: XCSSProp<
253
- 'color' | 'backgroundColor',
254
- '&:hover',
255
- { requiredProperties: 'color'; requiredPseudos: never }
256
- >;
248
+ xcss: XCSSProp<'color' | 'backgroundColor', '&:hover', { requiredProperties: 'color' }>;
257
249
  }) {
258
250
  return <div className={xcss}>foo</div>;
259
251
  }
@@ -268,27 +260,4 @@ describe('xcss prop', () => {
268
260
  />
269
261
  ).toBeObject();
270
262
  });
271
-
272
- it('should mark a xcss prop pseudo as required', () => {
273
- function CSSPropComponent({
274
- xcss,
275
- }: {
276
- xcss: XCSSProp<
277
- 'color' | 'backgroundColor',
278
- '&:hover',
279
- { requiredProperties: never; requiredPseudos: '&:hover' }
280
- >;
281
- }) {
282
- return <div className={xcss}>foo</div>;
283
- }
284
-
285
- expectTypeOf(
286
- <CSSPropComponent
287
- // @ts-expect-error — Property '"&:hover"' is missing in type '{ color: string; }' but required in type '{ "&:hover": MarkAsRequired<XCSSItem<"backgroundColor" | "color">, never>; }'.
288
- xcss={{
289
- color: 'red',
290
- }}
291
- />
292
- ).toBeObject();
293
- });
294
263
  });
@@ -1,44 +1,58 @@
1
1
  import type * as CSS from 'csstype';
2
2
 
3
+ import type { ApplySchemaValue } from '../create-strict-api/types';
3
4
  import { ac } from '../runtime';
4
- import type { CSSPseudos, CSSProperties, StrictCSSProperties } from '../types';
5
+ import type { CSSPseudos, CSSPseudoClasses, CSSProperties, StrictCSSProperties } from '../types';
5
6
 
6
7
  type MarkAsRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
7
8
 
8
- type XCSSItem<TStyleDecl extends keyof CSSProperties, TSchema> = {
9
- [Q in keyof CSSProperties]: Q extends TStyleDecl
10
- ?
11
- | CompiledPropertyDeclarationReference
12
- | (Q extends keyof TSchema ? TSchema[Q] : CSSProperties[Q])
9
+ type XCSSValue<
10
+ TStyleDecl extends keyof CSSProperties,
11
+ TSchema,
12
+ TPseudoKey extends CSSPseudoClasses | ''
13
+ > = {
14
+ [Q in keyof StrictCSSProperties]: Q extends TStyleDecl
15
+ ? ApplySchemaValue<TSchema, Q, TPseudoKey>
13
16
  : never;
14
17
  };
15
18
 
16
- type XCSSPseudos<
17
- TAllowedProperties extends keyof CSSProperties,
19
+ type XCSSPseudo<
20
+ TAllowedProperties extends keyof StrictCSSProperties,
18
21
  TAllowedPseudos extends CSSPseudos,
19
22
  TRequiredProperties extends { requiredProperties: TAllowedProperties },
20
23
  TSchema
21
24
  > = {
22
25
  [Q in CSSPseudos]?: Q extends TAllowedPseudos
23
26
  ? MarkAsRequired<
24
- XCSSItem<TAllowedProperties, Q extends keyof TSchema ? TSchema[Q] : object>,
27
+ XCSSValue<TAllowedProperties, TSchema, Q extends CSSPseudoClasses ? Q : ''>,
25
28
  TRequiredProperties['requiredProperties']
26
29
  >
27
30
  : never;
28
31
  };
29
32
 
33
+ type XCSSMediaQuery<
34
+ TAllowedProperties extends keyof StrictCSSProperties,
35
+ TAllowedPseudos extends CSSPseudos,
36
+ TAllowedMediaQueries extends string,
37
+ TSchema
38
+ > = {
39
+ [Q in `@media ${TAllowedMediaQueries}`]?:
40
+ | XCSSValue<TAllowedProperties, TSchema, ''>
41
+ | XCSSPseudo<TAllowedProperties, TAllowedPseudos, never, TSchema>;
42
+ };
43
+
30
44
  /**
31
45
  * These APIs we don't want to allow to be passed through the `xcss` prop but we also
32
46
  * must declare them so the (lack-of a) excess property check doesn't bite us and allow
33
47
  * unexpected values through.
34
48
  */
35
- type BlockedRules = {
49
+ type BlockedRules<TMode extends 'loose' | 'strict'> = {
50
+ // To ensure styles that aren't allowed through XCSS prop strict APIs we block any
51
+ // loose media queries from being passed through as we can't ensure they're correct.
52
+ '@media [loose]'?: TMode extends 'loose' ? any : never;
36
53
  selectors?: never;
37
54
  } & {
38
- /**
39
- * We currently block all at rules from xcss prop.
40
- * This needs us to decide on what the final API is across Compiled to be able to set.
41
- */
55
+ // We also block all type level at rule "objects" that are present on cssMap.
42
56
  [Q in CSS.AtRules]?: never;
43
57
  };
44
58
 
@@ -54,7 +68,7 @@ type CompiledPropertyDeclarationReference = {
54
68
  export type CompiledStyles<TObject> = {
55
69
  [Q in keyof TObject]: TObject[Q] extends Record<string, unknown>
56
70
  ? CompiledStyles<TObject[Q]>
57
- : CompiledPropertyDeclarationReference;
71
+ : CompiledPropertyDeclarationReference & TObject[Q];
58
72
  };
59
73
 
60
74
  /**
@@ -111,7 +125,7 @@ export type XCSSAllPseudos = CSSPseudos;
111
125
  * xcss?: XCSSProp<XCSSAllProperties, '&:hover'>;
112
126
  *
113
127
  * // The xcss prop is required as well as the color property. No pseudos are required.
114
- * xcss: XCSSProp<XCSSAllProperties, '&:hover', { requiredProperties: 'color', requiredPseudos: never }>;
128
+ * xcss: XCSSProp<XCSSAllProperties, '&:hover', { requiredProperties: 'color' }>;
115
129
  * }
116
130
  *
117
131
  * function MyComponent({ xcss }: MyComponentProps) {
@@ -134,32 +148,37 @@ export type XCSSAllPseudos = CSSPseudos;
134
148
  * To concatenate and conditonally apply styles use the {@link cssMap} {@link cx} functions.
135
149
  */
136
150
  export type XCSSProp<
137
- TAllowedProperties extends keyof CSSProperties,
151
+ TAllowedProperties extends keyof StrictCSSProperties,
138
152
  TAllowedPseudos extends CSSPseudos,
139
153
  TRequiredProperties extends {
140
154
  requiredProperties: TAllowedProperties;
141
- requiredPseudos: TAllowedPseudos;
142
155
  } = never
143
- > = Internal$XCSSProp<TAllowedProperties, TAllowedPseudos, object, TRequiredProperties>;
156
+ > = Internal$XCSSProp<
157
+ TAllowedProperties,
158
+ TAllowedPseudos,
159
+ string,
160
+ object,
161
+ TRequiredProperties,
162
+ 'loose'
163
+ >;
144
164
 
145
165
  export type Internal$XCSSProp<
146
- TAllowedProperties extends keyof CSSProperties,
166
+ TAllowedProperties extends keyof StrictCSSProperties,
147
167
  TAllowedPseudos extends CSSPseudos,
168
+ TAllowedMediaQueries extends string,
148
169
  TSchema,
149
170
  TRequiredProperties extends {
150
171
  requiredProperties: TAllowedProperties;
151
- requiredPseudos: TAllowedPseudos;
152
- }
172
+ },
173
+ TMode extends 'loose' | 'strict'
153
174
  > =
154
175
  | (MarkAsRequired<
155
- XCSSItem<TAllowedProperties, TSchema>,
176
+ XCSSValue<TAllowedProperties, TSchema, ''>,
156
177
  TRequiredProperties['requiredProperties']
157
178
  > &
158
- MarkAsRequired<
159
- XCSSPseudos<TAllowedProperties, TAllowedPseudos, TRequiredProperties, TSchema>,
160
- TRequiredProperties['requiredPseudos']
161
- > &
162
- BlockedRules)
179
+ XCSSPseudo<TAllowedProperties, TAllowedPseudos, TRequiredProperties, TSchema> &
180
+ XCSSMediaQuery<TAllowedProperties, TAllowedPseudos, TAllowedMediaQueries, TSchema> &
181
+ BlockedRules<TMode>)
163
182
  | false
164
183
  | null
165
184
  | undefined;