@wordpress/block-editor 16.0.0 → 16.1.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 (368) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +8 -0
  3. package/build/components/alignment-control/ui.cjs +2 -4
  4. package/build/components/alignment-control/ui.cjs.map +2 -2
  5. package/build/components/background-image-control/index.cjs +40 -10
  6. package/build/components/background-image-control/index.cjs.map +2 -2
  7. package/build/components/block-alignment-control/ui.cjs +5 -3
  8. package/build/components/block-alignment-control/ui.cjs.map +2 -2
  9. package/build/components/block-inspector/index.cjs +73 -46
  10. package/build/components/block-inspector/index.cjs.map +2 -2
  11. package/build/components/block-list/block-html.cjs +4 -1
  12. package/build/components/block-list/block-html.cjs.map +2 -2
  13. package/build/components/block-list/use-block-props/index.cjs +5 -2
  14. package/build/components/block-list/use-block-props/index.cjs.map +2 -2
  15. package/build/components/block-list/use-block-props/use-focus-handler.cjs +2 -3
  16. package/build/components/block-list/use-block-props/use-focus-handler.cjs.map +2 -2
  17. package/build/components/block-list/use-block-props/use-register-block-event-handlers.cjs +48 -0
  18. package/build/components/block-list/use-block-props/use-register-block-event-handlers.cjs.map +7 -0
  19. package/build/components/block-parent-selector/index.cjs +2 -2
  20. package/build/components/block-parent-selector/index.cjs.map +2 -2
  21. package/build/components/block-toolbar/switch-section-style.cjs +4 -2
  22. package/build/components/block-toolbar/switch-section-style.cjs.map +3 -3
  23. package/build/components/block-tools/empty-block-inserter.cjs +2 -7
  24. package/build/components/block-tools/empty-block-inserter.cjs.map +2 -2
  25. package/build/components/block-vertical-alignment-control/ui.cjs +3 -5
  26. package/build/components/block-vertical-alignment-control/ui.cjs.map +2 -2
  27. package/build/components/border-radius-control/utils.cjs +8 -58
  28. package/build/components/border-radius-control/utils.cjs.map +2 -2
  29. package/build/components/colors-gradients/control.cjs +5 -3
  30. package/build/components/colors-gradients/control.cjs.map +2 -2
  31. package/build/components/dimension-control/index.cjs +30 -20
  32. package/build/components/dimension-control/index.cjs.map +3 -3
  33. package/build/components/dimensions-tool/aspect-ratio-tool.cjs +9 -2
  34. package/build/components/dimensions-tool/aspect-ratio-tool.cjs.map +3 -3
  35. package/build/components/editor-styles/index.cjs +9 -1
  36. package/build/components/editor-styles/index.cjs.map +2 -2
  37. package/build/components/global-styles/background-panel.cjs +66 -25
  38. package/build/components/global-styles/background-panel.cjs.map +3 -3
  39. package/build/components/global-styles/border-panel.cjs +153 -55
  40. package/build/components/global-styles/border-panel.cjs.map +3 -3
  41. package/build/components/global-styles/color-gradient-dropdown-item.cjs +46 -14
  42. package/build/components/global-styles/color-gradient-dropdown-item.cjs.map +3 -3
  43. package/build/components/global-styles/color-panel.cjs +63 -17
  44. package/build/components/global-styles/color-panel.cjs.map +2 -2
  45. package/build/components/global-styles/dimensions-panel.cjs +242 -64
  46. package/build/components/global-styles/dimensions-panel.cjs.map +3 -3
  47. package/build/components/global-styles/filters-panel.cjs +59 -20
  48. package/build/components/global-styles/filters-panel.cjs.map +3 -3
  49. package/build/components/global-styles/hooks.cjs +4 -1
  50. package/build/components/global-styles/hooks.cjs.map +2 -2
  51. package/build/components/global-styles/inheritance/index.cjs +142 -0
  52. package/build/components/global-styles/inheritance/index.cjs.map +7 -0
  53. package/build/components/global-styles/inherited-value-context.cjs +173 -0
  54. package/build/components/global-styles/inherited-value-context.cjs.map +7 -0
  55. package/build/components/global-styles/shadow-panel-components.cjs +38 -20
  56. package/build/components/global-styles/shadow-panel-components.cjs.map +2 -2
  57. package/build/components/global-styles/state-control.cjs +1 -1
  58. package/build/components/global-styles/state-control.cjs.map +2 -2
  59. package/build/components/global-styles/typography-panel.cjs +271 -78
  60. package/build/components/global-styles/typography-panel.cjs.map +3 -3
  61. package/build/components/inspector-controls-tabs/styles-tab.cjs +10 -1
  62. package/build/components/inspector-controls-tabs/styles-tab.cjs.map +2 -2
  63. package/build/components/line-height-control/index.cjs +1 -1
  64. package/build/components/line-height-control/index.cjs.map +2 -2
  65. package/build/components/link-control/index.cjs +3 -1
  66. package/build/components/link-control/index.cjs.map +2 -2
  67. package/build/components/preset-input-control/index.cjs +2 -1
  68. package/build/components/preset-input-control/index.cjs.map +2 -2
  69. package/build/components/provider/block-refs-provider.cjs +8 -2
  70. package/build/components/provider/block-refs-provider.cjs.map +2 -2
  71. package/build/components/provider/index.cjs +1 -1
  72. package/build/components/provider/index.cjs.map +2 -2
  73. package/build/components/provider/use-block-sync.cjs +18 -6
  74. package/build/components/provider/use-block-sync.cjs.map +2 -2
  75. package/build/components/provider/use-media-upload-settings.cjs +3 -1
  76. package/build/components/provider/use-media-upload-settings.cjs.map +2 -2
  77. package/build/components/rich-text/index.cjs +23 -13
  78. package/build/components/rich-text/index.cjs.map +2 -2
  79. package/build/components/spacing-sizes-control/utils.cjs +6 -19
  80. package/build/components/spacing-sizes-control/utils.cjs.map +2 -2
  81. package/build/components/text-indent-control/index.cjs +8 -2
  82. package/build/components/text-indent-control/index.cjs.map +2 -2
  83. package/build/components/url-input/index.cjs +303 -344
  84. package/build/components/url-input/index.cjs.map +3 -3
  85. package/build/components/writing-flow/index.cjs +2 -0
  86. package/build/components/writing-flow/index.cjs.map +3 -3
  87. package/build/components/writing-flow/use-arrow-nav.cjs +24 -1
  88. package/build/components/writing-flow/use-arrow-nav.cjs.map +2 -2
  89. package/build/components/writing-flow/use-click-selection.cjs +28 -2
  90. package/build/components/writing-flow/use-click-selection.cjs.map +2 -2
  91. package/build/components/writing-flow/use-editable-root-event-handlers.cjs +179 -0
  92. package/build/components/writing-flow/use-editable-root-event-handlers.cjs.map +7 -0
  93. package/build/components/writing-flow/use-editable-root.cjs +5 -42
  94. package/build/components/writing-flow/use-editable-root.cjs.map +2 -2
  95. package/build/components/writing-flow/use-home-end.cjs +1 -1
  96. package/build/components/writing-flow/use-home-end.cjs.map +2 -2
  97. package/build/components/writing-flow/use-selection-observer.cjs +17 -13
  98. package/build/components/writing-flow/use-selection-observer.cjs.map +2 -2
  99. package/build/components/writing-flow/utils.cjs +3 -14
  100. package/build/components/writing-flow/utils.cjs.map +2 -2
  101. package/build/hooks/background.cjs +13 -10
  102. package/build/hooks/background.cjs.map +2 -2
  103. package/build/hooks/block-style-variation.cjs +4 -45
  104. package/build/hooks/block-style-variation.cjs.map +3 -3
  105. package/build/hooks/border.cjs +19 -4
  106. package/build/hooks/border.cjs.map +2 -2
  107. package/build/hooks/custom-css.cjs +6 -2
  108. package/build/hooks/custom-css.cjs.map +2 -2
  109. package/build/hooks/dimensions.cjs +15 -4
  110. package/build/hooks/dimensions.cjs.map +2 -2
  111. package/build/hooks/duotone.cjs +26 -14
  112. package/build/hooks/duotone.cjs.map +3 -3
  113. package/build/hooks/elements.cjs +14 -4
  114. package/build/hooks/elements.cjs.map +2 -2
  115. package/build/hooks/line-height.cjs +1 -2
  116. package/build/hooks/line-height.cjs.map +2 -2
  117. package/build/hooks/typography.cjs +13 -4
  118. package/build/hooks/typography.cjs.map +2 -2
  119. package/build/private-apis.cjs +9 -5
  120. package/build/private-apis.cjs.map +3 -3
  121. package/build/store/actions.cjs +9 -4
  122. package/build/store/actions.cjs.map +2 -2
  123. package/build/store/private-selectors.cjs +28 -1
  124. package/build/store/private-selectors.cjs.map +2 -2
  125. package/build/store/reducer.cjs +6 -6
  126. package/build/store/reducer.cjs.map +2 -2
  127. package/build/store/selectors.cjs +23 -0
  128. package/build/store/selectors.cjs.map +2 -2
  129. package/build/utils/native-undo.cjs +55 -0
  130. package/build/utils/native-undo.cjs.map +7 -0
  131. package/build-module/components/alignment-control/ui.mjs +2 -4
  132. package/build-module/components/alignment-control/ui.mjs.map +2 -2
  133. package/build-module/components/background-image-control/index.mjs +43 -10
  134. package/build-module/components/background-image-control/index.mjs.map +2 -2
  135. package/build-module/components/block-alignment-control/ui.mjs +5 -3
  136. package/build-module/components/block-alignment-control/ui.mjs.map +2 -2
  137. package/build-module/components/block-inspector/index.mjs +74 -46
  138. package/build-module/components/block-inspector/index.mjs.map +2 -2
  139. package/build-module/components/block-list/block-html.mjs +4 -1
  140. package/build-module/components/block-list/block-html.mjs.map +2 -2
  141. package/build-module/components/block-list/use-block-props/index.mjs +5 -2
  142. package/build-module/components/block-list/use-block-props/index.mjs.map +2 -2
  143. package/build-module/components/block-list/use-block-props/use-focus-handler.mjs +2 -3
  144. package/build-module/components/block-list/use-block-props/use-focus-handler.mjs.map +2 -2
  145. package/build-module/components/block-list/use-block-props/use-register-block-event-handlers.mjs +28 -0
  146. package/build-module/components/block-list/use-block-props/use-register-block-event-handlers.mjs.map +7 -0
  147. package/build-module/components/block-parent-selector/index.mjs +2 -2
  148. package/build-module/components/block-parent-selector/index.mjs.map +2 -2
  149. package/build-module/components/block-toolbar/switch-section-style.mjs +4 -2
  150. package/build-module/components/block-toolbar/switch-section-style.mjs.map +2 -2
  151. package/build-module/components/block-tools/empty-block-inserter.mjs +2 -7
  152. package/build-module/components/block-tools/empty-block-inserter.mjs.map +2 -2
  153. package/build-module/components/block-vertical-alignment-control/ui.mjs +4 -6
  154. package/build-module/components/block-vertical-alignment-control/ui.mjs.map +2 -2
  155. package/build-module/components/border-radius-control/utils.mjs +11 -53
  156. package/build-module/components/border-radius-control/utils.mjs.map +2 -2
  157. package/build-module/components/colors-gradients/control.mjs +5 -3
  158. package/build-module/components/colors-gradients/control.mjs.map +2 -2
  159. package/build-module/components/dimension-control/index.mjs +30 -20
  160. package/build-module/components/dimension-control/index.mjs.map +2 -2
  161. package/build-module/components/dimensions-tool/aspect-ratio-tool.mjs +10 -6
  162. package/build-module/components/dimensions-tool/aspect-ratio-tool.mjs.map +2 -2
  163. package/build-module/components/editor-styles/index.mjs +9 -1
  164. package/build-module/components/editor-styles/index.mjs.map +2 -2
  165. package/build-module/components/global-styles/background-panel.mjs +71 -29
  166. package/build-module/components/global-styles/background-panel.mjs.map +2 -2
  167. package/build-module/components/global-styles/border-panel.mjs +157 -56
  168. package/build-module/components/global-styles/border-panel.mjs.map +2 -2
  169. package/build-module/components/global-styles/color-gradient-dropdown-item.mjs +51 -15
  170. package/build-module/components/global-styles/color-gradient-dropdown-item.mjs.map +2 -2
  171. package/build-module/components/global-styles/color-panel.mjs +63 -17
  172. package/build-module/components/global-styles/color-panel.mjs.map +2 -2
  173. package/build-module/components/global-styles/dimensions-panel.mjs +248 -66
  174. package/build-module/components/global-styles/dimensions-panel.mjs.map +2 -2
  175. package/build-module/components/global-styles/filters-panel.mjs +64 -21
  176. package/build-module/components/global-styles/filters-panel.mjs.map +2 -2
  177. package/build-module/components/global-styles/hooks.mjs +4 -1
  178. package/build-module/components/global-styles/hooks.mjs.map +2 -2
  179. package/build-module/components/global-styles/inheritance/index.mjs +108 -0
  180. package/build-module/components/global-styles/inheritance/index.mjs.map +7 -0
  181. package/build-module/components/global-styles/inherited-value-context.mjs +141 -0
  182. package/build-module/components/global-styles/inherited-value-context.mjs.map +7 -0
  183. package/build-module/components/global-styles/shadow-panel-components.mjs +38 -20
  184. package/build-module/components/global-styles/shadow-panel-components.mjs.map +2 -2
  185. package/build-module/components/global-styles/state-control.mjs +1 -1
  186. package/build-module/components/global-styles/state-control.mjs.map +2 -2
  187. package/build-module/components/global-styles/typography-panel.mjs +276 -79
  188. package/build-module/components/global-styles/typography-panel.mjs.map +2 -2
  189. package/build-module/components/inspector-controls-tabs/styles-tab.mjs +6 -1
  190. package/build-module/components/inspector-controls-tabs/styles-tab.mjs.map +2 -2
  191. package/build-module/components/line-height-control/index.mjs +1 -1
  192. package/build-module/components/line-height-control/index.mjs.map +2 -2
  193. package/build-module/components/link-control/index.mjs +4 -2
  194. package/build-module/components/link-control/index.mjs.map +2 -2
  195. package/build-module/components/preset-input-control/index.mjs +2 -1
  196. package/build-module/components/preset-input-control/index.mjs.map +2 -2
  197. package/build-module/components/provider/block-refs-provider.mjs +8 -2
  198. package/build-module/components/provider/block-refs-provider.mjs.map +2 -2
  199. package/build-module/components/provider/index.mjs +1 -1
  200. package/build-module/components/provider/index.mjs.map +2 -2
  201. package/build-module/components/provider/use-block-sync.mjs +18 -6
  202. package/build-module/components/provider/use-block-sync.mjs.map +2 -2
  203. package/build-module/components/provider/use-media-upload-settings.mjs +3 -1
  204. package/build-module/components/provider/use-media-upload-settings.mjs.map +2 -2
  205. package/build-module/components/rich-text/index.mjs +28 -14
  206. package/build-module/components/rich-text/index.mjs.map +2 -2
  207. package/build-module/components/spacing-sizes-control/utils.mjs +6 -18
  208. package/build-module/components/spacing-sizes-control/utils.mjs.map +2 -2
  209. package/build-module/components/text-indent-control/index.mjs +8 -2
  210. package/build-module/components/text-indent-control/index.mjs.map +2 -2
  211. package/build-module/components/url-input/index.mjs +306 -353
  212. package/build-module/components/url-input/index.mjs.map +2 -2
  213. package/build-module/components/writing-flow/index.mjs +2 -0
  214. package/build-module/components/writing-flow/index.mjs.map +2 -2
  215. package/build-module/components/writing-flow/use-arrow-nav.mjs +24 -1
  216. package/build-module/components/writing-flow/use-arrow-nav.mjs.map +2 -2
  217. package/build-module/components/writing-flow/use-click-selection.mjs +28 -2
  218. package/build-module/components/writing-flow/use-click-selection.mjs.map +2 -2
  219. package/build-module/components/writing-flow/use-editable-root-event-handlers.mjs +154 -0
  220. package/build-module/components/writing-flow/use-editable-root-event-handlers.mjs.map +7 -0
  221. package/build-module/components/writing-flow/use-editable-root.mjs +5 -37
  222. package/build-module/components/writing-flow/use-editable-root.mjs.map +2 -2
  223. package/build-module/components/writing-flow/use-home-end.mjs +1 -1
  224. package/build-module/components/writing-flow/use-home-end.mjs.map +2 -2
  225. package/build-module/components/writing-flow/use-selection-observer.mjs +18 -14
  226. package/build-module/components/writing-flow/use-selection-observer.mjs.map +2 -2
  227. package/build-module/components/writing-flow/utils.mjs +2 -11
  228. package/build-module/components/writing-flow/utils.mjs.map +2 -2
  229. package/build-module/hooks/background.mjs +13 -10
  230. package/build-module/hooks/background.mjs.map +2 -2
  231. package/build-module/hooks/block-style-variation.mjs +8 -44
  232. package/build-module/hooks/block-style-variation.mjs.map +2 -2
  233. package/build-module/hooks/border.mjs +19 -4
  234. package/build-module/hooks/border.mjs.map +2 -2
  235. package/build-module/hooks/custom-css.mjs +7 -3
  236. package/build-module/hooks/custom-css.mjs.map +2 -2
  237. package/build-module/hooks/dimensions.mjs +15 -4
  238. package/build-module/hooks/dimensions.mjs.map +2 -2
  239. package/build-module/hooks/duotone.mjs +20 -9
  240. package/build-module/hooks/duotone.mjs.map +2 -2
  241. package/build-module/hooks/elements.mjs +14 -4
  242. package/build-module/hooks/elements.mjs.map +2 -2
  243. package/build-module/hooks/line-height.mjs +1 -2
  244. package/build-module/hooks/line-height.mjs.map +2 -2
  245. package/build-module/hooks/typography.mjs +13 -4
  246. package/build-module/hooks/typography.mjs.map +2 -2
  247. package/build-module/private-apis.mjs +7 -3
  248. package/build-module/private-apis.mjs.map +2 -2
  249. package/build-module/store/actions.mjs +9 -4
  250. package/build-module/store/actions.mjs.map +2 -2
  251. package/build-module/store/private-selectors.mjs +29 -1
  252. package/build-module/store/private-selectors.mjs.map +2 -2
  253. package/build-module/store/reducer.mjs +6 -6
  254. package/build-module/store/reducer.mjs.map +2 -2
  255. package/build-module/store/selectors.mjs +23 -0
  256. package/build-module/store/selectors.mjs.map +2 -2
  257. package/build-module/utils/native-undo.mjs +29 -0
  258. package/build-module/utils/native-undo.mjs.map +7 -0
  259. package/build-style/content-rtl.css +10 -18
  260. package/build-style/content.css +10 -18
  261. package/build-style/style-rtl.css +150 -7
  262. package/build-style/style.css +150 -7
  263. package/package.json +40 -41
  264. package/src/components/alignment-control/test/__snapshots__/index.js.snap +1 -1
  265. package/src/components/alignment-control/ui.js +2 -4
  266. package/src/components/background-image-control/index.js +74 -37
  267. package/src/components/background-image-control/style.scss +13 -4
  268. package/src/components/block-alignment-control/test/__snapshots__/index.js.snap +3 -3
  269. package/src/components/block-alignment-control/test/index.js +1 -1
  270. package/src/components/block-alignment-control/ui.js +4 -2
  271. package/src/components/block-breadcrumb/test/__snapshots__/index.js.snap +1 -1
  272. package/src/components/block-compare/test/__snapshots__/block-view.js.snap +1 -1
  273. package/src/components/block-icon/test/__snapshots__/index.js.snap +1 -1
  274. package/src/components/block-inspector/index.js +60 -31
  275. package/src/components/block-list/block-html.js +5 -0
  276. package/src/components/block-list/content.scss +1 -11
  277. package/src/components/block-list/use-block-props/index.js +5 -1
  278. package/src/components/block-list/use-block-props/use-focus-handler.js +9 -8
  279. package/src/components/block-list/use-block-props/use-register-block-event-handlers.js +45 -0
  280. package/src/components/block-parent-selector/index.js +5 -2
  281. package/src/components/block-toolbar/switch-section-style.js +5 -2
  282. package/src/components/block-tools/empty-block-inserter.js +3 -7
  283. package/src/components/block-vertical-alignment-control/test/__snapshots__/index.js.snap +3 -3
  284. package/src/components/block-vertical-alignment-control/test/index.js +18 -2
  285. package/src/components/block-vertical-alignment-control/ui.js +3 -5
  286. package/src/components/border-radius-control/test/utils.js +0 -41
  287. package/src/components/border-radius-control/utils.js +16 -107
  288. package/src/components/button-block-appender/content.scss +6 -2
  289. package/src/components/color-palette/test/__snapshots__/control.js.snap +1 -1
  290. package/src/components/colors-gradients/control.js +6 -3
  291. package/src/components/default-block-appender/test/__snapshots__/index.js.snap +1 -1
  292. package/src/components/dimension-control/index.js +13 -1
  293. package/src/components/dimensions-tool/aspect-ratio-tool.js +13 -6
  294. package/src/components/editor-styles/index.js +16 -1
  295. package/src/components/global-styles/background-panel.js +107 -34
  296. package/src/components/global-styles/border-panel.js +219 -48
  297. package/src/components/global-styles/color-gradient-dropdown-item.js +93 -28
  298. package/src/components/global-styles/color-panel.js +92 -11
  299. package/src/components/global-styles/dimensions-panel.js +422 -71
  300. package/src/components/global-styles/filters-panel.js +88 -28
  301. package/src/components/global-styles/hooks.js +8 -1
  302. package/src/components/global-styles/inheritance/index.js +204 -0
  303. package/src/components/global-styles/inheritance/style.scss +184 -0
  304. package/src/components/global-styles/inheritance/test/index.js +281 -0
  305. package/src/components/global-styles/inherited-value-context.js +271 -0
  306. package/src/components/global-styles/shadow-panel-components.js +48 -26
  307. package/src/components/global-styles/state-control.js +2 -2
  308. package/src/components/global-styles/test/background-panel.js +338 -1
  309. package/src/components/global-styles/test/border-panel.js +441 -0
  310. package/src/components/global-styles/test/color-panel.js +182 -2
  311. package/src/components/global-styles/test/dimensions-panel.js +753 -0
  312. package/src/components/global-styles/test/filters-panel.js +194 -0
  313. package/src/components/global-styles/test/inherited-value-context-core.js +98 -0
  314. package/src/components/global-styles/test/inherited-value-context.js +454 -0
  315. package/src/components/global-styles/test/typography-panel-core.js +185 -0
  316. package/src/components/global-styles/test/typography-panel.js +849 -12
  317. package/src/components/global-styles/typography-panel.js +449 -100
  318. package/src/components/image-editor/style.scss +33 -0
  319. package/src/components/inner-blocks/test/__snapshots__/index.js.snap +1 -1
  320. package/src/components/inspector-controls-tabs/styles-tab.js +5 -1
  321. package/src/components/line-height-control/index.js +1 -1
  322. package/src/components/link-control/index.js +7 -2
  323. package/src/components/preset-input-control/index.js +4 -1
  324. package/src/components/provider/block-refs-provider.js +12 -2
  325. package/src/components/provider/index.js +2 -2
  326. package/src/components/provider/use-block-sync.js +18 -6
  327. package/src/components/provider/use-media-upload-settings.js +2 -0
  328. package/src/components/rich-text/index.js +37 -16
  329. package/src/components/spacing-sizes-control/test/utils.js +16 -5
  330. package/src/components/spacing-sizes-control/utils.js +11 -28
  331. package/src/components/text-indent-control/index.js +8 -0
  332. package/src/components/url-input/index.js +363 -431
  333. package/src/components/url-input/test/index.js +592 -0
  334. package/src/components/warning/test/__snapshots__/index.js.snap +1 -1
  335. package/src/components/writing-flow/index.js +2 -0
  336. package/src/components/writing-flow/use-arrow-nav.js +51 -1
  337. package/src/components/writing-flow/use-click-selection.js +54 -3
  338. package/src/components/writing-flow/use-editable-root-event-handlers.js +266 -0
  339. package/src/components/writing-flow/use-editable-root.js +7 -77
  340. package/src/components/writing-flow/use-home-end.js +17 -9
  341. package/src/components/writing-flow/use-selection-observer.js +32 -15
  342. package/src/components/writing-flow/utils.js +3 -24
  343. package/src/hooks/background.js +26 -21
  344. package/src/hooks/block-fields/rich-text/styles.scss +1 -1
  345. package/src/hooks/block-style-variation.js +8 -74
  346. package/src/hooks/border.js +19 -4
  347. package/src/hooks/custom-css.js +15 -6
  348. package/src/hooks/dimensions.js +17 -4
  349. package/src/hooks/duotone.js +26 -8
  350. package/src/hooks/elements.js +14 -3
  351. package/src/hooks/line-height.js +0 -1
  352. package/src/hooks/test/inherited-value-wiring.js +411 -0
  353. package/src/hooks/test/preset-round-trip.js +456 -0
  354. package/src/hooks/typography.js +35 -23
  355. package/src/private-apis.js +11 -2
  356. package/src/store/actions.js +10 -4
  357. package/src/store/private-selectors.js +57 -1
  358. package/src/store/reducer.js +9 -9
  359. package/src/store/selectors.js +56 -8
  360. package/src/store/test/private-selectors.js +160 -1
  361. package/src/style.scss +2 -0
  362. package/src/utils/native-undo.js +56 -0
  363. package/build/components/duotone/utils.cjs +0 -87
  364. package/build/components/duotone/utils.cjs.map +0 -7
  365. package/build-module/components/duotone/utils.mjs +0 -59
  366. package/build-module/components/duotone/utils.mjs.map +0 -7
  367. package/src/components/duotone/utils.js +0 -90
  368. package/src/hooks/test/get-variation-styles-with-ref-values.js +0 -91
