@wordpress/style-engine 0.8.0 → 0.11.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 (44) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +38 -6
  3. package/build/styles/border/index.js +94 -0
  4. package/build/styles/border/index.js.map +1 -0
  5. package/build/styles/index.js +3 -1
  6. package/build/styles/index.js.map +1 -1
  7. package/build/styles/spacing/margin.js +4 -1
  8. package/build/styles/spacing/margin.js.map +1 -1
  9. package/build/styles/spacing/padding.js +4 -1
  10. package/build/styles/spacing/padding.js.map +1 -1
  11. package/build/styles/utils.js +25 -9
  12. package/build/styles/utils.js.map +1 -1
  13. package/build-module/styles/border/index.js +85 -0
  14. package/build-module/styles/border/index.js.map +1 -0
  15. package/build-module/styles/index.js +2 -1
  16. package/build-module/styles/index.js.map +1 -1
  17. package/build-module/styles/spacing/margin.js +4 -1
  18. package/build-module/styles/spacing/margin.js.map +1 -1
  19. package/build-module/styles/spacing/padding.js +4 -1
  20. package/build-module/styles/spacing/padding.js.map +1 -1
  21. package/build-module/styles/utils.js +24 -10
  22. package/build-module/styles/utils.js.map +1 -1
  23. package/build-types/styles/border/index.d.ts +10 -0
  24. package/build-types/styles/border/index.d.ts.map +1 -0
  25. package/build-types/styles/index.d.ts +3 -7
  26. package/build-types/styles/index.d.ts.map +1 -1
  27. package/build-types/styles/spacing/margin.d.ts.map +1 -1
  28. package/build-types/styles/spacing/padding.d.ts.map +1 -1
  29. package/build-types/styles/utils.d.ts +16 -7
  30. package/build-types/styles/utils.d.ts.map +1 -1
  31. package/build-types/types.d.ts +26 -1
  32. package/build-types/types.d.ts.map +1 -1
  33. package/class-wp-style-engine.php +303 -97
  34. package/package.json +2 -2
  35. package/phpunit/class-wp-style-engine-test.php +154 -6
  36. package/src/styles/border/index.ts +145 -0
  37. package/src/styles/index.ts +7 -1
  38. package/src/styles/spacing/margin.ts +4 -6
  39. package/src/styles/spacing/padding.ts +4 -6
  40. package/src/styles/utils.ts +33 -11
  41. package/src/test/index.js +51 -0
  42. package/src/test/utils.js +12 -0
  43. package/src/types.ts +33 -1
  44. package/tsconfig.tsbuildinfo +1 -1
@@ -17,8 +17,8 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
17
17
  *
18
18
  * @dataProvider data_generate_styles_fixtures
19
19
  */
20
- function test_generate_styles( $block_styles, $expected_output ) {
21
- $generated_styles = wp_style_engine_generate( $block_styles );
20
+ function test_generate_styles( $block_styles, $options, $expected_output ) {
21
+ $generated_styles = wp_style_engine_generate( $block_styles, $options );
22
22
  $this->assertSame( $expected_output, $generated_styles );
23
23
  }
24
24
 
@@ -31,11 +31,13 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
31
31
  return array(
32
32
  'default_return_value' => array(
33
33
  'block_styles' => array(),
34
+ 'options' => null,
34
35
  'expected_output' => null,
35
36
  ),
36
37
 
37
38
  'inline_invalid_block_styles_empty' => array(
38
39
  'block_styles' => 'hello world!',
40
+ 'options' => null,
39
41
  'expected_output' => null,
40
42
  ),
41
43
 
@@ -43,6 +45,7 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
43
45
  'block_styles' => array(
44
46
  'pageBreakAfter' => 'verso',
45
47
  ),
48
+ 'options' => null,
46
49
  'expected_output' => array(),
47
50
  ),
48
51
 
@@ -50,6 +53,7 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
50
53
  'block_styles' => array(
51
54
  'pageBreakAfter' => 'verso',
52
55
  ),
56
+ 'options' => null,
53
57
  'expected_output' => array(),
54
58
  ),
55
59
 
@@ -59,6 +63,7 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
59
63
  'gap' => '1000vw',
