@wordpress/block-editor 15.6.1-next.36001005c.0 → 15.6.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.
Files changed (163) hide show
  1. package/build/components/background-image-control/index.js +2 -2
  2. package/build/components/background-image-control/index.js.map +2 -2
  3. package/build/components/block-list/block.js +3 -3
  4. package/build/components/block-list/block.js.map +2 -2
  5. package/build/components/block-list/index.js +2 -2
  6. package/build/components/block-list/index.js.map +1 -1
  7. package/build/components/block-quick-navigation/index.js +0 -1
  8. package/build/components/block-quick-navigation/index.js.map +2 -2
  9. package/build/components/global-styles/border-panel.js +1 -2
  10. package/build/components/global-styles/border-panel.js.map +2 -2
  11. package/build/components/global-styles/color-panel.js +1 -2
  12. package/build/components/global-styles/color-panel.js.map +2 -2
  13. package/build/components/global-styles/dimensions-panel.js +2 -3
  14. package/build/components/global-styles/dimensions-panel.js.map +2 -2
  15. package/build/components/global-styles/filters-panel.js +1 -2
  16. package/build/components/global-styles/filters-panel.js.map +2 -2
  17. package/build/components/global-styles/get-block-css-selector.js +78 -0
  18. package/build/components/global-styles/get-block-css-selector.js.map +7 -0
  19. package/build/components/global-styles/hooks.js +95 -23
  20. package/build/components/global-styles/hooks.js.map +2 -2
  21. package/build/components/global-styles/index.js +14 -0
  22. package/build/components/global-styles/index.js.map +2 -2
  23. package/build/components/global-styles/typography-panel.js +19 -3
  24. package/build/components/global-styles/typography-panel.js.map +2 -2
  25. package/build/components/global-styles/typography-utils.js +49 -2
  26. package/build/components/global-styles/typography-utils.js.map +2 -2
  27. package/build/components/global-styles/use-global-styles-output.js +998 -0
  28. package/build/components/global-styles/use-global-styles-output.js.map +7 -0
  29. package/build/components/global-styles/utils.js +377 -0
  30. package/build/components/global-styles/utils.js.map +2 -2
  31. package/build/components/rich-text/index.js +8 -7
  32. package/build/components/rich-text/index.js.map +2 -2
  33. package/build/hooks/block-bindings.js +111 -170
  34. package/build/hooks/block-bindings.js.map +2 -2
  35. package/build/hooks/block-style-variation.js +10 -6
  36. package/build/hooks/block-style-variation.js.map +2 -2
  37. package/build/hooks/custom-class-name.js +1 -1
  38. package/build/hooks/custom-class-name.js.map +1 -1
  39. package/build/hooks/duotone.js +3 -3
  40. package/build/hooks/duotone.js.map +2 -2
  41. package/build/hooks/fit-text.js +31 -18
  42. package/build/hooks/fit-text.js.map +2 -2
  43. package/build/hooks/font-size.js +6 -5
  44. package/build/hooks/font-size.js.map +2 -2
  45. package/build/hooks/metadata.js +48 -2
  46. package/build/hooks/metadata.js.map +2 -2
  47. package/build/hooks/typography.js +11 -4
  48. package/build/hooks/typography.js.map +3 -3
  49. package/build/hooks/use-typography-props.js +2 -2
  50. package/build/hooks/use-typography-props.js.map +2 -2
  51. package/build/store/private-selectors.js +3 -3
  52. package/build/store/private-selectors.js.map +2 -2
  53. package/build/store/selectors.js +38 -13
  54. package/build/store/selectors.js.map +2 -2
  55. package/build/store/utils.js +2 -1
  56. package/build/store/utils.js.map +2 -2
  57. package/build/utils/fit-text-utils.js +4 -4
  58. package/build/utils/fit-text-utils.js.map +2 -2
  59. package/build-module/components/background-image-control/index.js +1 -1
  60. package/build-module/components/background-image-control/index.js.map +2 -2
  61. package/build-module/components/block-list/block.js +3 -3
  62. package/build-module/components/block-list/block.js.map +2 -2
  63. package/build-module/components/block-list/index.js +2 -2
  64. package/build-module/components/block-list/index.js.map +1 -1
  65. package/build-module/components/block-quick-navigation/index.js +0 -1
  66. package/build-module/components/block-quick-navigation/index.js.map +2 -2
  67. package/build-module/components/global-styles/border-panel.js +1 -2
  68. package/build-module/components/global-styles/border-panel.js.map +2 -2
  69. package/build-module/components/global-styles/color-panel.js +1 -2
  70. package/build-module/components/global-styles/color-panel.js.map +2 -2
  71. package/build-module/components/global-styles/dimensions-panel.js +1 -2
  72. package/build-module/components/global-styles/dimensions-panel.js.map +2 -2
  73. package/build-module/components/global-styles/filters-panel.js +1 -2
  74. package/build-module/components/global-styles/filters-panel.js.map +2 -2
  75. package/build-module/components/global-styles/get-block-css-selector.js +54 -0
  76. package/build-module/components/global-styles/get-block-css-selector.js.map +7 -0
  77. package/build-module/components/global-styles/hooks.js +95 -27
  78. package/build-module/components/global-styles/hooks.js.map +2 -2
  79. package/build-module/components/global-styles/index.js +14 -0
  80. package/build-module/components/global-styles/index.js.map +2 -2
  81. package/build-module/components/global-styles/typography-panel.js +19 -3
  82. package/build-module/components/global-styles/typography-panel.js.map +2 -2
  83. package/build-module/components/global-styles/typography-utils.js +49 -1
  84. package/build-module/components/global-styles/typography-utils.js.map +2 -2
  85. package/build-module/components/global-styles/use-global-styles-output.js +979 -0
  86. package/build-module/components/global-styles/use-global-styles-output.js.map +7 -0
  87. package/build-module/components/global-styles/utils.js +364 -0
  88. package/build-module/components/global-styles/utils.js.map +2 -2
  89. package/build-module/components/rich-text/index.js +8 -7
  90. package/build-module/components/rich-text/index.js.map +2 -2
  91. package/build-module/hooks/block-bindings.js +112 -172
  92. package/build-module/hooks/block-bindings.js.map +2 -2
  93. package/build-module/hooks/block-style-variation.js +12 -4
  94. package/build-module/hooks/block-style-variation.js.map +2 -2
  95. package/build-module/hooks/custom-class-name.js +1 -1
  96. package/build-module/hooks/custom-class-name.js.map +1 -1
  97. package/build-module/hooks/duotone.js +3 -3
  98. package/build-module/hooks/duotone.js.map +2 -2
  99. package/build-module/hooks/fit-text.js +32 -19
  100. package/build-module/hooks/fit-text.js.map +2 -2
  101. package/build-module/hooks/font-size.js +5 -4
  102. package/build-module/hooks/font-size.js.map +2 -2
  103. package/build-module/hooks/metadata.js +46 -1
  104. package/build-module/hooks/metadata.js.map +2 -2
  105. package/build-module/hooks/typography.js +11 -4
  106. package/build-module/hooks/typography.js.map +3 -3
  107. package/build-module/hooks/use-typography-props.js +1 -1
  108. package/build-module/hooks/use-typography-props.js.map +2 -2
  109. package/build-module/store/private-selectors.js +2 -2
  110. package/build-module/store/private-selectors.js.map +2 -2
  111. package/build-module/store/selectors.js +39 -14
  112. package/build-module/store/selectors.js.map +2 -2
  113. package/build-module/store/utils.js +3 -2
  114. package/build-module/store/utils.js.map +2 -2
  115. package/build-module/utils/fit-text-utils.js +4 -4
  116. package/build-module/utils/fit-text-utils.js.map +2 -2
  117. package/build-style/style-rtl.css +6 -10
  118. package/build-style/style.css +6 -10
  119. package/package.json +35 -36
  120. package/src/components/background-image-control/index.js +1 -1
  121. package/src/components/block-card/style.scss +1 -1
  122. package/src/components/block-list/block.js +1 -1
  123. package/src/components/block-list/index.js +2 -2
  124. package/src/components/block-navigation/style.scss +1 -1
  125. package/src/components/block-quick-navigation/index.js +0 -1
  126. package/src/components/block-switcher/style.scss +1 -1
  127. package/src/components/color-palette/test/__snapshots__/control.js.snap +1 -1
  128. package/src/components/global-styles/border-panel.js +1 -2
  129. package/src/components/global-styles/color-panel.js +1 -2
  130. package/src/components/global-styles/color-panel.native.js +1 -1
  131. package/src/components/global-styles/dimensions-panel.js +1 -2
  132. package/src/components/global-styles/filters-panel.js +1 -2
  133. package/src/components/global-styles/get-block-css-selector.js +114 -0
  134. package/src/components/global-styles/hooks.js +108 -29
  135. package/src/components/global-styles/index.js +8 -0
  136. package/src/components/global-styles/test/typography-utils.js +806 -0
  137. package/src/components/global-styles/test/use-global-styles-output.js +1131 -0
  138. package/src/components/global-styles/test/utils.js +442 -1
  139. package/src/components/global-styles/typography-panel.js +27 -3
  140. package/src/components/global-styles/typography-utils.js +133 -0
  141. package/src/components/global-styles/use-global-styles-output.js +1487 -0
  142. package/src/components/global-styles/utils.js +537 -0
  143. package/src/components/inserter/style.scss +2 -2
  144. package/src/components/multi-selection-inspector/style.scss +1 -1
  145. package/src/components/rich-text/index.js +8 -14
  146. package/src/hooks/block-bindings.js +79 -153
  147. package/src/hooks/block-style-variation.js +12 -4
  148. package/src/hooks/custom-class-name.js +1 -1
  149. package/src/hooks/duotone.js +3 -3
  150. package/src/hooks/fit-text.js +37 -28
  151. package/src/hooks/font-size.js +8 -4
  152. package/src/hooks/metadata.js +89 -0
  153. package/src/hooks/test/metadata.js +316 -0
  154. package/src/hooks/typography.js +15 -4
  155. package/src/hooks/use-typography-props.js +1 -1
  156. package/src/store/private-selectors.js +2 -2
  157. package/src/store/selectors.js +59 -21
  158. package/src/store/test/selectors.js +1 -1
  159. package/src/store/utils.js +2 -1
  160. package/src/style.scss +0 -1
  161. package/src/utils/fit-text-utils.js +4 -16
  162. package/tsconfig.json +0 -1
  163. package/src/components/block-quick-navigation/style.scss +0 -5