@@ -0,0 +1,454 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { render, screen } from '@testing-library/react';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useSelect } from '@wordpress/data';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import { useResolvedStyle } from '../inherited-value-context';
15
+ import { BlockContextProvider } from '../../block-context';
16
+
17
+ import { globalStylesDataKey } from '../../../store/private-keys';
18
+
19
+ // The inheritance treatment sits behind the
20
+ // `gutenberg-global-styles-inheritance-ui` experiment. Turn it on so these
21
+ // tests exercise the resolving path. The off path lives in
22
+ // `inherited-value-context-core.js`.
23
+ beforeEach( () => {
24
+ window.__experimentalGlobalStylesInheritanceUI = true;
25
+ } );
26
+
27
+ afterEach( () => {
28
+ delete window.__experimentalGlobalStylesInheritanceUI;
29
+ } );
30
+
31
+ jest.mock( '@wordpress/data', () => ( {
32
+ useSelect: jest.fn(),
33
+ useDispatch: jest.fn( () => ( {} ) ),
34
+ useRegistry: jest.fn( () => ( {} ) ),
35
+ createSelector: jest.fn( ( callback ) => callback ),
36
+ createReduxStore: jest.fn(),
37
+ createRegistry: jest.fn(),
38
+ register: jest.fn(),
39
+ select: jest.fn(),
40
+ dispatch: jest.fn(),
41
+ combineReducers: jest.fn( ( reducers ) => reducers ),
42
+ subscribe: jest.fn(),
43
+ RegistryProvider: ( { children } ) => children,
44
+ RegistryConsumer: ( { children } ) => children( {} ),
45
+ AsyncModeProvider: ( { children } ) => children,
46
+ useRegistrySelect: jest.fn(),
47
+ useRegistryDispatch: jest.fn( () => ( {} ) ),
48
+ withSelect: ( mapStateToProps ) => ( Component ) => ( props ) =>
49
+ Component( {
50
+ ...props,
51
+ ...( mapStateToProps?.( () => ( {} ) ) || {} ),
52
+ } ),
53
+ withDispatch: () => ( Component ) => ( props ) => Component( props ),
54
+ withRegistry: ( Component ) => Component,
55
+ } ) );
56
+
57
+ jest.mock( '../../../store', () => ( {
58
+ store: { name: 'core/block-editor' },
59
+ } ) );
60
+
61
+ // `inherited-value-context.js` imports `store as blocksStore` from
62
+ // `@wordpress/blocks` for `useVariationAndElements`. The blocks store's
63
+ // transitive import chain fails under this file's `@wordpress/data` mock
64
+ // (missing `createSelector`), so stub the blocks module with just the shape
65
+ // needed.
66
+ jest.mock( '@wordpress/blocks', () => ( {
67
+ store: { name: 'core/blocks' },
68
+ getBlockType: ( blockName ) =>
69
+ ( {
70
+ 'core/group': { title: 'Group' },
71
+ 'core/heading': { title: 'Heading' },
72
+ 'core/paragraph': { title: 'Paragraph' },
73
+ } )[ blockName ],
74
+ } ) );
75
+
76
+ // `useResolvedStyle` derives the applied variation from the block's
77
+ // `className`; map `is-style-<slug>` to `<slug>` for these tests. The
78
+ // variation-ref resolution itself now lives in `resolveStyle`
79
+ // (`@wordpress/global-styles-engine`) and is covered by its own tests.
80
+ jest.mock( '../../../hooks/block-style-variation', () => ( {
81
+ getVariationNameFromClass: ( className ) => {
82
+ const match = /is-style-([\w-]+)/.exec( className || '' );
83
+ return match ? match[ 1 ] : null;
84
+ },
85
+ } ) );
86
+
87
+ describe( 'useResolvedStyle hook', () => {
88
+ beforeEach( () => {
89
+ useSelect.mockReset();
90
+ } );
91
+
92
+ function Probe( { blockName, className, selectedState } ) {
93
+ const v = useResolvedStyle( blockName, className, selectedState );
94
+ return <div data-testid="probe">{ JSON.stringify( v ) }</div>;
95
+ }
96
+
97
+ // The hook issues two `useSelect` reads: the merged Global Styles
98
+ // payload (block-editor store) and the block's registered styles
99
+ // (blocks store). Feed both from one stub.
100
+ function mockStores(
101
+ rawGlobalStyles,
102
+ { blockStyles = [], attributes = {} } = {}
103
+ ) {
104
+ useSelect.mockImplementation( ( mapSelect ) =>
105
+ mapSelect( () => ( {
106
+ // `globalStylesDataKey` holds the BARE merged styles tree
107
+ // in production (see `editor/src/components/provider/
108
+ // use-block-editor-settings.js:237`,
109
+ // `mergedGlobalStyles.styles`), not the wrapped
110
+ // `{ settings, styles }` envelope. The hook wraps the bare
111
+ // tree before passing to the builder.
112
+ getSettings: () => ( {
113
+ [ globalStylesDataKey ]: rawGlobalStyles,
114
+ } ),
115
+ getBlockStyles: () => blockStyles,
116
+ // The hook reads the block's `level` attribute to pick the
117
+ // heading element layers. No block edit context is provided
118
+ // here, so `clientId` is undefined; the stub returns whatever
119
+ // attributes the test supplies regardless.
120
+ getBlockAttributes: () => attributes,
121
+ } ) )
122
+ );
123
+ }
124
+
125
+ // Resolves a block's styles as rendered inside an Accordion, which supplies
126
+ // the heading level to every descendant through block context.
127
+ function resolveInAccordion( blockName, level = 3 ) {
128
+ render(
129
+ <BlockContextProvider
130
+ value={ { 'core/accordion-heading-level': level } }
131
+ >
132
+ <Probe blockName={ blockName } />
133
+ </BlockContextProvider>
134
+ );
135
+ return JSON.parse( screen.getByTestId( 'probe' ).textContent );
136
+ }
137
+
138
+ // Accordion Heading resolves its level from the parent Accordion's block
139
+ // context, not from its own `level` attribute, and has no level-0 state.
140
+ test( 'accordion-heading takes its level from block context', () => {
141
+ mockStores( {
142
+ elements: {
143
+ heading: { color: { text: '#111111' } },
144
+ h3: { typography: { fontSize: '19px' } },
145
+ },
146
+ } );
147
+ const parsed = resolveInAccordion( 'core/accordion-heading' );
148
+ expect( parsed.value.typography.fontSize ).toBe( '19px' );
149
+ expect( parsed.value.color.text ).toBe( '#111111' );
150
+ } );
151
+
152
+ test( 'accordion-heading with no context still resolves to h3', () => {
153
+ mockStores( {
154
+ elements: { h3: { typography: { fontSize: '19px' } } },
155
+ } );
156
+ render( <Probe blockName="core/accordion-heading" /> );
157
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
158
+ expect( parsed.value.typography.fontSize ).toBe( '19px' );
159
+ } );
160
+
161
+ test( "accordion-heading's own level attribute wins over block context", () => {
162
+ mockStores(
163
+ {
164
+ elements: {
165
+ h3: { typography: { fontSize: '19px' } },
166
+ h5: { typography: { fontSize: '13px' } },
167
+ },
168
+ },
169
+ { attributes: { level: 5 } }
170
+ );
171
+ const parsed = resolveInAccordion( 'core/accordion-heading' );
172
+ // The attribute is what the front end serializes (`save.js` renders
173
+ // `h${ level || 3 }`), so it takes precedence when the two disagree.
174
+ expect( parsed.value.typography.fontSize ).toBe( '13px' );
175
+ } );
176
+
177
+ // An ordinary Heading can be placed inside an Accordion Panel, which puts
178
+ // `core/accordion-heading-level` in its block context. Only the blocks that
179
+ // opt in may read that key.
180
+ test( 'a plain heading ignores an accordion level in block context', () => {
181
+ mockStores(
182
+ {
183
+ elements: {
184
+ h2: { typography: { fontSize: '24px' } },
185
+ h3: { typography: { fontSize: '19px' } },
186
+ },
187
+ },
188
+ { attributes: { level: 2 } }
189
+ );
190
+ const parsed = resolveInAccordion( 'core/heading' );
191
+ expect( parsed.value.typography.fontSize ).toBe( '24px' );
192
+ } );
193
+
194
+ // A title block at level 0 renders a paragraph, so no heading element styles
195
+ // reach it. `0` is the one falsy level, and an `||` anywhere on the path
196
+ // from the `level` attribute to `getElementLayers` would silently turn it
197
+ // into "no level given" and wrongly fold the generic `heading` layer.
198
+ test( 'a title block at level 0 folds no heading element styles', () => {
199
+ mockStores(
200
+ {
201
+ typography: { lineHeight: '1.6' },
202
+ elements: {
203
+ heading: { typography: { fontSize: '30px' } },
204
+ h2: { typography: { fontSize: '24px' } },
205
+ },
206
+ },
207
+ { attributes: { level: 0 } }
208
+ );
209
+ render( <Probe blockName="core/site-title" /> );
210
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
211
+ expect( parsed.value.typography.fontSize ).toBeUndefined();
212
+ // Root styles still resolve, so the assertion above means "no heading
213
+ // layer", not "nothing resolved at all".
214
+ expect( parsed.value.typography.lineHeight ).toBe( '1.6' );
215
+ } );
216
+
217
+ // A whole-block link block (e.g. Read More) renders as an `<a>`, so the root
218
+ // `styles.elements.link` layer paints it and folds into its top-level
219
+ // controls, just as `button` does for core/button.
220
+ test( 'a whole-block link folds the link element into top-level controls', () => {
221
+ mockStores( {
222
+ typography: { lineHeight: '1.6' },
223
+ elements: {
224
+ link: {
225
+ color: { text: '#0073aa' },
226
+ typography: { fontSize: '14px' },
227
+ },
228
+ },
229
+ } );
230
+ render( <Probe blockName="core/read-more" /> );
231
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
232
+ expect( parsed.value.color.text ).toBe( '#0073aa' );
233
+ expect( parsed.value.typography.fontSize ).toBe( '14px' );
234
+ // The link element source is recorded as the element layer.
235
+ expect( parsed.sources[ 'color.text' ].layer ).toBe( 'element' );
236
+ } );
237
+
238
+ // A block that only *contains* links (Paragraph) must not fold `link` into
239
+ // its own text controls; the link styles stay under the `elements.link`
240
+ // passthrough for the Link colour control to read.
241
+ test( 'a container block does not fold the link element into its text controls', () => {
242
+ mockStores( {
243
+ typography: { lineHeight: '1.6' },
244
+ elements: { link: { color: { text: '#0073aa' } } },
245
+ } );
246
+ render( <Probe blockName="core/paragraph" /> );
247
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
248
+ expect( parsed.value.color?.text ).toBeUndefined();
249
+ // It remains available via the passthrough for the Link colour control.
250
+ expect( parsed.value.elements.link.color.text ).toBe( '#0073aa' );
251
+ } );
252
+
253
+ test( 'returns empty value and sources when no block name is given', () => {
254
+ mockStores( { typography: { fontSize: '16px' } } );
255
+ render( <Probe /> );
256
+ expect( screen.getByTestId( 'probe' ) ).toHaveTextContent(
257
+ '{"value":{},"sources":{}}'
258
+ );
259
+ } );
260
+
261
+ test( 'returns empty value and sources during hydration', () => {
262
+ mockStores( null );
263
+ render( <Probe blockName="core/heading" /> );
264
+ expect( screen.getByTestId( 'probe' ) ).toHaveTextContent(
265
+ '{"value":{},"sources":{}}'
266
+ );
267
+ } );
268
+
269
+ test( 'reads the block element passthrough from the merged payload', () => {
270
+ mockStores( {
271
+ typography: { fontSize: '16px' },
272
+ elements: { h2: { typography: { fontSize: '24px' } } },
273
+ } );
274
+ render( <Probe blockName="core/heading" /> );
275
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
276
+ // The h2 element styles are preserved under the nested
277
+ // `elements` passthrough, not folded up to the top level.
278
+ expect( parsed.value.elements.h2.typography.fontSize ).toBe( '24px' );
279
+ expect(
280
+ parsed.sources[ 'elements.h2.typography.fontSize' ].layer
281
+ ).toBe( 'root' );
282
+ } );
283
+ } );
284
+
285
+ /**
286
+ * Regression suite for the production data-shape contract.
287
+ *
288
+ * `settings[ globalStylesDataKey ]` carries the BARE merged Global
289
+ * Styles tree — `{ typography: {...}, color: {...}, blocks: {...},
290
+ * elements: {...}, ... }` — produced by
291
+ * `editor/src/components/provider/use-block-editor-settings.js:237`
292
+ * (`mergedGlobalStyles.styles`). It is NOT the wrapped
293
+ * `{ settings, styles }` envelope.
294
+ *
295
+ * Pre-hot-fix, the hook passed the bare tree directly to
296
+ * `resolveStyle`, which destructures `const { styles } =
297
+ * globalStyles` and so saw `undefined` and early-returned `{}`. Every
298
+ * panel got an empty `inheritedValue`; nothing surfaced in the
299
+ * inspector. None of the original suites caught it because their
300
+ * fixtures all set `[ globalStylesDataKey ]: { styles: { ... } }` —
301
+ * encoding the wrong shape assumption.
302
+ *
303
+ * This suite uses fixtures that mirror the production producer's
304
+ * output and asserts the hook → resolver pipeline yields a
305
+ * non-empty `inheritedValue` end-to-end. A future regression on the
306
+ * data-shape contract — at the producer, the wrapping step inside the
307
+ * hook, or the resolver's destructure — surfaces here in CI.
308
+ */
309
+ describe( 'useResolvedStyle – production bare-tree shape', () => {
310
+ beforeEach( () => {
311
+ useSelect.mockReset();
312
+ } );
313
+
314
+ function Probe( { blockName, className } ) {
315
+ const { value } = useResolvedStyle( blockName, className );
316
+ return <div data-testid="probe">{ JSON.stringify( value ) }</div>;
317
+ }
318
+
319
+ /**
320
+ * Representative bare-tree fixture shaped like a real
321
+ * `mergedGlobalStyles.styles` payload from a child theme that:
322
+ *
323
+ * - Sets a root text color, body background, and root line height.
324
+ * - Overrides `core/heading` with a font-family + weight.
325
+ * - Overrides the `h2` element specifically with a smaller
326
+ * font size and a different color.
327
+ * - Registers a `plain` variation under `core/quote` that
328
+ * drops the border and recolors the text.
329
+ * - Includes a `{ ref: 'styles.color.background' }` envelope on
330
+ * the `core/group` background to verify ref resolution still
331
+ * works end-to-end (ref envelopes resolve against the wrapped
332
+ * `{ styles }` tree). This is block-sourced, so it survives the
333
+ * non-cascading root drop.
334
+ */
335
+ const productionShapeFixture = {
336
+ typography: { lineHeight: '1.6' },
337
+ color: {
338
+ text: '#1a1a1a',
339
+ background: '#ffffff',
340
+ },
341
+ elements: {
342
+ link: { color: { text: '#0073aa' } },
343
+ },
344
+ blocks: {
345
+ 'core/heading': {
346
+ typography: {
347
+ fontFamily: 'serif',
348
+ fontWeight: '600',
349
+ },
350
+ elements: {
351
+ h2: {
352
+ typography: { fontSize: '28px' },
353
+ color: { text: '#444444' },
354
+ },
355
+ },
356
+ },
357
+ 'core/quote': {
358
+ border: { width: '4px', color: '#cccccc' },
359
+ variations: {
360
+ plain: {
361
+ border: { width: '0px' },
362
+ color: { text: '#666666' },
363
+ },
364
+ },
365
+ },
366
+ 'core/group': {
367
+ color: { background: { ref: 'styles.color.background' } },
368
+ },
369
+ },
370
+ };
371
+
372
+ function mountWithFixture( ui, rawGlobalStyles = productionShapeFixture ) {
373
+ useSelect.mockImplementation( ( mapSelect ) =>
374
+ mapSelect( () => ( {
375
+ getSettings: () => ( {
376
+ [ globalStylesDataKey ]: rawGlobalStyles,
377
+ } ),
378
+ getBlockStyles: () => [],
379
+ getBlockAttributes: () => ( {} ),
380
+ } ) )
381
+ );
382
+ return render( ui );
383
+ }
384
+
385
+ test( 'root + block override merge yields non-empty payload at the panel boundary', () => {
386
+ mountWithFixture( <Probe blockName="core/heading" /> );
387
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
388
+ expect( parsed ).toMatchObject( {
389
+ typography: {
390
+ lineHeight: '1.6', // from root layer
391
+ fontFamily: 'serif', // from block override
392
+ fontWeight: '600', // from block override
393
+ },
394
+ color: { text: '#1a1a1a' }, // from root layer
395
+ } );
396
+ } );
397
+
398
+ test( 'block element override is preserved under the elements passthrough', () => {
399
+ mountWithFixture( <Probe blockName="core/heading" /> );
400
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
401
+ // Block-level h2 override stays nested under `elements.h2`.
402
+ expect( parsed.elements.h2.typography.fontSize ).toBe( '28px' );
403
+ expect( parsed.elements.h2.color.text ).toBe( '#444444' );
404
+ // Root + block overrides still merge at the top level.
405
+ expect( parsed.typography.lineHeight ).toBe( '1.6' );
406
+ } );
407
+
408
+ test( 'variation override layers on top of block override', () => {
409
+ mountWithFixture(
410
+ <Probe blockName="core/quote" className="is-style-plain" />
411
+ );
412
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
413
+ expect( parsed.border.width ).toBe( '0px' ); // variation wins
414
+ expect( parsed.border.color ).toBe( '#cccccc' ); // block passthrough
415
+ expect( parsed.color.text ).toBe( '#666666' ); // variation wins
416
+ } );
417
+
418
+ test( '{ ref } envelope resolves against the bare tree at the leaf', () => {
419
+ mountWithFixture( <Probe blockName="core/group" /> );
420
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
421
+ expect( parsed.color.background ).toBe( '#ffffff' );
422
+ } );
423
+
424
+ test( 'block with no overrides still inherits root + element layers', () => {
425
+ mountWithFixture( <Probe blockName="core/paragraph" /> );
426
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
427
+ expect( parsed.typography.lineHeight ).toBe( '1.6' );
428
+ // The root link element styles stay nested under `elements.link`.
429
+ expect( parsed.elements.link.color.text ).toBe( '#0073aa' );
430
+ } );
431
+
432
+ test( 'panel-readable keys are absent when the producer accidentally double-wraps the data', () => {
433
+ // Regression guard against re-introducing the original bug. If
434
+ // a future refactor accidentally double-wraps the data —
435
+ // i.e. stores `{ styles: { typography: ... } }` at the dataKey
436
+ // instead of the bare tree — the Provider's wrapper produces
437
+ // `{ styles: { styles: { typography: ... } } }`, the resolver
438
+ // destructures `styles = { styles: { typography: ... } }`, and
439
+ // `pickLayerRootContribution` treats `styles` as a leaf-bearing
440
+ // key so the merged payload is `{ styles: { typography: ... } }`
441
+ // — non-empty in object terms, but the panel-readable keys
442
+ // (`typography`, `color`, ...) are absent at the top level. The
443
+ // user observes the same thing they did before the hot fix: no
444
+ // inherited values surface in the inspector. This case asserts
445
+ // the panel-readable keys are missing, which is what the panels
446
+ // actually consume.
447
+ mountWithFixture( <Probe blockName="core/heading" />, {
448
+ styles: productionShapeFixture,
449
+ } );
450
+ const parsed = JSON.parse( screen.getByTestId( 'probe' ).textContent );
451
+ expect( parsed.typography ).toBeUndefined();
452
+ expect( parsed.color ).toBeUndefined();
453
+ } );
454
+ } );
@@ -0,0 +1,185 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { render, screen } from '@testing-library/react';
5
+ import { click, render as renderAriakit } from '@ariakit/test/react';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import TypographyPanel from '../typography-panel';
11
+
12
+ // Coverage for `TypographyPanel` with the `gutenberg-global-styles-inheritance-ui`
13
+ // experiment off, which is what WordPress Core gets. Deleted rather than set to
14
+ // `false`, because an experiment that was never turned on leaves the global
15
+ // unset, and `undefined` is the value that fires a receiving component's own
16
+ // default parameter. Setting `false` here would test a state that does not
17
+ // occur.
18
+ beforeEach( () => {
19
+ delete window.__experimentalGlobalStylesInheritanceUI;
20
+ } );
21
+
22
+ afterEach( () => {
23
+ delete window.__experimentalGlobalStylesInheritanceUI;
24
+ } );
25
+
26
+ const baseSettings = {
27
+ typography: {
28
+ lineHeight: true,
29
+ letterSpacing: true,
30
+ },
31
+ };
32
+
33
+ // Two presets with distinct slugs and distinct hex values, enough to drive the
34
+ // text color dropdown.
35
+ const PALETTE_SETTINGS = {
36
+ color: {
37
+ text: true,
38
+ custom: false,
39
+ customGradient: false,
40
+ defaultPalette: false,
41
+ palette: {
42
+ theme: [
43
+ { color: '#0000ff', name: 'Blue', slug: 'blue' },
44
+ { color: '#ff0000', name: 'Red', slug: 'red' },
45
+ ],
46
+ },
47
+ },
48
+ };
49
+
50
+ function renderPanel( props ) {
51
+ return render(
52
+ <TypographyPanel
53
+ value={ {} }
54
+ settings={ baseSettings }
55
+ onChange={ () => {} }
56
+ panelId="test-panel"
57
+ { ...props }
58
+ />
59
+ );
60
+ }
61
+
62
+ const getItem = ( name ) => {
63
+ const control = screen.getByRole( 'spinbutton', { name } );
64
+ // The class hooks sit on the wrapping ToolsPanelItem, which has no role.
65
+ // eslint-disable-next-line testing-library/no-node-access
66
+ return control.closest( '.components-tools-panel-item' );
67
+ };
68
+
69
+ describe( 'TypographyPanel — experiment off', () => {
70
+ // `showInheritanceLabelIndicators` defaults to the experiment flag, so a
71
+ // caller that passes no prop gets no inheritance treatment. The layout
72
+ // className must still come through.
73
+ it( 'applies no inherited treatment by default, even when an inherited value is present', () => {
74
+ renderPanel( {
75
+ value: {},
76
+ inheritedValue: { typography: { lineHeight: '1.5' } },
77
+ } );
78
+
79
+ const lineHeightItem = getItem( /line height/i );
80
+ expect( lineHeightItem ).toHaveClass( 'single-column' );
81
+ expect( lineHeightItem ).not.toHaveClass(
82
+ 'is-inherited-from-global-styles'
83
+ );
84
+ } );
85
+
86
+ it( 'renders no reset dot by default when a local value shadows an inherited one', () => {
87
+ renderPanel( {
88
+ value: { typography: { lineHeight: '2' } },
89
+ inheritedValue: { typography: { lineHeight: '1.5' } },
90
+ } );
91
+
92
+ expect( getItem( /line height/i ) ).not.toHaveClass(
93
+ 'has-local-override-from-global-styles'
94
+ );
95
+ expect(
96
+ screen.queryByRole( 'button', {
97
+ name: 'Reset to inherited value',
98
+ } )
99
+ ).not.toBeInTheDocument();
100
+ } );
101
+
102
+ it( 'renders the default color reset button by default when a local color shadows an inherited one', async () => {
103
+ await renderAriakit(
104
+ <TypographyPanel
105
+ value={ { color: { text: 'var:preset|color|blue' } } }
106
+ inheritedValue={ { color: { text: 'var:preset|color|red' } } }
107
+ settings={ PALETTE_SETTINGS }
108
+ panelId="test"
109
+ onChange={ jest.fn() }
110
+ />
111
+ );
112
+
113
+ expect(
114
+ screen.queryByRole( 'button', {
115
+ name: /reset to inherited value/i,
116
+ } )
117
+ ).not.toBeInTheDocument();
118
+ expect(
119
+ screen.getByRole( 'button', { name: /^reset$/i } )
120
+ ).toBeInTheDocument();
121
+ } );
122
+ } );
123
+
124
+ describe( 'TypographyPanel — experiment off, setTextColor link sync', () => {
125
+ async function pickRed( value, inheritedValue ) {
126
+ const onChange = jest.fn();
127
+ await renderAriakit(
128
+ <TypographyPanel
129
+ value={ value }
130
+ inheritedValue={ inheritedValue }
131
+ settings={ PALETTE_SETTINGS }
132
+ panelId="test"
133
+ onChange={ onChange }
134
+ />
135
+ );
136
+ await click(
137
+ screen.getByRole( 'button', { name: /Color/, expanded: false } )
138
+ );
139
+ // `findAllByRole` waits for the Popover/portal content to appear.
140
+ const swatches = await screen.findAllByRole( 'option' );
141
+ // swatch[0] = 'Blue', swatch[1] = 'Red'
142
+ await click( swatches[ 1 ] );
143
+ return onChange.mock.calls[ 0 ][ 0 ];
144
+ }
145
+
146
+ it( 'leaves an unset link color alone when a text color is already set', async () => {
147
+ // With the experiment on this falls back to the inherited link color
148
+ // and syncs. Off, it compares the inherited text and link colors
149
+ // directly, which on this path is the block's own pair, so a link
150
+ // color that was never set does not start tracking.
151
+ const result = await pickRed( {
152
+ color: { text: 'var:preset|color|blue' },
153
+ } );
154
+
155
+ expect( result?.color?.text ).toBe( 'var:preset|color|red' );
156
+ expect( result?.elements?.link?.color?.text ).toBeUndefined();
157
+ } );
158
+
159
+ it( 'starts a link color tracking when neither is set', async () => {
160
+ // Both sides are undefined, so they compare as matching and the link
161
+ // color starts following the text color.
162
+ const result = await pickRed( {} );
163
+
164
+ expect( result?.color?.text ).toBe( 'var:preset|color|red' );
165
+ expect( result?.elements?.link?.color?.text ).toBe(
166
+ 'var:preset|color|red'
167
+ );
168
+ } );
169
+
170
+ // In Global Styles `value` is the user config and `inheritedValue` the
171
+ // merged one. Comparing `value` would find undefined on both sides.
172
+ it( 'reads the merged config, not the user config, in Global Styles', async () => {
173
+ const result = await pickRed(
174
+ {},
175
+ {
176
+ color: { text: 'var:preset|color|blue' },
177
+ elements: { link: { color: { text: 'var:preset|color|red' } } },
178
+ }
179
+ );
180
+
181
+ expect( result?.color?.text ).toBe( 'var:preset|color|red' );
182
+ // The theme's text and link colors differ, so the link does not track.
183
+ expect( result?.elements?.link?.color?.text ).toBeUndefined();
184
+ } );
185
+ } );