@compiled/react 0.16.10 → 0.17.1
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.
- package/dist/browser/create-strict-api/index.d.ts +19 -22
- package/dist/browser/create-strict-api/index.js +6 -4
- package/dist/browser/create-strict-api/index.js.map +1 -1
- package/dist/browser/create-strict-api/types.d.ts +28 -0
- package/dist/browser/create-strict-api/types.js +2 -0
- package/dist/browser/create-strict-api/types.js.map +1 -0
- package/dist/browser/css/index.d.ts +2 -0
- package/dist/browser/css/index.js.map +1 -1
- package/dist/browser/css-map/index.d.ts +15 -2
- package/dist/browser/css-map/index.js.map +1 -1
- package/dist/browser/index.d.ts +3 -2
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/types.d.ts +4 -2
- package/dist/browser/xcss-prop/index.d.ts +17 -14
- package/dist/browser/xcss-prop/index.js.map +1 -1
- package/dist/cjs/create-strict-api/index.d.ts +19 -22
- package/dist/cjs/create-strict-api/index.js +6 -4
- package/dist/cjs/create-strict-api/index.js.map +1 -1
- package/dist/cjs/create-strict-api/types.d.ts +28 -0
- package/dist/cjs/create-strict-api/types.js +3 -0
- package/dist/cjs/create-strict-api/types.js.map +1 -0
- package/dist/cjs/css/index.d.ts +2 -0
- package/dist/cjs/css/index.js.map +1 -1
- package/dist/cjs/css-map/index.d.ts +15 -2
- package/dist/cjs/css-map/index.js.map +1 -1
- package/dist/cjs/index.d.ts +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types.d.ts +4 -2
- package/dist/cjs/xcss-prop/index.d.ts +17 -14
- package/dist/cjs/xcss-prop/index.js.map +1 -1
- package/dist/esm/create-strict-api/index.d.ts +19 -22
- package/dist/esm/create-strict-api/index.js +6 -4
- package/dist/esm/create-strict-api/index.js.map +1 -1
- package/dist/esm/create-strict-api/types.d.ts +28 -0
- package/dist/esm/create-strict-api/types.js +2 -0
- package/dist/esm/create-strict-api/types.js.map +1 -0
- package/dist/esm/css/index.d.ts +2 -0
- package/dist/esm/css/index.js.map +1 -1
- package/dist/esm/css-map/index.d.ts +15 -2
- package/dist/esm/css-map/index.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.d.ts +4 -2
- package/dist/esm/xcss-prop/index.d.ts +17 -14
- package/dist/esm/xcss-prop/index.js.map +1 -1
- package/package.json +1 -1
- package/src/create-strict-api/__tests__/__fixtures__/strict-api-recursive.ts +6 -6
- package/src/create-strict-api/__tests__/__fixtures__/strict-api.ts +14 -3
- package/src/create-strict-api/__tests__/css-func.test.tsx +49 -0
- package/src/create-strict-api/__tests__/generics.test.tsx +26 -5
- package/src/create-strict-api/__tests__/index.test.tsx +198 -32
- package/src/create-strict-api/index.ts +41 -34
- package/src/create-strict-api/types.ts +69 -0
- package/src/css/index.ts +2 -0
- package/src/css-map/index.ts +20 -2
- package/src/index.ts +16 -2
- package/src/types.ts +14 -10
- package/src/xcss-prop/__tests__/media-query-strict.test.tsx +179 -0
- package/src/xcss-prop/__tests__/media-query.test.tsx +53 -0
- package/src/xcss-prop/__tests__/xcss-prop.test.tsx +3 -34
- package/src/xcss-prop/index.ts +47 -28
package/src/css-map/index.ts
CHANGED
|
@@ -83,6 +83,21 @@ type ExtendedSelectors = {
|
|
|
83
83
|
selectors?: ExtendedSelector;
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
+
type LooseMediaQueries = Record<`@media ${string}`, CSSProperties & AllPseudos>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* We remap media query keys to `"@media"` so it's blocked inside the strict APIs.
|
|
90
|
+
* This is done as it's currently impossible to ensure type safety end-to-end — when
|
|
91
|
+
* passing in unknown media queries from the loose API into the strict API you end up
|
|
92
|
+
* being also able to pass any styles you want, which makes the whole point of the strict
|
|
93
|
+
* API meaningless.
|
|
94
|
+
*
|
|
95
|
+
* Sorry folks!
|
|
96
|
+
*/
|
|
97
|
+
type RemapMedia<TStyles> = {
|
|
98
|
+
[Q in keyof TStyles as Q extends `@media ${string}` ? '@media [loose]' : Q]: TStyles[Q];
|
|
99
|
+
};
|
|
100
|
+
|
|
86
101
|
/**
|
|
87
102
|
* ## CSS Map
|
|
88
103
|
*
|
|
@@ -100,11 +115,14 @@ type ExtendedSelectors = {
|
|
|
100
115
|
* ```
|
|
101
116
|
*/
|
|
102
117
|
export default function cssMap<
|
|
103
|
-
TStyles extends Record<
|
|
118
|
+
TStyles extends Record<
|
|
119
|
+
string,
|
|
120
|
+
CSSProperties & WhitelistedSelector & ExtendedSelectors & LooseMediaQueries
|
|
121
|
+
>
|
|
104
122
|
>(
|
|
105
123
|
_styles: TStyles
|
|
106
124
|
): {
|
|
107
|
-
readonly [P in keyof TStyles]: CompiledStyles<TStyles[P]
|
|
125
|
+
readonly [P in keyof TStyles]: CompiledStyles<RemapMedia<TStyles[P]>>;
|
|
108
126
|
} {
|
|
109
127
|
throw createSetupError();
|
|
110
128
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,10 +4,17 @@ import type { CompiledJSX } from './jsx/jsx-local-namespace';
|
|
|
4
4
|
|
|
5
5
|
export { ClassNames } from './class-names';
|
|
6
6
|
export { createStrictAPI } from './create-strict-api';
|
|
7
|
+
export type {
|
|
8
|
+
PseudosDeclarations,
|
|
9
|
+
MediaQueries,
|
|
10
|
+
AllowedStyles,
|
|
11
|
+
ApplySchema,
|
|
12
|
+
ApplySchemaMap,
|
|
13
|
+
} from './create-strict-api/types';
|
|
7
14
|
export { default as css } from './css';
|
|
8
15
|
export { default as cssMap } from './css-map';
|
|
9
16
|
export { keyframes } from './keyframes';
|
|
10
|
-
export { styled } from './styled';
|
|
17
|
+
export { styled, StyledProps } from './styled';
|
|
11
18
|
export type {
|
|
12
19
|
CSSProperties,
|
|
13
20
|
CSSProps,
|
|
@@ -16,7 +23,14 @@ export type {
|
|
|
16
23
|
CssType,
|
|
17
24
|
StrictCSSProperties,
|
|
18
25
|
} from './types';
|
|
19
|
-
export {
|
|
26
|
+
export {
|
|
27
|
+
type XCSSAllProperties,
|
|
28
|
+
type XCSSAllPseudos,
|
|
29
|
+
type XCSSProp,
|
|
30
|
+
type CompiledStyles,
|
|
31
|
+
type Internal$XCSSProp,
|
|
32
|
+
cx,
|
|
33
|
+
} from './xcss-prop';
|
|
20
34
|
|
|
21
35
|
// Pass through the (classic) jsx runtime.
|
|
22
36
|
// Compiled currently doesn't define its own and uses this purely to enable a local jsx namespace.
|
package/src/types.ts
CHANGED
|
@@ -35,14 +35,7 @@ export type CssFunction<TProps = unknown> =
|
|
|
35
35
|
| boolean // Something like `false && styles`
|
|
36
36
|
| undefined; // Something like `undefined && styles`
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
* This list of pseudo-classes and pseudo-elements are from csstype
|
|
40
|
-
* but with & added to the front. Compiled supports both &-ful
|
|
41
|
-
* and &-less forms and both will target the current element
|
|
42
|
-
* (`&:hover` <==> `:hover`), however we force the use of the
|
|
43
|
-
* &-ful form for consistency with the nested spec for new APIs.
|
|
44
|
-
*/
|
|
45
|
-
export type CSSPseudos =
|
|
38
|
+
export type CSSPseudoElements =
|
|
46
39
|
| '&::after'
|
|
47
40
|
| '&::backdrop'
|
|
48
41
|
| '&::before'
|
|
@@ -56,7 +49,9 @@ export type CSSPseudos =
|
|
|
56
49
|
| '&::selection'
|
|
57
50
|
| '&::spelling-error'
|
|
58
51
|
| '&::target-text'
|
|
59
|
-
| '&::view-transition'
|
|
52
|
+
| '&::view-transition';
|
|
53
|
+
|
|
54
|
+
export type CSSPseudoClasses =
|
|
60
55
|
| '&:active'
|
|
61
56
|
| '&:autofill'
|
|
62
57
|
| '&:blank'
|
|
@@ -94,13 +89,22 @@ export type CSSPseudos =
|
|
|
94
89
|
| '&:valid'
|
|
95
90
|
| '&:visited';
|
|
96
91
|
|
|
92
|
+
/*
|
|
93
|
+
* This list of pseudo-classes and pseudo-elements are from csstype
|
|
94
|
+
* but with & added to the front. Compiled supports both &-ful
|
|
95
|
+
* and &-less forms and both will target the current element
|
|
96
|
+
* (`&:hover` <==> `:hover`), however we force the use of the
|
|
97
|
+
* &-ful form for consistency with the nested spec for new APIs.
|
|
98
|
+
*/
|
|
99
|
+
export type CSSPseudos = CSSPseudoElements | CSSPseudoClasses;
|
|
100
|
+
|
|
97
101
|
/**
|
|
98
102
|
* The XCSSProp must be given all known available properties even
|
|
99
103
|
* if it takes a subset of them. This ensures the (lack-of an)
|
|
100
104
|
* excess property check doesn't enable makers to circumvent the
|
|
101
105
|
* system and pass in values they shouldn't.
|
|
102
106
|
*/
|
|
103
|
-
export type CSSProperties = Readonly<CSS.Properties<string | number>>;
|
|
107
|
+
export type CSSProperties = Readonly<CSS.Properties<(string & object) | number>>;
|
|
104
108
|
|
|
105
109
|
/**
|
|
106
110
|
* A stricter subset of the {@link CSSProperties} type that excludes
|
|
@@ -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
|
|
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
|
});
|
package/src/xcss-prop/index.ts
CHANGED
|
@@ -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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
17
|
-
TAllowedProperties extends keyof
|
|
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
|
-
|
|
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'
|
|
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
|
|
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<
|
|
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
|
|
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
|
-
|
|
152
|
-
|
|
172
|
+
},
|
|
173
|
+
TMode extends 'loose' | 'strict'
|
|
153
174
|
> =
|
|
154
175
|
| (MarkAsRequired<
|
|
155
|
-
|
|
176
|
+
XCSSValue<TAllowedProperties, TSchema, ''>,
|
|
156
177
|
TRequiredProperties['requiredProperties']
|
|
157
178
|
> &
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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;
|