60
64
  ),
61
65
  ),
66
+ 'options' => null,
62
67
  'expected_output' => array(),
63
68
  ),
64
69
 
@@ -68,12 +73,19 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
68
73
  'text' => 'var:preset|color|texas-flood',
69
74
  ),
70
75
  'spacing' => array(
71
- 'margin' => '111px',
76
+ 'margin' => '111px',
77
+ 'padding' => '0',
78
+ ),
79
+ 'border' => array(
80
+ 'color' => 'var:preset|color|cool-caramel',
81
+ 'width' => '2rem',
82
+ 'style' => 'dotted',
72
83
  ),
73
84
  ),
85
+ 'options' => array(),
74
86
  'expected_output' => array(
75
- 'css' => 'margin: 111px;',
76
- 'classnames' => 'has-text-color has-texas-flood-color',
87
+ 'css' => 'border-style: dotted; border-width: 2rem; padding: 0; margin: 111px;',
88
+ 'classnames' => 'has-text-color has-texas-flood-color has-border-color has-cool-caramel-border-color',
77
89
  ),
78
90
  ),
79
91
 
@@ -93,9 +105,18 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
93
105
  'right' => '10em',
94
106
  ),
95
107
  ),
108
+ 'border' => array(
109
+ 'radius' => array(
110
+ 'topLeft' => '99px',
111
+ 'topRight' => '98px',
112
+ 'bottomLeft' => '97px',
113
+ 'bottomRight' => '96px',
114
+ ),
115
+ ),
96
116
  ),
117
+ 'options' => null,
97
118
  'expected_output' => array(
98
- 'css' => 'padding-top: 42px; padding-left: 2%; padding-bottom: 44px; padding-right: 5rem; margin-top: 12rem; margin-left: 2vh; margin-bottom: 2px; margin-right: 10em;',
119
+ 'css' => 'border-top-left-radius: 99px; border-top-right-radius: 98px; border-bottom-left-radius: 97px; border-bottom-right-radius: 96px; padding-top: 42px; padding-left: 2%; padding-bottom: 44px; padding-right: 5rem; margin-top: 12rem; margin-left: 2vh; margin-bottom: 2px; margin-right: 10em;',
99
120
  ),
100
121
  ),
101
122
 
@@ -112,10 +133,57 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
112
133
  'letterSpacing' => '2',
113
134
  ),
114
135
  ),
136
+ 'options' => null,
115
137
  'expected_output' => array(
116
138
  'css' => 'font-family: Roboto,Oxygen-Sans,Ubuntu,sans-serif; font-style: italic; font-weight: 800; line-height: 1.3; text-decoration: underline; text-transform: uppercase; letter-spacing: 2;',
117
139
  ),
118
140
  ),
141
+
142
+ 'style_block_with_selector' => array(
143
+ 'block_styles' => array(
144
+ 'spacing' => array(
145
+ 'padding' => array(
146
+ 'top' => '42px',
147
+ 'left' => '2%',
148
+ 'bottom' => '44px',
149
+ 'right' => '5rem',
150
+ ),
151
+ ),
152
+ ),
153
+ 'options' => array( 'selector' => '.wp-selector > p' ),
154
+ 'expected_output' => array(
155
+ 'css' => '.wp-selector > p { padding-top: 42px; padding-left: 2%; padding-bottom: 44px; padding-right: 5rem; }',
156
+ ),
157
+ ),
158
+
159
+ 'elements_with_css_var_value' => array(
160
+ 'block_styles' => array(
161
+ 'color' => array(
162
+ 'text' => 'var:preset|color|my-little-pony',
163
+ ),
164
+ ),
165
+ 'options' => array(
166
+ 'selector' => '.wp-selector',
167
+ 'css_vars' => true,
168
+ ),
169
+ 'expected_output' => array(
170
+ 'css' => '.wp-selector { color: var(--wp--preset--color--my-little-pony); }',
171
+ 'classnames' => 'has-text-color has-my-little-pony-color',
172
+ ),
173
+ ),
174
+
175
+ 'elements_with_invalid_preset_style_property' => array(
176
+ 'block_styles' => array(
177
+ 'color' => array(
178
+ 'text' => 'var:preset|invalid_property|my-little-pony',
179
+ ),
180
+ ),
181
+ 'options' => array( 'selector' => '.wp-selector' ),
182
+ 'expected_output' => array(
183
+ 'classnames' => 'has-text-color',
184
+ ),
185
+ ),
186
+
119
187
  'valid_classnames_deduped' => array(
120
188
  'block_styles' => array(
121
189
  'color' => array(
@@ -128,10 +196,25 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
128
196
  'fontFamily' => 'var:preset|font-family|totally-awesome',
129
197
  ),
130
198
  ),
199
+ 'options' => array(),
131
200
  'expected_output' => array(
132
201
  'classnames' => 'has-text-color has-copper-socks-color has-background has-splendid-carrot-background-color has-like-wow-dude-gradient-background has-fantastic-font-size has-totally-awesome-font-family',
133
202
  ),
134
203
  ),