@@ -1,9 +1,258 @@
1
1
  /**
2
2
  * Internal dependencies
3
3
  */
4
- import { areGlobalStyleConfigsEqual } from '../utils';
4
+ import {
5
+ areGlobalStyleConfigsEqual,
6
+ getBlockStyleVariationSelector,
7
+ getPresetVariableFromValue,
8
+ getValueFromVariable,
9
+ scopeFeatureSelectors,
10
+ getResolvedThemeFilePath,
11
+ getResolvedRefValue,
12
+ getResolvedValue,
13
+ } from '../utils';
5
14
 
6
15
  describe( 'editor utils', () => {
16
+ const themeJson = {
17
+ version: 1,
18
+ settings: {
19
+ color: {
20
+ palette: {
21
+ theme: [
22
+ {
23
+ slug: 'primary',
24
+ color: '#007cba',
25
+ name: 'Primary',
26
+ },
27
+ {
28
+ slug: 'secondary',
29
+ color: '#006ba1',
30
+ name: 'Secondary',
31
+ },
32
+ ],
33
+ custom: [
34
+ {
35
+ slug: 'primary',
36
+ color: '#007cba',
37
+ name: 'Primary',
38
+ },
39
+ {
40
+ slug: 'secondary',
41
+ color: '#a65555',
42
+ name: 'Secondary',
43
+ },
44
+ ],
45
+ },
46
+ custom: true,
47
+ customDuotone: true,
48
+ customGradient: true,
49
+ link: true,
50
+ },
51
+ custom: {
52
+ color: {
53
+ primary: 'var(--wp--preset--color--primary)',
54
+ secondary: 'var(--wp--preset--color--secondary)',
55
+ },
56
+ },
57
+ },
58
+ styles: {
59
+ background: {
60
+ backgroundImage: {
61
+ url: 'file:./assets/image.jpg',
62
+ },
63
+ backgroundAttachment: 'fixed',
64
+ backgroundPosition: 'top left',
65
+ },
66
+ blocks: {
67
+ 'core/group': {
68
+ background: {
69
+ backgroundImage: {
70
+ ref: 'styles.background.backgroundImage',
71
+ },
72
+ },
73
+ dimensions: {
74
+ minHeight: '100px',
75
+ },
76
+ spacing: {
77
+ padding: {
78
+ top: 0,
79
+ },
80
+ },
81
+ },
82
+ },
83
+ },
84
+ _links: {
85
+ 'wp:theme-file': [
86
+ {
87
+ name: 'file:./assets/image.jpg',
88
+ href: 'https://wordpress.org/assets/image.jpg',
89
+ target: 'styles.background.backgroundImage.url',
90
+ },
91
+ {
92
+ name: 'file:./assets/other/image.jpg',
93
+ href: 'https://wordpress.org/assets/other/image.jpg',
94
+ target: "styles.blocks.['core/group'].background.backgroundImage.url",
95
+ },
96
+ ],
97
+ },
98
+ isGlobalStylesUserThemeJSON: true,
99
+ };
100
+
101
+ describe( 'getPresetVariableFromValue', () => {
102
+ const context = 'root';
103
+ const propertyName = 'color.text';
104
+ const value = '#007cba';
105
+
106
+ describe( 'when a provided global style (e.g. fontFamily, color,etc.) does not exist', () => {
107
+ it( 'returns the originally provided value', () => {
108
+ const actual = getPresetVariableFromValue(
109
+ themeJson.settings,
110
+ context,
111
+ 'fakePropertyName',
112
+ value
113
+ );
114
+ expect( actual ).toBe( value );
115
+ } );
116
+ } );
117
+
118
+ describe( 'when a global style is cleared by the user', () => {
119
+ it( 'returns an undefined preset variable', () => {
120
+ const actual = getPresetVariableFromValue(
121
+ themeJson.settings,
122
+ context,
123
+ propertyName,
124
+ undefined
125
+ );
126
+ expect( actual ).toBe( undefined );
127
+ } );
128
+ } );
129
+
130
+ describe( 'when a global style is selected by the user', () => {
131
+ describe( 'and it is not a preset value (e.g. custom color)', () => {
132
+ it( 'returns the originally provided value', () => {
133
+ const customValue = '#6e4545';
134
+ const actual = getPresetVariableFromValue(
135
+ themeJson.settings,
136
+ context,
137
+ propertyName,
138
+ customValue
139
+ );
140
+ expect( actual ).toBe( customValue );
141
+ } );
142
+ } );
143
+
144
+ describe( 'and it is a preset value', () => {
145
+ it( 'returns the preset variable', () => {
146
+ const actual = getPresetVariableFromValue(
147
+ themeJson.settings,
148
+ context,
149
+ propertyName,
150
+ value
151
+ );
152
+ expect( actual ).toBe( 'var:preset|color|primary' );
153
+ } );
154
+ } );
155
+ } );
156
+ } );
157
+
158
+ describe( 'getValueFromVariable', () => {
159
+ describe( 'when provided an invalid variable', () => {
160
+ it( 'returns the originally provided value', () => {
161
+ const actual = getValueFromVariable(
162
+ themeJson,
163
+ 'root',
164
+ undefined
165
+ );
166
+
167
+ expect( actual ).toBe( undefined );
168
+ } );
169
+ } );
170
+
171
+ describe( 'when provided a preset variable', () => {
172
+ it( 'retrieves the correct preset value', () => {
173
+ const actual = getValueFromVariable(
174
+ themeJson,
175
+ 'root',
176
+ 'var:preset|color|primary'
177
+ );
178
+
179
+ expect( actual ).toBe( '#007cba' );
180
+ } );
181
+ } );
182
+
183
+ describe( 'when provided a custom variable', () => {
184
+ it( 'retrieves the correct custom value', () => {
185
+ const actual = getValueFromVariable(
186
+ themeJson,
187
+ 'root',
188
+ 'var(--wp--custom--color--secondary)'
189
+ );
190
+
191
+ expect( actual ).toBe( '#a65555' );
192
+ } );
193
+ } );
194
+
195
+ describe( 'when provided a dynamic reference', () => {
196
+ it( 'retrieves the referenced value', () => {
197
+ const stylesWithRefs = {
198
+ ...themeJson,
199
+ styles: {
200
+ color: {
201
+ background: {
202
+ ref: 'styles.color.text',
203
+ },
204
+ text: 'purple-rain',
205
+ },
206
+ },
207
+ };
208
+ const actual = getValueFromVariable( stylesWithRefs, 'root', {
209
+ ref: 'styles.color.text',
210
+ } );
211
+
212
+ expect( actual ).toBe( stylesWithRefs.styles.color.text );
213
+ } );
214
+
215
+ it( 'returns the originally provided value where value is dynamic reference and reference does not exist', () => {
216
+ const stylesWithRefs = {
217
+ ...themeJson,
218
+ styles: {
219
+ color: {
220
+ text: {
221
+ ref: 'styles.background.text',
222
+ },
223
+ },
224
+ },
225
+ };
226
+ const actual = getValueFromVariable( stylesWithRefs, 'root', {
227
+ ref: 'styles.color.text',
228
+ } );
229
+
230
+ expect( actual ).toBe( stylesWithRefs.styles.color.text );
231
+ } );
232
+
233
+ it( 'returns the originally provided value where value is dynamic reference', () => {
234
+ const stylesWithRefs = {
235
+ ...themeJson,
236
+ styles: {
237
+ color: {
238
+ background: {
239
+ ref: 'styles.color.text',
240
+ },
241
+ text: {
242
+ ref: 'styles.background.text',
243
+ },
244
+ },
245
+ },
246
+ };
247
+ const actual = getValueFromVariable( stylesWithRefs, 'root', {
248
+ ref: 'styles.color.text',
249
+ } );
250
+
251
+ expect( actual ).toBe( stylesWithRefs.styles.color.text );
252
+ } );
253
+ } );
254
+ } );
255
+
7
256
  describe( 'areGlobalStyleConfigsEqual', () => {
8
257
  test.each( [
9
258
  { original: null, variation: null, expected: true },
@@ -55,4 +304,196 @@ describe( 'editor utils', () => {
55
304
  }
56
305
  );
57
306
  } );
307
+
308
+ describe( 'getBlockStyleVariationSelector', () => {
309
+ test.each( [
310
+ { type: 'empty', selector: '', expected: '.is-style-custom' },
311
+ {
312
+ type: 'class',
313
+ selector: '.wp-block',
314
+ expected: '.wp-block.is-style-custom',
315
+ },
316
+ {
317
+ type: 'id',
318
+ selector: '#wp-block',
319
+ expected: '#wp-block.is-style-custom',
320
+ },
321
+ {
322
+ type: 'element tag',
323
+ selector: 'p',
324
+ expected: 'p.is-style-custom',
325
+ },
326
+ {
327
+ type: 'attribute',
328
+ selector: '[style*="color"]',
329
+ expected: '[style*="color"].is-style-custom',
330
+ },
331
+ {
332
+ type: 'descendant',
333
+ selector: '.wp-block .inner',
334
+ expected: '.wp-block.is-style-custom .inner',
335
+ },
336
+ {
337
+ type: 'comma-separated',
338
+ selector: '.wp-block .inner, .wp-block .alternative',
339
+ expected:
340
+ '.wp-block.is-style-custom .inner, .wp-block.is-style-custom .alternative',
341
+ },
342
+ {
343
+ type: 'pseudo',
344
+ selector: 'div:first-child',
345
+ expected: 'div.is-style-custom:first-child',
346
+ },
347
+ {
348
+ type: ':is',
349
+ selector: '.wp-block:is(.outer .inner:first-child)',
350
+ expected:
351
+ '.wp-block.is-style-custom:is(.outer .inner:first-child)',
352
+ },
353
+ {
354
+ type: ':not',
355
+ selector: '.wp-block:not(.outer .inner:first-child)',
356
+ expected:
357
+ '.wp-block.is-style-custom:not(.outer .inner:first-child)',
358
+ },
359
+ {
360
+ type: ':has',
361
+ selector: '.wp-block:has(.outer .inner:first-child)',
362
+ expected:
363
+ '.wp-block.is-style-custom:has(.outer .inner:first-child)',
364
+ },
365
+ {
366
+ type: ':where',
367
+ selector: '.wp-block:where(.outer .inner:first-child)',
368
+ expected:
369
+ '.wp-block.is-style-custom:where(.outer .inner:first-child)',
370
+ },
371
+ {
372
+ type: 'wrapping :where',
373
+ selector: ':where(.outer .inner:first-child)',
374
+ expected: ':where(.outer.is-style-custom .inner:first-child)',
375
+ },
376
+ {
377
+ type: 'complex',
378
+ selector:
379
+ '.wp:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
380
+ expected:
381
+ '.wp.is-style-custom:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp.is-style-custom:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
382
+ },
383
+ ] )(
384
+ 'should add variation class to ancestor in $type selector',
385
+ ( { selector, expected } ) => {
386
+ expect(
387
+ getBlockStyleVariationSelector( 'custom', selector )
388
+ ).toBe( expected );
389
+ }
390
+ );
391
+ } );
392
+
393
+ describe( 'scopeFeatureSelectors', () => {
394
+ it( 'correctly scopes selectors while maintaining selectors object structure', () => {
395
+ const actual = scopeFeatureSelectors( '.custom, .secondary', {
396
+ color: '.my-block h1',
397
+ typography: {
398
+ root: '.my-block',
399
+ lineHeight: '.my-block h1',
400
+ },
401
+ } );
402
+
403
+ expect( actual ).toEqual( {
404
+ color: '.custom .my-block h1, .secondary .my-block h1',
405
+ typography: {
406
+ root: '.custom .my-block, .secondary .my-block',
407
+ lineHeight: '.custom .my-block h1, .secondary .my-block h1',
408
+ },
409
+ } );
410
+ } );
411
+ } );
412
+
413
+ describe( 'getResolvedThemeFilePath()', () => {
414
+ it.each( [
415
+ [
416
+ 'file:./assets/image.jpg',
417
+ 'https://wordpress.org/assets/image.jpg',
418
+ 'Should return absolute URL if found in themeFileURIs',
419
+ ],
420
+ [
421
+ 'file:./misc/image.jpg',
422
+ 'file:./misc/image.jpg',
423
+ 'Should return value if not found in themeFileURIs',
424
+ ],
425
+ [
426
+ 'https://wordpress.org/assets/image.jpg',
427
+ 'https://wordpress.org/assets/image.jpg',
428
+ 'Should not match absolute URLs',
429
+ ],
430
+ ] )(
431
+ 'Given file %s and return value %s: %s',
432
+ ( file, returnedValue ) => {
433
+ expect(
434
+ getResolvedThemeFilePath(
435
+ file,
436
+ themeJson._links[ 'wp:theme-file' ]
437
+ ) === returnedValue
438
+ ).toBe( true );
439
+ }
440
+ );
441
+ } );
442
+
443
+ describe( 'getResolvedRefValue()', () => {
444
+ it.each( [
445
+ [ 'blue', 'blue', null ],
446
+ [ 0, 0, themeJson ],
447
+ [
448
+ { ref: 'styles.background.backgroundImage' },
449
+ { url: 'file:./assets/image.jpg' },
450
+ themeJson,
451
+ ],
452
+ [
453
+ { ref: 'styles.blocks.core/group.spacing.padding.top' },
454
+ 0,
455
+ themeJson,
456
+ ],
457
+ [
458
+ {
459
+ ref: 'styles.blocks.core/group.background.backgroundImage',
460
+ },
461
+ undefined,
462
+ themeJson,
463
+ ],
464
+ ] )(
465
+ 'Given ruleValue %s return expected value of %s',
466
+ ( ruleValue, returnedValue, tree ) => {
467
+ expect( getResolvedRefValue( ruleValue, tree ) ).toEqual(
468
+ returnedValue
469
+ );
470
+ }
471
+ );
472
+ } );
473
+
474
+ describe( 'getResolvedValue()', () => {
475
+ it.each( [
476
+ [ 'blue', 'blue', null ],
477
+ [ 0, 0, themeJson ],
478
+ [
479
+ { ref: 'styles.background.backgroundImage' },
480
+ { url: 'https://wordpress.org/assets/image.jpg' },
481
+ themeJson,
482
+ ],
483
+ [
484
+ {
485
+ ref: 'styles.blocks.core/group.background.backgroundImage',
486
+ },
487
+ undefined,
488
+ themeJson,
489
+ ],
490
+ ] )(
491
+ 'Given ruleValue %s return expected value of %s',
492
+ ( ruleValue, returnedValue, tree ) => {
493
+ expect( getResolvedValue( ruleValue, tree ) ).toEqual(
494
+ returnedValue
495
+ );
496
+ }
497
+ );
498
+ } );
58
499
  } );
@@ -9,7 +9,6 @@ import {
9
9
  } from '@wordpress/components';
10
10
  import { __ } from '@wordpress/i18n';
11
11
  import { useCallback, useMemo, useEffect } from '@wordpress/element';
12
- import { getValueFromVariable } from '@wordpress/global-styles-engine';
13
12
 
14
13
  /**
15
14
  * Internal dependencies
@@ -22,7 +21,7 @@ import TextAlignmentControl from '../text-alignment-control';
22
21
  import TextTransformControl from '../text-transform-control';
23
22
  import TextDecorationControl from '../text-decoration-control';
24
23
  import WritingModeControl from '../writing-mode-control';
25
- import { useToolsPanelDropdownMenuProps } from './utils';
24
+ import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils';
26
25
  import { setImmutably } from '../../utils/object';
27
26
  import {
28
27
  getMergedFontFamiliesAndFontFamilyFaces,
@@ -213,6 +212,30 @@ export default function TypographyPanel( {
213
212
  const mergedFontSizes = getMergedFontSizes( settings );
214
213
 
215
214
  const fontSize = decodeValue( inheritedValue?.typography?.fontSize );
215
+
216
+ // Extract the slug from the CSS custom property if it exists
217
+ const currentFontSizeSlug = ( () => {
218
+ const rawValue = inheritedValue?.typography?.fontSize;
219
+ if ( ! rawValue || typeof rawValue !== 'string' ) {
220
+ return undefined;
221
+ }
222
+
223
+ // Block supports use `var:preset` format.
224
+ if ( rawValue.startsWith( 'var:preset|font-size|' ) ) {
225
+ return rawValue.replace( 'var:preset|font-size|', '' );
226
+ }
227
+
228
+ // Global styles data uses `var(--wp--preset)` format.
229
+ const cssVarMatch = rawValue.match(
230
+ /^var\(--wp--preset--font-size--([^)]+)\)$/
231
+ );
232
+ if ( cssVarMatch ) {
233
+ return cssVarMatch[ 1 ];
234
+ }
235
+
236
+ return undefined;
237
+ } )();
238
+
216
239
  const setFontSize = ( newValue, metadata ) => {
217
240
  const actualValue = !! metadata?.slug
218
241
  ? `var:preset|font-size|${ metadata?.slug }`
@@ -433,7 +456,8 @@ export default function TypographyPanel( {
433
456
  panelId={ panelId }
434
457
  >
435
458
  <FontSizePicker
436
- value={ fontSize }
459
+ value={ currentFontSizeSlug || fontSize }
460
+ valueMode={ currentFontSizeSlug ? 'slug' : 'literal' }
437
461
  onChange={ setFontSize }
438
462
  fontSizes={ mergedFontSizes }
439
463
  disableCustomFontSizes={ disableCustomFontSizes }
@@ -1,8 +1,141 @@
1
+ /**
2
+ * The fluid utilities must match the backend equivalent.
3
+ * See: gutenberg_get_typography_font_size_value() in lib/block-supports/typography.php
4
+ * ---------------------------------------------------------------
5
+ */
6
+
1
7
  /**
2
8
  * Internal dependencies
3
9
  */
10
+ import {
11
+ getComputedFluidTypographyValue,
12
+ getTypographyValueAndUnit,
13
+ } from '../font-sizes/fluid-utils';
4
14
  import { getFontStylesAndWeights } from '../../utils/get-font-styles-and-weights';
5
15
 
16
+ /**
17
+ * @typedef {Object} FluidPreset
18
+ * @property {string|undefined} max A maximum font size value.
19
+ * @property {?string|undefined} min A minimum font size value.
20
+ */
21
+
22
+ /**
23
+ * @typedef {Object} Preset
24
+ * @property {?string|?number} size A default font size.
25
+ * @property {string} name A font size name, displayed in the UI.
26
+ * @property {string} slug A font size slug
27
+ * @property {boolean|FluidPreset|undefined} fluid Specifies the minimum and maximum font size value of a fluid font size.
28
+ */
29
+
30
+ /**
31
+ * @typedef {Object} TypographySettings
32
+ * @property {?string} minViewportWidth Minimum viewport size from which type will have fluidity. Optional if size is specified.
33
+ * @property {?string} maxViewportWidth Maximum size up to which type will have fluidity. Optional if size is specified.
34
+ * @property {?number} scaleFactor A scale factor to determine how fast a font scales within boundaries. Optional.
35
+ * @property {?number} minFontSizeFactor How much to scale defaultFontSize by to derive minimumFontSize. Optional.
36
+ * @property {?string} minFontSize The smallest a calculated font size may be. Optional.
37
+ */
38
+
39
+ /**
40
+ * Returns a font-size value based on a given font-size preset.
41
+ * Takes into account fluid typography parameters and attempts to return a css formula depending on available, valid values.
42
+ *
43
+ * The Core PHP equivalent is wp_get_typography_font_size_value().
44
+ *
45
+ * @param {Preset} preset
46
+ * @param {Object} settings
47
+ * @param {boolean|TypographySettings} settings.typography.fluid Whether fluid typography is enabled, and, optionally, fluid font size options.
48
+ * @param {?Object} settings.typography.layout Layout options.
49
+ *
50
+ * @return {string|*} A font-size value or the value of preset.size.
51
+ */
52
+ export function getTypographyFontSizeValue( preset, settings ) {
53
+ const { size: defaultSize } = preset;
54
+
55
+ /*
56
+ * Catch falsy values and 0/'0'. Fluid calculations cannot be performed on `0`.
57
+ * Also return early when a preset font size explicitly disables fluid typography with `false`.
58
+ */
59
+ if ( ! defaultSize || '0' === defaultSize || false === preset?.fluid ) {
60
+ return defaultSize;
61
+ }
62
+
63
+ /*
64
+ * Return early when fluid typography is disabled in the settings, and there
65
+ * are no local settings to enable it for the individual preset.
66
+ *
67
+ * If this condition isn't met, either the settings or individual preset settings
68
+ * have enabled fluid typography.
69
+ */
70
+ if (
71
+ ! isFluidTypographyEnabled( settings?.typography ) &&
72
+ ! isFluidTypographyEnabled( preset )
73
+ ) {
74
+ return defaultSize;
75
+ }
76
+
77
+ let fluidTypographySettings =
78
+ getFluidTypographyOptionsFromSettings( settings );
79
+ fluidTypographySettings =
80
+ typeof fluidTypographySettings?.fluid === 'object'
81
+ ? fluidTypographySettings?.fluid
82
+ : {};
83
+
84
+ const fluidFontSizeValue = getComputedFluidTypographyValue( {
85
+ minimumFontSize: preset?.fluid?.min,
86
+ maximumFontSize: preset?.fluid?.max,
87
+ fontSize: defaultSize,
88
+ minimumFontSizeLimit: fluidTypographySettings?.minFontSize,
89
+ maximumViewportWidth: fluidTypographySettings?.maxViewportWidth,
90
+ minimumViewportWidth: fluidTypographySettings?.minViewportWidth,
91
+ } );
92
+
93
+ if ( !! fluidFontSizeValue ) {
94
+ return fluidFontSizeValue;
95
+ }
96
+
97
+ return defaultSize;
98
+ }
99
+
100
+ function isFluidTypographyEnabled( typographySettings ) {
101
+ const fluidSettings = typographySettings?.fluid;
102
+ return (
103
+ true === fluidSettings ||
104
+ ( fluidSettings &&
105
+ typeof fluidSettings === 'object' &&
106
+ Object.keys( fluidSettings ).length > 0 )
107
+ );
108
+ }
109
+
110
+ /**
111
+ * Returns fluid typography settings from theme.json setting object.
112
+ *
113
+ * @param {Object} settings Theme.json settings
114
+ * @param {Object} settings.typography Theme.json typography settings
115
+ * @param {Object} settings.layout Theme.json layout settings
116
+ * @return {TypographySettings} Fluid typography settings
117
+ */
118
+ export function getFluidTypographyOptionsFromSettings( settings ) {
119
+ const typographySettings = settings?.typography;
120
+ const layoutSettings = settings?.layout;
121
+ const defaultMaxViewportWidth = getTypographyValueAndUnit(
122
+ layoutSettings?.wideSize
123
+ )
124
+ ? layoutSettings?.wideSize
125
+ : null;
126
+ return isFluidTypographyEnabled( typographySettings ) &&
127
+ defaultMaxViewportWidth
128
+ ? {
129
+ fluid: {
130
+ maxViewportWidth: defaultMaxViewportWidth,
131
+ ...typographySettings.fluid,
132
+ },
133
+ }
134
+ : {
135
+ fluid: typographySettings?.fluid,
136
+ };
137
+ }
138
+
6
139
  /**
7
140
  * Returns an object of merged font families and the font faces from the selected font family
8
141
  * based on the theme.json settings object and the currently selected font family.