204
+
205
+ 'valid_classnames_and_css_vars' => array(
206
+ 'block_styles' => array(
207
+ 'color' => array(
208
+ 'text' => 'var:preset|color|teal-independents',
209
+ ),
210
+ ),
211
+ 'options' => array( 'css_vars' => true ),
212
+ 'expected_output' => array(
213
+ 'css' => 'color: var(--wp--preset--color--teal-independents);',
214
+ 'classnames' => 'has-text-color has-teal-independents-color',
215
+ ),
216
+ ),
217
+
135
218
  'valid_classnames_with_null_style_values' => array(
136
219
  'block_styles' => array(
137
220
  'color' => array(
@@ -139,11 +222,13 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
139
222
  'background' => null,
140
223
  ),
141
224
  ),
225
+ 'options' => array(),
142
226
  'expected_output' => array(
143
227
  'css' => 'color: #fff;',
144
228
  'classnames' => 'has-text-color',
145
229
  ),
146
230
  ),
231
+
147
232
  'invalid_classnames_preset_value' => array(
148
233
  'block_styles' => array(
149
234
  'color' => array(
@@ -155,10 +240,12 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
155
240
  'padding' => 'var:preset|spacing|padding',
156
241
  ),
157
242
  ),
243
+ 'options' => array(),
158
244
  'expected_output' => array(
159
245
  'classnames' => 'has-text-color has-background',
160
246
  ),
161
247
  ),
248
+
162
249
  'invalid_classnames_options' => array(
163
250
  'block_styles' => array(
164
251
  'typography' => array(
@@ -170,8 +257,69 @@ class WP_Style_Engine_Test extends WP_UnitTestCase {
170
257
  ),
171
258
  ),
172
259
  ),
260
+ 'options' => array(),
173
261
  'expected_output' => array(),
174
262
  ),
263
+
264
+ 'inline_valid_box_model_style_with_sides' => array(
265
+ 'block_styles' => array(
266
+ 'border' => array(
267
+ 'top' => array(
268
+ 'color' => '#fe1',
269
+ 'width' => '1.5rem',
270
+ 'style' => 'dashed',
271
+ ),
272
+ 'right' => array(
273
+ 'color' => '#fe2',
274
+ 'width' => '1.4rem',
275
+ 'style' => 'solid',
276
+ ),
277
+ 'bottom' => array(
278
+ 'color' => '#fe3',
279
+ 'width' => '1.3rem',
280
+ ),
281
+ 'left' => array(
282
+ 'color' => 'var:preset|color|swampy-yellow',
283
+ 'width' => '0.5rem',
284
+ 'style' => 'dotted',
285
+ ),
286
+ ),
287
+ ),
288
+ 'options' => array(),
289
+ 'expected_output' => array(
290
+ 'css' => 'border-top-color: #fe1; border-top-width: 1.5rem; border-top-style: dashed; border-right-color: #fe2; border-right-width: 1.4rem; border-right-style: solid; border-bottom-color: #fe3; border-bottom-width: 1.3rem; border-left-color: var(--wp--preset--color--swampy-yellow); border-left-width: 0.5rem; border-left-style: dotted;',
291
+ ),
292
+ ),
293
+
294
+ 'inline_invalid_box_model_style_with_sides' => array(
295
+ 'block_styles' => array(
296
+ 'border' => array(
297
+ 'top' => array(
298
+ 'top' => '#fe1',
299
+ 'right' => '1.5rem',
300
+ 'cheese' => 'dashed',
301
+ ),
302
+ 'right' => array(
303
+ 'right' => '#fe2',
304
+ 'top' => '1.4rem',
305
+ 'bacon' => 'solid',
306
+ ),
307
+ 'bottom' => array(
308
+ 'color' => 'var:preset|color|terrible-lizard',
309
+ 'bottom' => '1.3rem',
310
+ ),
311
+ 'left' => array(
312
+ 'left' => null,
313
+ 'width' => null,
314
+ 'top' => 'dotted',
315
+ ),
316
+ ),
317
+ ),
318
+ 'options' => array(),
319
+ 'expected_output' => array(
320
+ 'css' => 'border-bottom-color: var(--wp--preset--color--terrible-lizard);',
321
+ ),
322
+ ),
175
323
  );
176
324
  }
177
325
  }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import type {
5
+ BorderIndividualStyles,
6
+ BorderIndividualProperty,
7
+ GeneratedCSSRule,
8
+ Style,
9
+ StyleDefinition,
10
+ StyleOptions,
11
+ } from '../../types';
12
+ import { generateRule, generateBoxRules, upperFirst } from '../utils';
13
+
14
+ const color = {
15
+ name: 'color',
16
+ generate: (
17
+ style: Style,
18
+ options: StyleOptions,
19
+ path: string[] = [ 'border', 'color' ],
20
+ ruleKey: string = 'borderColor'
21
+ ): GeneratedCSSRule[] => {
22
+ return generateRule( style, options, path, ruleKey );
23
+ },
24
+ };
25
+
26
+ const radius = {
27
+ name: 'radius',
28
+ generate: ( style: Style, options: StyleOptions ): GeneratedCSSRule[] => {
29
+ return generateBoxRules(
30
+ style,
31
+ options,
32
+ [ 'border', 'radius' ],
33
+ {
34
+ default: 'borderRadius',
35
+ individual: 'border%sRadius',
36
+ },
37
+ [ 'topLeft', 'topRight', 'bottomLeft', 'bottomRight' ]
38
+ );
39
+ },
40
+ };
41
+
42
+ const borderStyle = {
43
+ name: 'style',
44
+ generate: (
45
+ style: Style,
46
+ options: StyleOptions,
47
+ path: string[] = [ 'border', 'style' ],
48
+ ruleKey: string = 'borderStyle'
49
+ ): GeneratedCSSRule[] => {
50
+ return generateRule( style, options, path, ruleKey );
51
+ },
52
+ };
53
+
54
+ const width = {
55
+ name: 'width',
56
+ generate: (
57
+ style: Style,
58
+ options: StyleOptions,
59
+ path: string[] = [ 'border', 'width' ],
60
+ ruleKey: string = 'borderWidth'
61
+ ): GeneratedCSSRule[] => {
62
+ return generateRule( style, options, path, ruleKey );
63
+ },
64
+ };
65
+
66
+ const borderDefinitionsWithIndividualStyles: StyleDefinition[] = [
67
+ color,
68
+ borderStyle,
69
+ width,
70
+ ];
71
+
72
+ /**
73
+ * Returns a curried generator function with the individual border property ('top' | 'right' | 'bottom' | 'left') baked in.
74
+ *
75
+ * @param individualProperty Individual border property ('top' | 'right' | 'bottom' | 'left').
76
+ *
77
+ * @return StyleDefinition[ 'generate' ]
78
+ */
79
+ const createBorderGenerateFunction =
80
+ ( individualProperty: BorderIndividualProperty ) =>
81
+ ( style: Style, options: StyleOptions ) => {
82
+ const styleValue:
83
+ | BorderIndividualStyles< typeof individualProperty >
84
+ | undefined = style?.border?.[ individualProperty ];
85
+
86
+ if ( ! styleValue ) {
87
+ return [];
88
+ }
89
+
90
+ return borderDefinitionsWithIndividualStyles.reduce(
91
+ (
92
+ acc: GeneratedCSSRule[],
93
+ borderDefinition: StyleDefinition
94
+ ): GeneratedCSSRule[] => {
95
+ const key = borderDefinition.name;
96
+ if (
97
+ styleValue.hasOwnProperty( key ) &&
98
+ typeof borderDefinition.generate === 'function'
99
+ ) {
100
+ const ruleKey = `border${ upperFirst(
101
+ individualProperty
102
+ ) }${ upperFirst( key ) }`;
103
+ acc.push(
104
+ ...borderDefinition.generate(
105
+ style,
106
+ options,
107
+ [ 'border', individualProperty, key ],
108
+ ruleKey
109
+ )
110
+ );
111
+ }
112
+ return acc;
113
+ },
114
+ []
115
+ );
116
+ };
117
+
118
+ const borderTop = {
119
+ name: 'borderTop',
120
+ generate: createBorderGenerateFunction( 'top' ),
121
+ };
122
+
123
+ const borderRight = {
124
+ name: 'borderRight',
125
+ generate: createBorderGenerateFunction( 'right' ),
126
+ };
127
+
128
+ const borderBottom = {
129
+ name: 'borderBottom',
130
+ generate: createBorderGenerateFunction( 'bottom' ),
131
+ };
132
+
133
+ const borderLeft = {
134
+ name: 'borderLeft',
135
+ generate: createBorderGenerateFunction( 'left' ),
136
+ };
137
+
138
+ export default [
139
+ ...borderDefinitionsWithIndividualStyles,
140
+ radius,
141
+ borderTop,
142
+ borderRight,
143
+ borderBottom,
144
+ borderLeft,
145
+ ];
@@ -1,8 +1,14 @@
1
1
  /**
2
2
  * Internal dependencies
3
3
  */
4
+ import border from './border';
4
5
  import color from './color';
5
6
  import spacing from './spacing';
6
7
  import typography from './typography';
7
8
 
8
- export const styleDefinitions = [ ...color, ...spacing, ...typography ];
9
+ export const styleDefinitions = [
10
+ ...border,
11
+ ...color,
12
+ ...spacing,
13
+ ...typography,
14
+ ];
@@ -7,12 +7,10 @@ import { generateBoxRules } from '../utils';
7
7
  const margin = {
8
8
  name: 'margin',
9
9
  generate: ( style: Style, options: StyleOptions ) => {
10
- return generateBoxRules(
11
- style,
12
- options,
13
- [ 'spacing', 'margin' ],
14
- 'margin'
15
- );
10
+ return generateBoxRules( style, options, [ 'spacing', 'margin' ], {
11
+ default: 'margin',
12
+ individual: 'margin%s',
13
+ } );
16
14
  },
17
15
  };
18
16
 
@@ -7,12 +7,10 @@ import { generateBoxRules } from '../utils';
7
7
  const padding = {
8
8
  name: 'padding',
9
9
  generate: ( style: Style, options: StyleOptions ) => {
10
- return generateBoxRules(
11
- style,
12
- options,
13
- [ 'spacing', 'padding' ],
14
- 'padding'
15
- );
10
+ return generateBoxRules( style, options, [ 'spacing', 'padding' ], {
11
+ default: 'padding',
12
+ individual: 'padding%s',
13
+ } );
16
14
  },
17
15
  };
18
16
 
@@ -1,12 +1,18 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { get, upperFirst } from 'lodash';
4
+ import { get } from 'lodash';
5
5
 
6
6
  /**
7
7
  * Internal dependencies
8
8
  */
9
- import type { GeneratedCSSRule, Style, Box, StyleOptions } from '../types';
9
+ import type {
10
+ CssRulesKeys,
11
+ GeneratedCSSRule,
12
+ Style,
13
+ Box,
14
+ StyleOptions,
15
+ } from '../types';
10
16
  import {
11
17
  VARIABLE_REFERENCE_PREFIX,
12
18
  VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE,
@@ -45,18 +51,20 @@ export function generateRule(
45
51
  /**
46
52
  * Returns a JSON representation of the generated CSS rules taking into account box model properties, top, right, bottom, left.
47
53
  *
48
- * @param style Style object.
49
- * @param options Options object with settings to adjust how the styles are generated.
50
- * @param path An array of strings representing the path to the style value in the style object.
51
- * @param ruleKey A CSS property key.
54
+ * @param style Style object.
55
+ * @param options Options object with settings to adjust how the styles are generated.
56
+ * @param path An array of strings representing the path to the style value in the style object.
57
+ * @param ruleKeys An array of CSS property keys and patterns.
58
+ * @param individualProperties The "sides" or individual properties for which to generate rules.
52
59
  *
53
- * @return GeneratedCSSRule[] CSS rules.
60
+ * @return GeneratedCSSRule[] CSS rules.
54
61
  */
55
62
  export function generateBoxRules(
56
63
  style: Style,
57
64
  options: StyleOptions,
58
65
  path: string[],
59
- ruleKey: string
66
+ ruleKeys: CssRulesKeys,
67
+ individualProperties: string[] = [ 'top', 'right', 'bottom', 'left' ]
60
68
  ): GeneratedCSSRule[] {
61
69
  const boxStyle: Box | string | undefined = get( style, path );
62
70
  if ( ! boxStyle ) {
@@ -67,17 +75,20 @@ export function generateBoxRules(
67
75
  if ( typeof boxStyle === 'string' ) {
68
76
  rules.push( {
69
77
  selector: options?.selector,
70
- key: ruleKey,
78
+ key: ruleKeys.default,
71
79
  value: boxStyle,
72
80
  } );
73
81
  } else {
74
- const sideRules = [ 'top', 'right', 'bottom', 'left' ].reduce(
82
+ const sideRules = individualProperties.reduce(
75
83
  ( acc: GeneratedCSSRule[], side: string ) => {
76
84
  const value: string | undefined = get( boxStyle, [ side ] );
77
85
  if ( value ) {
78
86
  acc.push( {
79
87
  selector: options?.selector,
80
- key: `${ ruleKey }${ upperFirst( side ) }`,
88
+ key: ruleKeys?.individual.replace(
89
+ '%s',
90
+ upperFirst( side )
91
+ ),
81
92
  value,
82
93
  } );
83
94
  }
@@ -111,3 +122,14 @@ export function getCSSVarFromStyleValue( styleValue: string ): string {
111
122
  }
112
123
  return styleValue;
113
124
  }
125
+
126
+ /**
127
+ * Capitalizes the first letter in a string.
128
+ *
129
+ * @param {string} str The string whose first letter the function will capitalize.
130
+ *
131
+ * @return string A CSS var value.
132
+ */
133
+ export function upperFirst( [ firstLetter, ...rest ]: string ) {
134
+ return firstLetter.toUpperCase() + rest.join( '' );
135
+ }
package/src/test/index.js CHANGED
@@ -81,6 +81,57 @@ describe( 'generate', () => {
81
81
  } )
82
82
  ).toEqual( 'color: var(--wp--preset--color--ham-sandwich);' );
83
83
  } );
84
+
85
+ it( 'should parse border rules', () => {
86
+ expect(
87
+ generate( {
88
+ border: {
89
+ color: 'var:preset|color|perky-peppermint',
90
+ width: '0.5em',
91
+ style: 'dotted',
92
+ radius: '11px',
93
+ },
94
+ } )
95
+ ).toEqual(
96
+ 'border-color: var(--wp--preset--color--perky-peppermint); border-style: dotted; border-width: 0.5em; border-radius: 11px;'
97
+ );
98
+ } );
99
+
100
+ it( 'should parse individual border rules', () => {
101
+ expect(
102
+ generate( {
103
+ border: {
104
+ top: {
105
+ color: 'var:preset|color|sandy-beach',
106
+ width: '9px',
107
+ style: 'dashed',
108
+ },
109
+ right: {
110
+ color: 'var:preset|color|leafy-avenue',
111
+ width: '5rem',
112
+ },
113
+ bottom: {
114
+ color: '#eee',
115
+ width: '2%',
116
+ style: 'solid',
117
+ },
118
+ left: {
119
+ color: 'var:preset|color|avocado-blues',
120
+ width: '100px',
121
+ style: 'dotted',
122
+ },
123
+ radius: {
124
+ topLeft: '1px',
125
+ topRight: '2px',
126
+ bottomLeft: '3px',
127
+ bottomRight: '4px',
128
+ },
129
+ },
130
+ } )
131
+ ).toEqual(
132
+ 'border-top-left-radius: 1px; border-top-right-radius: 2px; border-bottom-left-radius: 3px; border-bottom-right-radius: 4px; border-top-color: var(--wp--preset--color--sandy-beach); border-top-style: dashed; border-top-width: 9px; border-right-color: var(--wp--preset--color--leafy-avenue); border-right-width: 5rem; border-bottom-color: #eee; border-bottom-style: solid; border-bottom-width: 2%; border-left-color: var(--wp--preset--color--avocado-blues); border-left-style: dotted; border-left-width: 100px;'
133
+ );
134
+ } );
84
135
  } );
85
136
 
86
137
  describe( 'getCSSRules', () => {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { upperFirst } from '../styles/utils';
5
+
6
+ describe( 'utils', () => {
7
+ describe( 'upperFirst()', () => {
8
+ it( 'should return an string with a capitalized first letter', () => {
9
+ expect( upperFirst( 'toontown' ) ).toEqual( 'Toontown' );
10
+ } );
11
+ } );
12
+ } );
package/src/types.ts CHANGED
@@ -11,7 +11,32 @@ export type Box< T extends BoxVariants = undefined > = {
11
11
  left?: CSSProperties[ T extends undefined ? 'left' : `${ T }Left` ];
12
12
  };
13
13
 
14
+ export type BorderIndividualProperty = 'top' | 'right' | 'bottom' | 'left';
15
+ // `T` is one of the values in `BorderIndividualProperty`. The expected CSSProperties key is something like `borderTopColor`.
16
+ export type BorderIndividualStyles< T extends BorderIndividualProperty > = {
17
+ color?: CSSProperties[ `border${ Capitalize< string & T > }Color` ];
18
+ style?: CSSProperties[ `border${ Capitalize< string & T > }Style` ];
19
+ width?: CSSProperties[ `border${ Capitalize< string & T > }Width` ];
20
+ };
21
+
14
22
  export interface Style {
23
+ border?: {
24
+ color?: CSSProperties[ 'borderColor' ];
25
+ radius?:
26
+ | CSSProperties[ 'borderRadius' ]
27
+ | {
28
+ topLeft?: CSSProperties[ 'borderTopLeftRadius' ];
29
+ topRight?: CSSProperties[ 'borderTopRightRadius' ];
30
+ bottomLeft?: CSSProperties[ 'borderBottomLeftRadius' ];
31
+ bottomRight?: CSSProperties[ 'borderBottomLeftRadius' ];
32
+ };
33
+ style?: CSSProperties[ 'borderStyle' ];
34
+ width?: CSSProperties[ 'borderWidth' ];
35
+ top?: BorderIndividualStyles< 'top' >;
36
+ right?: BorderIndividualStyles< 'right' >;
37
+ bottom?: BorderIndividualStyles< 'bottom' >;
38
+ left?: BorderIndividualStyles< 'left' >;
39
+ };
15
40
  spacing?: {
16
41
  margin?: CSSProperties[ 'margin' ] | Box< 'margin' >;
17
42
  padding?: CSSProperties[ 'padding' ] | Box< 'padding' >;
@@ -40,6 +65,8 @@ export interface Style {
40
65
  };
41
66
  }
42
67
 
68
+ export type CssRulesKeys = { default: string; individual: string };
69
+
43
70
  export type StyleOptions = {
44
71
  /**
45
72
  * CSS selector for the generated style.
@@ -59,6 +86,11 @@ export type GeneratedCSSRule = {
59
86
 
60
87
  export interface StyleDefinition {
61
88
  name: string;
62
- generate?: ( style: Style, options: StyleOptions ) => GeneratedCSSRule[];
89
+ generate?: (
90
+ style: Style,
91
+ options: StyleOptions,
92
+ path?: string[],
93
+ ruleKey?: string
94
+ ) => GeneratedCSSRule[];
63
95
  getClassNames?: ( style: Style ) => string[];
64
96
  }