@wordpress/components 40.0.0 → 40.1.1-next.v.202609031004.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 (291) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/build/border-box-control/border-box-control/component.cjs +37 -5
  3. package/build/border-box-control/border-box-control/component.cjs.map +3 -3
  4. package/build/border-box-control/border-box-control/hook.cjs +24 -3
  5. package/build/border-box-control/border-box-control/hook.cjs.map +3 -3
  6. package/build/border-box-control/border-box-control-linked-button/hook.cjs +2 -2
  7. package/build/border-box-control/border-box-control-linked-button/hook.cjs.map +2 -2
  8. package/build/border-box-control/border-box-control-split-controls/hook.cjs +2 -2
  9. package/build/border-box-control/border-box-control-split-controls/hook.cjs.map +2 -2
  10. package/build/border-box-control/border-box-control-visualizer/hook.cjs +2 -2
  11. package/build/border-box-control/border-box-control-visualizer/hook.cjs.map +2 -2
  12. package/build/color-palette/index.cjs +1 -1
  13. package/build/color-palette/index.cjs.map +2 -2
  14. package/build/context/context-system-provider.cjs +2 -5
  15. package/build/context/context-system-provider.cjs.map +3 -3
  16. package/build/context/use-context-system.cjs +7 -2
  17. package/build/context/use-context-system.cjs.map +2 -2
  18. package/build/icon/index.cjs +30 -12
  19. package/build/icon/index.cjs.map +2 -2
  20. package/build/item-group/item/hook.cjs +1 -1
  21. package/build/item-group/item/hook.cjs.map +2 -2
  22. package/build/item-group/item-group/hook.cjs +1 -1
  23. package/build/item-group/item-group/hook.cjs.map +2 -2
  24. package/build/resizable-box/index.cjs.map +1 -1
  25. package/build/snackbar/index.cjs +8 -5
  26. package/build/snackbar/index.cjs.map +2 -2
  27. package/build/tools-panel/tools-panel/hook.cjs +102 -106
  28. package/build/tools-panel/tools-panel/hook.cjs.map +2 -2
  29. package/build/tools-panel/tools-panel-item/hook.cjs +33 -33
  30. package/build/tools-panel/tools-panel-item/hook.cjs.map +2 -2
  31. package/build-module/border-box-control/border-box-control/component.mjs +38 -6
  32. package/build-module/border-box-control/border-box-control/component.mjs.map +2 -2
  33. package/build-module/border-box-control/border-box-control/hook.mjs +14 -3
  34. package/build-module/border-box-control/border-box-control/hook.mjs.map +2 -2
  35. package/build-module/border-box-control/border-box-control-linked-button/hook.mjs +2 -2
  36. package/build-module/border-box-control/border-box-control-linked-button/hook.mjs.map +2 -2
  37. package/build-module/border-box-control/border-box-control-split-controls/hook.mjs +2 -2
  38. package/build-module/border-box-control/border-box-control-split-controls/hook.mjs.map +2 -2
  39. package/build-module/border-box-control/border-box-control-visualizer/hook.mjs +2 -2
  40. package/build-module/border-box-control/border-box-control-visualizer/hook.mjs.map +2 -2
  41. package/build-module/color-palette/index.mjs +1 -1
  42. package/build-module/color-palette/index.mjs.map +2 -2
  43. package/build-module/context/context-system-provider.mjs +2 -5
  44. package/build-module/context/context-system-provider.mjs.map +3 -3
  45. package/build-module/context/use-context-system.mjs +7 -2
  46. package/build-module/context/use-context-system.mjs.map +2 -2
  47. package/build-module/icon/index.mjs +30 -12
  48. package/build-module/icon/index.mjs.map +2 -2
  49. package/build-module/item-group/item/hook.mjs +1 -1
  50. package/build-module/item-group/item/hook.mjs.map +2 -2
  51. package/build-module/item-group/item-group/hook.mjs +1 -1
  52. package/build-module/item-group/item-group/hook.mjs.map +2 -2
  53. package/build-module/resizable-box/index.mjs.map +1 -1
  54. package/build-module/snackbar/index.mjs +8 -5
  55. package/build-module/snackbar/index.mjs.map +2 -2
  56. package/build-module/tools-panel/tools-panel/hook.mjs +103 -107
  57. package/build-module/tools-panel/tools-panel/hook.mjs.map +2 -2
  58. package/build-module/tools-panel/tools-panel-item/hook.mjs +35 -35
  59. package/build-module/tools-panel/tools-panel-item/hook.mjs.map +2 -2
  60. package/build-style/style-rtl.css +7 -35
  61. package/build-style/style.css +7 -35
  62. package/build-types/border-box-control/border-box-control/component.d.ts +4 -0
  63. package/build-types/border-box-control/border-box-control/component.d.ts.map +1 -1
  64. package/build-types/border-box-control/border-box-control/hook.d.ts +4 -2
  65. package/build-types/border-box-control/border-box-control/hook.d.ts.map +1 -1
  66. package/build-types/border-box-control/types.d.ts +6 -2
  67. package/build-types/border-box-control/types.d.ts.map +1 -1
  68. package/build-types/composite/types.d.ts +1 -1
  69. package/build-types/context/context-system-provider.d.ts +8 -6
  70. package/build-types/context/context-system-provider.d.ts.map +1 -1
  71. package/build-types/context/use-context-system.d.ts.map +1 -1
  72. package/build-types/icon/index.d.ts.map +1 -1
  73. package/build-types/popover/types.d.ts +6 -2
  74. package/build-types/popover/types.d.ts.map +1 -1
  75. package/build-types/snackbar/index.d.ts.map +1 -1
  76. package/build-types/tools-panel/tools-panel/hook.d.ts +4 -4
  77. package/build-types/tools-panel/tools-panel/hook.d.ts.map +1 -1
  78. package/build-types/tools-panel/tools-panel-item/hook.d.ts.map +1 -1
  79. package/build-types/tools-panel/types.d.ts +52 -9
  80. package/build-types/tools-panel/types.d.ts.map +1 -1
  81. package/package.json +26 -25
  82. package/src/angle-picker-control/style.module.scss +1 -1
  83. package/src/autocomplete/test/get-autocomplete-match.ts +2 -1
  84. package/src/base-control/stories/index.story.tsx +2 -2
  85. package/src/border-box-control/border-box-control/README.md +5 -3
  86. package/src/border-box-control/border-box-control/component.tsx +83 -15
  87. package/src/border-box-control/border-box-control/hook.ts +19 -1
  88. package/src/border-box-control/stories/index.story.tsx +10 -0
  89. package/src/border-box-control/style.module.scss +34 -3
  90. package/src/border-box-control/test/{index.tsx → index.jsdom.test.tsx} +133 -0
  91. package/src/border-box-control/test/utils.ts +11 -0
  92. package/src/border-box-control/types.ts +6 -2
  93. package/src/border-control/test/{index.js → index.jsdom.test.tsx} +20 -13
  94. package/src/checkbox-control/style.scss +2 -2
  95. package/src/checkbox-control/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +8 -5
  96. package/src/color-palette/index.tsx +1 -1
  97. package/src/composite/README.md +1 -1
  98. package/src/composite/types.ts +1 -1
  99. package/src/context/{context-system-provider.js → context-system-provider.tsx} +19 -5
  100. package/src/context/stories/{ComponentsProvider.stories.jsx → ComponentsProvider.stories.tsx} +2 -2
  101. package/src/context/test/__snapshots__/{context-system-provider.js.snap → context-system-provider.jsdom.test.tsx.snap} +3 -3
  102. package/src/context/test/context-connect.tsx +1 -2
  103. package/src/context/test/{context-system-provider.js → context-system-provider.jsdom.test.tsx} +81 -43
  104. package/src/context/test/wordpress-component.tsx +1 -2
  105. package/src/context/use-context-system.js +13 -4
  106. package/src/custom-gradient-picker/test/serializer.ts +1 -0
  107. package/src/date-time/test/utils.test.ts +9 -0
  108. package/src/draggable/style.module.scss +1 -1
  109. package/src/dropdown/style.module.scss +2 -2
  110. package/src/font-size-picker/test/utils.ts +1 -0
  111. package/src/icon/index.tsx +28 -12
  112. package/src/icon/test/{index.tsx → index.jsdom.test.tsx} +66 -0
  113. package/src/input-control/test/{index.js → index.jsdom.test.tsx} +16 -10
  114. package/src/item-group/style.module.scss +1 -2
  115. package/src/navigator/test/router.ts +1 -0
  116. package/src/notice/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +5 -2
  117. package/src/popover/README.md +2 -2
  118. package/src/popover/test/{index.tsx → index.jsdom.test.tsx} +23 -0
  119. package/src/popover/types.ts +9 -3
  120. package/src/query-controls/test/terms.ts +1 -0
  121. package/src/resizable-box/index.tsx +1 -1
  122. package/src/slot-fill/test/{index.js → index.jsdom.test.tsx} +3 -3
  123. package/src/slot-fill/test/{slot.js → slot.jsdom.test.tsx} +17 -9
  124. package/src/snackbar/index.tsx +10 -6
  125. package/src/snackbar/test/{index.tsx → index.jsdom.test.tsx} +28 -2
  126. package/src/snackbar/test/list.jsdom.test.tsx +99 -0
  127. package/src/surface/style.module.scss +2 -2
  128. package/src/textarea-control/style.module.scss +1 -1
  129. package/src/tip/style.scss +1 -1
  130. package/src/toggle-group-control/toggle-group-control/style.module.scss +1 -1
  131. package/src/tools-panel/test/{index.tsx → index.jsdom.test.tsx} +528 -9
  132. package/src/tools-panel/tools-panel/hook.ts +195 -180
  133. package/src/tools-panel/tools-panel-item/README.md +42 -1
  134. package/src/tools-panel/tools-panel-item/hook.ts +97 -69
  135. package/src/tools-panel/types.ts +54 -9
  136. package/src/truncate/style.module.scss +0 -1
  137. package/src/unit-control/test/utils.ts +1 -0
  138. package/src/utils/hooks/test/{use-controlled-state.js → use-controlled-state.jsdom.test.tsx} +17 -3
  139. package/src/utils/hooks/test/{use-controlled-value.js → use-controlled-value.jsdom.test.tsx} +3 -1
  140. package/src/utils/hooks/test/{use-cx.js → use-cx.jsdom.test.tsx} +3 -1
  141. package/src/utils/test/colors.js +1 -0
  142. package/src/utils/test/{get-node-text.js → get-node-text.tsx} +1 -0
  143. package/src/utils/test/math.js +1 -0
  144. package/src/utils/test/rtl.js +1 -0
  145. package/src/utils/test/space.js +1 -0
  146. package/src/utils/test/strings.js +1 -0
  147. package/src/utils/test/unit-values.js +1 -0
  148. package/src/view/test/{index.js → index.jsdom.test.tsx} +5 -1
  149. package/src/snackbar/test/list.tsx +0 -39
  150. /package/src/alignment-matrix-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  151. /package/src/autocomplete/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  152. /package/src/badge/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  153. /package/src/base-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  154. /package/src/box-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  155. /package/src/button/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  156. /package/src/card/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  157. /package/src/card/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  158. /package/src/checkbox-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  159. /package/src/circular-option-picker/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  160. /package/src/color-indicator/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  161. /package/src/color-indicator/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  162. /package/src/color-palette/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  163. /package/src/color-palette/test/{utils.ts → utils.jsdom.test.ts} +0 -0
  164. /package/src/color-picker/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  165. /package/src/combobox-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  166. /package/src/composite/legacy/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  167. /package/src/composite/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  168. /package/src/confirm-dialog/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  169. /package/src/content-editable-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  170. /package/src/custom-gradient-picker/gradient-bar/test/{utils.ts → utils.jsdom.test.ts} +0 -0
  171. /package/src/custom-gradient-picker/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  172. /package/src/custom-select-control/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  173. /package/src/custom-select-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  174. /package/src/custom-select-control-v2/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  175. /package/src/date-time/date-picker/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  176. /package/src/date-time/date-picker/test/{use-lilius.ts → use-lilius.jsdom.test.ts} +0 -0
  177. /package/src/date-time/date-time/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  178. /package/src/date-time/time-picker/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  179. /package/src/date-time/time-picker/time-input/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  180. /package/src/disabled/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  181. /package/src/divider/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  182. /package/src/dropdown/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  183. /package/src/dropdown-menu/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  184. /package/src/duotone-picker/test/{custom-duotone-bar.tsx → custom-duotone-bar.jsdom.test.tsx} +0 -0
  185. /package/src/duotone-picker/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  186. /package/src/elevation/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  187. /package/src/elevation/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  188. /package/src/external-link/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  189. /package/src/flex/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  190. /package/src/flex/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  191. /package/src/focal-point-picker/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  192. /package/src/focal-point-picker/test/{media.tsx → media.jsdom.test.tsx} +0 -0
  193. /package/src/font-size-picker/test/{font-size-picker-select.tsx → font-size-picker-select.jsdom.test.tsx} +0 -0
  194. /package/src/font-size-picker/test/{font-size-picker-toggle-group.tsx → font-size-picker-toggle-group.jsdom.test.tsx} +0 -0
  195. /package/src/font-size-picker/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  196. /package/src/form-file-upload/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  197. /package/src/form-toggle/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  198. /package/src/form-toggle/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  199. /package/src/form-token-field/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  200. /package/src/gradient-picker/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  201. /package/src/grid/test/{grid.tsx → grid.jsdom.test.tsx} +0 -0
  202. /package/src/guide/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  203. /package/src/h-stack/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  204. /package/src/h-stack/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  205. /package/src/heading/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  206. /package/src/heading/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  207. /package/src/higher-order/with-fallback-styles/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  208. /package/src/higher-order/with-filters/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  209. /package/src/higher-order/with-filters/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  210. /package/src/higher-order/with-focus-outside/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  211. /package/src/higher-order/with-focus-return/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  212. /package/src/higher-order/with-notices/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  213. /package/src/higher-order/with-spoken-messages/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  214. /package/src/input-control/test/__snapshots__/{index.js.snap → index.jsdom.test.tsx.snap} +0 -0
  215. /package/src/isolated-event-container/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  216. /package/src/item-group/test/__snapshots__/{index.js.snap → index.jsdom.test.tsx.snap} +0 -0
  217. /package/src/item-group/test/{index.js → index.jsdom.test.tsx} +0 -0
  218. /package/src/keyboard-shortcuts/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  219. /package/src/menu/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  220. /package/src/menu-group/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  221. /package/src/menu-item/test/__snapshots__/{index.js.snap → index.jsdom.test.tsx.snap} +0 -0
  222. /package/src/menu-item/test/{index.js → index.jsdom.test.tsx} +0 -0
  223. /package/src/modal/test/{aria-helper.ts → aria-helper.jsdom.test.ts} +0 -0
  224. /package/src/modal/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  225. /package/src/navigable-container/test/{navigable-menu.tsx → navigable-menu.jsdom.test.tsx} +0 -0
  226. /package/src/navigable-container/test/{tababble-container.tsx → tababble-container.jsdom.test.tsx} +0 -0
  227. /package/src/navigator/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  228. /package/src/notice/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  229. /package/src/notice/test/{list.tsx → list.jsdom.test.tsx} +0 -0
  230. /package/src/number-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  231. /package/src/palette-edit/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  232. /package/src/panel/test/__snapshots__/{body.tsx.snap → body.jsdom.test.tsx.snap} +0 -0
  233. /package/src/panel/test/__snapshots__/{header.tsx.snap → header.jsdom.test.tsx.snap} +0 -0
  234. /package/src/panel/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  235. /package/src/panel/test/__snapshots__/{row.tsx.snap → row.jsdom.test.tsx.snap} +0 -0
  236. /package/src/panel/test/{body.tsx → body.jsdom.test.tsx} +0 -0
  237. /package/src/panel/test/{header.tsx → header.jsdom.test.tsx} +0 -0
  238. /package/src/panel/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  239. /package/src/panel/test/{row.tsx → row.jsdom.test.tsx} +0 -0
  240. /package/src/placeholder/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  241. /package/src/progress-bar/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  242. /package/src/radio-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  243. /package/src/range-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  244. /package/src/resizable-box/resize-tooltip/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  245. /package/src/resizable-box/resize-tooltip/test/{label.tsx → label.jsdom.test.tsx} +0 -0
  246. /package/src/sandbox/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  247. /package/src/scroll-lock/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  248. /package/src/scrollable/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  249. /package/src/search-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  250. /package/src/select-control/test/__snapshots__/{select-control.tsx.snap → select-control.jsdom.test.tsx.snap} +0 -0
  251. /package/src/select-control/test/{select-control.tsx → select-control.jsdom.test.tsx} +0 -0
  252. /package/src/shortcut/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  253. /package/src/shortcut/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  254. /package/src/slot-fill/test/__snapshots__/{slot.js.snap → slot.jsdom.test.tsx.snap} +0 -0
  255. /package/src/spacer/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  256. /package/src/spacer/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  257. /package/src/style-provider/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  258. /package/src/surface/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  259. /package/src/tab-panel/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  260. /package/src/tabs/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  261. /package/src/text/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  262. /package/src/text/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  263. /package/src/text-control/test/{text-control.tsx → text-control.jsdom.test.tsx} +0 -0
  264. /package/src/text-highlight/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  265. /package/src/toggle-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  266. /package/src/toggle-group-control/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  267. /package/src/toggle-group-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  268. /package/src/toolbar/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  269. /package/src/toolbar/test/{toolbar-group.tsx → toolbar-group.jsdom.test.tsx} +0 -0
  270. /package/src/tooltip/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  271. /package/src/tree-grid/test/__snapshots__/{cell.tsx.snap → cell.jsdom.test.tsx.snap} +0 -0
  272. /package/src/tree-grid/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  273. /package/src/tree-grid/test/__snapshots__/{roving-tab-index-item.tsx.snap → roving-tab-index-item.jsdom.test.tsx.snap} +0 -0
  274. /package/src/tree-grid/test/__snapshots__/{roving-tab-index.tsx.snap → roving-tab-index.jsdom.test.tsx.snap} +0 -0
  275. /package/src/tree-grid/test/__snapshots__/{row.tsx.snap → row.jsdom.test.tsx.snap} +0 -0
  276. /package/src/tree-grid/test/{cell.tsx → cell.jsdom.test.tsx} +0 -0
  277. /package/src/tree-grid/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  278. /package/src/tree-grid/test/{roving-tab-index-item.tsx → roving-tab-index-item.jsdom.test.tsx} +0 -0
  279. /package/src/tree-grid/test/{roving-tab-index.tsx → roving-tab-index.jsdom.test.tsx} +0 -0
  280. /package/src/tree-grid/test/{row.tsx → row.jsdom.test.tsx} +0 -0
  281. /package/src/truncate/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  282. /package/src/unit-control/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  283. /package/src/utils/test/{polymorphic-element.tsx → polymorphic-element.jsdom.test.tsx} +0 -0
  284. /package/src/v-stack/test/__snapshots__/{index.tsx.snap → index.jsdom.test.tsx.snap} +0 -0
  285. /package/src/v-stack/test/{index.tsx → index.jsdom.test.tsx} +0 -0
  286. /package/src/validated-form-controls/test/{content-editable-control.tsx → content-editable-control.jsdom.test.tsx} +0 -0
  287. /package/src/validated-form-controls/test/{custom-select-control.tsx → custom-select-control.jsdom.test.tsx} +0 -0
  288. /package/src/validated-form-controls/test/{input-control.tsx → input-control.jsdom.test.tsx} +0 -0
  289. /package/src/validated-form-controls/test/{range-control.tsx → range-control.jsdom.test.tsx} +0 -0
  290. /package/src/validated-form-controls/test/{textarea-control.tsx → textarea-control.jsdom.test.tsx} +0 -0
  291. /package/src/view/test/__snapshots__/{index.js.snap → index.jsdom.test.tsx.snap} +0 -0
@@ -4,107 +4,68 @@ import {
4
4
  useEffect,
5
5
  useMemo,
6
6
  useReducer,
7
- useRef,
7
+ useState,
8
8
  } from '@wordpress/element';
9
9
  import styles from '../style.module.scss';
10
10
  import type { WordPressComponentProps } from '../../context';
11
11
  import { useContextSystem } from '../../context';
12
12
  import type {
13
- ToolsPanelItem,
13
+ RegisteredToolsPanelItem,
14
14
  ToolsPanelMenuItemKey,
15
15
  ToolsPanelMenuItems,
16
- ToolsPanelMenuItemsConfig,
17
16
  ToolsPanelProps,
18
17
  ResetAllFilter,
19
18
  } from '../types';
20
19
 
21
20
  type PanelItemsState = {
22
- panelItems: ToolsPanelItem[];
21
+ panelItems: RegisteredToolsPanelItem[];
23
22
  menuItemOrder: string[];
24
- menuItems: ToolsPanelMenuItems;
23
+ /**
24
+ * Menu state that can't be read back off the items: an optional item the
25
+ * user has shown, or a default item flagged as customized. Anything absent
26
+ * here falls back to whether the item currently holds a value.
27
+ */
28
+ menuItemValues: Record< string, boolean >;
25
29
  };
26
30
 
27
31
  type PanelItemsAction =
28
- | { type: 'REGISTER_PANEL'; item: ToolsPanelItem }
29
- | { type: 'UNREGISTER_PANEL'; label: string }
32
+ | { type: 'REGISTER_PANEL'; item: RegisteredToolsPanelItem }
30
33
  | {
31
- type: 'UPDATE_VALUE';
32
- group: ToolsPanelMenuItemKey;
34
+ type: 'UNREGISTER_PANEL';
33
35
  label: string;
34
- value: boolean;
36
+ item?: RegisteredToolsPanelItem;
35
37
  }
38
+ | { type: 'UPDATE_VALUE'; label: string; value: boolean }
36
39
  | { type: 'TOGGLE_VALUE'; label: string }
37
40
  | { type: 'RESET_ALL' };
38
41
 
39
- function emptyMenuItems(): ToolsPanelMenuItems {
40
- return { default: {}, optional: {} };
41
- }
42
-
43
42
  function emptyState(): PanelItemsState {
44
- return { panelItems: [], menuItemOrder: [], menuItems: emptyMenuItems() };
43
+ return { panelItems: [], menuItemOrder: [], menuItemValues: {} };
45
44
  }
46
45
 
47
- const generateMenuItems = ( {
48
- panelItems,
49
- shouldReset,
50
- currentMenuItems,
51
- menuItemOrder,
52
- }: ToolsPanelMenuItemsConfig ) => {
53
- const newMenuItems: ToolsPanelMenuItems = emptyMenuItems();
54
- const menuItems: ToolsPanelMenuItems = emptyMenuItems();
55
-
56
- panelItems.forEach( ( { hasValue, isShownByDefault, label } ) => {
57
- const group = isShownByDefault ? 'default' : 'optional';
58
-
59
- // If a menu item for this label has already been flagged as customized
60
- // (for default controls), or toggled on (for optional controls), do not
61
- // overwrite its value as those controls would lose that state.
62
- const existingItemValue = currentMenuItems?.[ group ]?.[ label ];
63
- const value = existingItemValue ? existingItemValue : hasValue();
64
-
65
- newMenuItems[ group ][ label ] = shouldReset ? false : value;
66
- } );
67
-
68
- // Loop the known, previously registered items first to maintain menu order.
69
- menuItemOrder.forEach( ( key ) => {
70
- if ( newMenuItems.default.hasOwnProperty( key ) ) {
71
- menuItems.default[ key ] = newMenuItems.default[ key ];
72
- }
73
-
74
- if ( newMenuItems.optional.hasOwnProperty( key ) ) {
75
- menuItems.optional[ key ] = newMenuItems.optional[ key ];
76
- }
77
- } );
78
-
79
- // Loop newMenuItems object adding any that aren't in the known items order.
80
- Object.keys( newMenuItems.default ).forEach( ( key ) => {
81
- if ( ! menuItems.default.hasOwnProperty( key ) ) {
82
- menuItems.default[ key ] = newMenuItems.default[ key ];
83
- }
84
- } );
85
-
86
- Object.keys( newMenuItems.optional ).forEach( ( key ) => {
87
- if ( ! menuItems.optional.hasOwnProperty( key ) ) {
88
- menuItems.optional[ key ] = newMenuItems.optional[ key ];
89
- }
90
- } );
46
+ // An item is always shown while it has a value. `defaultShown` only opts an
47
+ // optional item in when it has none.
48
+ const getSeedValue = ( item: RegisteredToolsPanelItem ) =>
49
+ item.hasValue() || ( ! item.isShownByDefault && !! item.defaultShown );
91
50
 
92
- return menuItems;
93
- };
51
+ const getMenuGroup = (
52
+ item: RegisteredToolsPanelItem
53
+ ): ToolsPanelMenuItemKey => ( item.isShownByDefault ? 'default' : 'optional' );
94
54
 
95
55
  function panelItemsReducer(
96
- panelItems: ToolsPanelItem[],
56
+ panelItems: RegisteredToolsPanelItem[],
97
57
  action: PanelItemsAction
98
58
  ) {
99
59
  switch ( action.type ) {
100
60
  case 'REGISTER_PANEL': {
101
- const newItems = [ ...panelItems ];
102
- // If an item with this label has already been registered, remove it
103
- // first. This can happen when an item is moved between the default
104
- // and optional groups.
105
- const existingIndex = newItems.findIndex(
106
- ( oldItem ) => oldItem.label === action.item.label
61
+ // An existing registration is replaced rather than appended, so
62
+ // that an item moving between the default and optional groups
63
+ // doesn't end up in the list twice.
64
+ const existingIndex = panelItems.findIndex(
65
+ ( item ) => item.label === action.item.label
107
66
  );
67
+
68
+ const newItems = [ ...panelItems ];
108
69
  if ( existingIndex !== -1 ) {
109
70
  newItems.splice( existingIndex, 1 );
110
71
  }
@@ -115,12 +76,17 @@ function panelItemsReducer(
115
76
  const index = panelItems.findIndex(
116
77
  ( item ) => item.label === action.label
117
78
  );
118
- if ( index !== -1 ) {
119
- const newItems = [ ...panelItems ];
120
- newItems.splice( index, 1 );
121
- return newItems;
79
+ if ( index === -1 ) {
80
+ return panelItems;
122
81
  }
123
- return panelItems;
82
+ // A replacement may already hold this label, in which case this is
83
+ // a late cleanup for a registration that is no longer current.
84
+ if ( action.item && panelItems[ index ] !== action.item ) {
85
+ return panelItems;
86
+ }
87
+ const newItems = [ ...panelItems ];
88
+ newItems.splice( index, 1 );
89
+ return newItems;
124
90
  }
125
91
  default:
126
92
  return panelItems;
@@ -133,12 +99,11 @@ function menuItemOrderReducer(
133
99
  ) {
134
100
  switch ( action.type ) {
135
101
  case 'REGISTER_PANEL': {
136
- // Track the initial order of item registration. This is used for
137
- // maintaining menu item order later.
102
+ // Append-only: an item that unregisters and comes back keeps its
103
+ // original place in the menu.
138
104
  if ( menuItemOrder.includes( action.item.label ) ) {
139
105
  return menuItemOrder;
140
106
  }
141
-
142
107
  return [ ...menuItemOrder, action.item.label ];
143
108
  }
144
109
  default:
@@ -146,76 +111,86 @@ function menuItemOrderReducer(
146
111
  }
147
112
  }
148
113
 
149
- function menuItemsReducer( state: PanelItemsState, action: PanelItemsAction ) {
114
+ function menuItemValuesReducer(
115
+ state: PanelItemsState,
116
+ action: PanelItemsAction
117
+ ) {
118
+ const values = state.menuItemValues;
119
+
150
120
  switch ( action.type ) {
151
- case 'REGISTER_PANEL':
152
- case 'UNREGISTER_PANEL':
153
- // generate new menu items from original `menuItems` and updated `panelItems` and `menuItemOrder`
154
- return generateMenuItems( {
155
- currentMenuItems: state.menuItems,
156
- panelItems: state.panelItems,
157
- menuItemOrder: state.menuItemOrder,
158
- shouldReset: false,
159
- } );
160
- case 'RESET_ALL':
161
- return generateMenuItems( {
162
- panelItems: state.panelItems,
163
- menuItemOrder: state.menuItemOrder,
164
- shouldReset: true,
165
- } );
121
+ case 'REGISTER_PANEL': {
122
+ // A new registration starts from its own value rather than
123
+ // inheriting what an item it replaces recorded under the same
124
+ // label. Clearing here rather than on deregistration covers both
125
+ // orderings, since the two are not guaranteed to arrive in one.
126
+ // Relies on re-registering also re-reporting the value.
127
+ if ( ! values.hasOwnProperty( action.item.label ) ) {
128
+ return values;
129
+ }
130
+ const { [ action.item.label ]: unusedValue, ...rest } = values;
131
+ return rest;
132
+ }
166
133
  case 'UPDATE_VALUE': {
167
- const oldValue = state.menuItems[ action.group ][ action.label ];
168
- if ( action.value === oldValue ) {
169
- return state.menuItems;
134
+ if ( values[ action.label ] === action.value ) {
135
+ return values;
170
136
  }
171
- return {
172
- ...state.menuItems,
173
- [ action.group ]: {
174
- ...state.menuItems[ action.group ],
175
- [ action.label ]: action.value,
176
- },
177
- };
137
+ return { ...values, [ action.label ]: action.value };
178
138
  }
179
139
  case 'TOGGLE_VALUE': {
180
- const currentItem = state.panelItems.find(
181
- ( item ) => item.label === action.label
140
+ const item = state.panelItems.find(
141
+ ( { label } ) => label === action.label
142
+ );
143
+ if ( ! item ) {
144
+ return values;
145
+ }
146
+ const current = values[ action.label ] ?? getSeedValue( item );
147
+ return { ...values, [ action.label ]: ! current };
148
+ }
149
+ case 'RESET_ALL': {
150
+ // Optional items are set to false so they hide right away.
151
+ // Default items are left alone: a reset can leave a value in
152
+ // place, since `onDeselect` and `resetAllFilter` are optional and
153
+ // `resetAll` need not cover every attribute. Each item reports its
154
+ // value again if the reset changed it.
155
+ const hidden = state.panelItems.filter(
156
+ ( item ) =>
157
+ ! item.isShownByDefault && values[ item.label ] !== false
182
158
  );
183
159
 
184
- if ( ! currentItem ) {
185
- return state.menuItems;
160
+ if ( ! hidden.length ) {
161
+ return values;
186
162
  }
187
163
 
188
- const menuGroup = currentItem.isShownByDefault
189
- ? 'default'
190
- : 'optional';
191
-
192
- const newMenuItems = {
193
- ...state.menuItems,
194
- [ menuGroup ]: {
195
- ...state.menuItems[ menuGroup ],
196
- [ action.label ]:
197
- ! state.menuItems[ menuGroup ][ action.label ],
198
- },
199
- };
200
- return newMenuItems;
164
+ const next = { ...values };
165
+ hidden.forEach( ( { label } ) => {
166
+ next[ label ] = false;
167
+ } );
168
+ return next;
201
169
  }
202
-
203
170
  default:
204
- return state.menuItems;
171
+ return values;
205
172
  }
206
173
  }
207
174
 
208
175
  function panelReducer( state: PanelItemsState, action: PanelItemsAction ) {
209
176
  const panelItems = panelItemsReducer( state.panelItems, action );
210
177
  const menuItemOrder = menuItemOrderReducer( state.menuItemOrder, action );
211
- // `menuItemsReducer` is a bit unusual because it generates new state from original `menuItems`
212
- // and the updated `panelItems` and `menuItemOrder`.
213
- const menuItems = menuItemsReducer(
214
- { panelItems, menuItemOrder, menuItems: state.menuItems },
178
+ const menuItemValues = menuItemValuesReducer(
179
+ { ...state, panelItems },
215
180
  action
216
181
  );
217
182
 
218
- return { panelItems, menuItemOrder, menuItems };
183
+ // Items dispatch far more often than they change anything. Holding onto
184
+ // the existing state lets React skip the render.
185
+ if (
186
+ panelItems === state.panelItems &&
187
+ menuItemOrder === state.menuItemOrder &&
188
+ menuItemValues === state.menuItemValues
189
+ ) {
190
+ return state;
191
+ }
192
+
193
+ return { panelItems, menuItemOrder, menuItemValues };
219
194
  }
220
195
 
221
196
  function resetAllFiltersReducer(
@@ -251,45 +226,43 @@ export function useToolsPanel(
251
226
  ...otherProps
252
227
  } = useContextSystem( props, 'ToolsPanel' );
253
228
 
254
- const isResettingRef = useRef( false );
255
- const wasResetting = isResettingRef.current;
229
+ // Marks the render that follows a reset so items can tell a reset apart
230
+ // from the user switching them off. Clearing it from an effect rather than
231
+ // as each control updates keeps the whole reset within one pass; otherwise
232
+ // later controls see it already cleared and reset themselves again from
233
+ // stale data.
234
+ const [ isResetting, setIsResetting ] = useState( false );
256
235
 
257
- // `isResettingRef` is cleared via this hook to effectively batch together
258
- // the resetAll task. Without this, the flag is cleared after the first
259
- // control updates and forces a rerender with subsequent controls then
260
- // believing they need to reset, unfortunately using stale data.
261
236
  useEffect( () => {
262
- if ( wasResetting ) {
263
- isResettingRef.current = false;
237
+ if ( isResetting ) {
238
+ setIsResetting( false );
264
239
  }
265
- }, [ wasResetting ] );
240
+ }, [ isResetting ] );
266
241
 
267
- // Allow panel items to register themselves.
268
- const [ { panelItems, menuItems }, panelDispatch ] = useReducer(
269
- panelReducer,
270
- undefined,
271
- emptyState
272
- );
242
+ const [ { panelItems, menuItemOrder, menuItemValues }, panelDispatch ] =
243
+ useReducer( panelReducer, undefined, emptyState );
273
244
 
274
- const [ resetAllFilters, dispatchResetAllFilters ] = useReducer(
245
+ // Reset all filters registered against the context directly, by consumers
246
+ // that aren't themselves a panel item. Items supply theirs when they
247
+ // register.
248
+ const [ externalResetAllFilters, dispatchResetAllFilters ] = useReducer(
275
249
  resetAllFiltersReducer,
276
250
  []
277
251
  );
278
252
 
279
- const registerPanelItem = useCallback( ( item: ToolsPanelItem ) => {
280
- // Add item to panel items.
281
- panelDispatch( { type: 'REGISTER_PANEL', item } );
282
- }, [] );
253
+ const registerPanelItem = useCallback(
254
+ ( item: RegisteredToolsPanelItem ) => {
255
+ panelDispatch( { type: 'REGISTER_PANEL', item } );
256
+ },
257
+ []
258
+ );
283
259
 
284
- // Panels need to deregister on unmount to avoid orphans in menu state.
285
- // This is an issue when panel items are being injected via SlotFills.
286
- const deregisterPanelItem = useCallback( ( label: string ) => {
287
- // When switching selections between components injecting matching
288
- // controls, e.g. both panels have a "padding" control, the
289
- // deregistration of the first panel doesn't occur until after the
290
- // registration of the next.
291
- panelDispatch( { type: 'UNREGISTER_PANEL', label } );
292
- }, [] );
260
+ const deregisterPanelItem = useCallback(
261
+ ( label: string, item?: RegisteredToolsPanelItem ) => {
262
+ panelDispatch( { type: 'UNREGISTER_PANEL', label, item } );
263
+ },
264
+ []
265
+ );
293
266
 
294
267
  const registerResetAllFilter = useCallback( ( filter: ResetAllFilter ) => {
295
268
  dispatchResetAllFilters( { type: 'REGISTER', filter } );
@@ -302,24 +275,38 @@ export function useToolsPanel(
302
275
  []
303
276
  );
304
277
 
305
- // Updates the status of the panel’s menu items. For default items the
306
- // value represents whether it differs from the default and for optional
307
- // items whether the item is shown.
278
+ // Argument order is unchanged from before this hook derived the menu; only
279
+ // the group argument is gone, since it follows from `isShownByDefault`.
308
280
  const flagItemCustomization = useCallback(
309
- (
310
- value: boolean,
311
- label: string,
312
- group: ToolsPanelMenuItemKey = 'default'
313
- ) => {
314
- panelDispatch( { type: 'UPDATE_VALUE', group, label, value } );
281
+ ( value: boolean, label: string ) => {
282
+ panelDispatch( { type: 'UPDATE_VALUE', label, value } );
315
283
  },
316
284
  []
317
285
  );
318
286
 
319
- // Whether all optional menu items are hidden or not must be tracked
320
- // in order to later determine if the panel display is empty and handle
321
- // conditional display of a plus icon to indicate the presence of further
322
- // menu items.
287
+ // Derived during render so the panel can never paint a half-built menu.
288
+ // See: https://github.com/WordPress/gutenberg/pull/65564
289
+ const menuItems = useMemo( () => {
290
+ const result: ToolsPanelMenuItems = { default: {}, optional: {} };
291
+ const byLabel = new Map(
292
+ panelItems.map( ( item ) => [ item.label, item ] )
293
+ );
294
+
295
+ // `menuItemOrder` holds every registered label, so it alone drives
296
+ // the menu.
297
+ menuItemOrder.forEach( ( label ) => {
298
+ const item = byLabel.get( label );
299
+ if ( ! item ) {
300
+ return;
301
+ }
302
+ result[ getMenuGroup( item ) ][ label ] =
303
+ menuItemValues[ label ] ?? getSeedValue( item );
304
+ } );
305
+
306
+ return result;
307
+ }, [ panelItems, menuItemOrder, menuItemValues ] );
308
+
309
+ // Drives the plus icon and the empty panel styling.
323
310
  const areAllOptionalControlsHidden = useMemo( () => {
324
311
  return (
325
312
  isMenuItemTypeEmpty( menuItems.default ) &&
@@ -338,26 +325,53 @@ export function useToolsPanel(
338
325
  className
339
326
  );
340
327
 
341
- // Toggle the checked state of a menu item which is then used to determine
342
- // display of the item within the panel.
343
- const toggleItem = useCallback( ( label: string ) => {
344
- panelDispatch( { type: 'TOGGLE_VALUE', label } );
345
- }, [] );
328
+ // `onShownChange` is invoked from here rather than in response to the
329
+ // resulting state change, so that it only ever reports an explicit menu
330
+ // action by the user.
331
+ const toggleItem = useCallback(
332
+ ( label: string ) => {
333
+ const currentItem = panelItems.find(
334
+ ( item ) => item.label === label
335
+ );
336
+
337
+ if ( ! currentItem ) {
338
+ return;
339
+ }
340
+
341
+ panelDispatch( { type: 'TOGGLE_VALUE', label } );
342
+
343
+ // Default items stay visible when toggled off, which resets them
344
+ // instead of hiding them. Only optional items have a show or hide
345
+ // transition to report.
346
+ if ( currentItem.isShownByDefault ) {
347
+ return;
348
+ }
349
+
350
+ currentItem.onShownChange?.( ! menuItems.optional[ label ] );
351
+ },
352
+ [ menuItems, panelItems ]
353
+ );
354
+
355
+ const resetAllFilters = useMemo( () => {
356
+ const itemFilters = panelItems
357
+ .map( ( item ) => item.resetAllFilter )
358
+ .filter( ( filter ): filter is ResetAllFilter => !! filter );
359
+
360
+ return [ ...itemFilters, ...externalResetAllFilters ];
361
+ }, [ panelItems, externalResetAllFilters ] );
346
362
 
347
- // Resets display of children and executes resetAll callback if available.
348
363
  const resetAllItems = useCallback( () => {
349
364
  if ( typeof resetAll === 'function' ) {
350
- isResettingRef.current = true;
365
+ setIsResetting( true );
351
366
  resetAll( resetAllFilters );
352
367
  }
353
368
 
354
- // Turn off display of all non-default items.
355
369
  panelDispatch( { type: 'RESET_ALL' } );
356
370
  }, [ resetAllFilters, resetAll ] );
357
371
 
358
- // Assist ItemGroup styling when there are potentially hidden placeholder
359
- // items by identifying first & last items that are toggled on for display.
360
- const getFirstVisibleItemLabel = ( items: ToolsPanelItem[] ) => {
372
+ // Lets `ItemGroup` style the visible ends of the panel when hidden
373
+ // placeholder items sit among the children.
374
+ const getFirstVisibleItemLabel = ( items: RegisteredToolsPanelItem[] ) => {
361
375
  const optionalItems = menuItems.optional || {};
362
376
  const firstItem = items.find(
363
377
  ( item ) => item.isShownByDefault || optionalItems[ item.label ]
@@ -381,7 +395,7 @@ export function useToolsPanel(
381
395
  firstDisplayedItem,
382
396
  flagItemCustomization,
383
397
  hasMenuItems,
384
- isResetting: isResettingRef.current,
398
+ isResetting,
385
399
  lastDisplayedItem,
386
400
  menuItems,
387
401
  panelId,
@@ -397,6 +411,7 @@ export function useToolsPanel(
397
411
  deregisterResetAllFilter,
398
412
  firstDisplayedItem,
399
413
  flagItemCustomization,
414
+ isResetting,
400
415
  lastDisplayedItem,
401
416
  menuItems,
402
417
  panelId,
@@ -16,6 +16,15 @@ for how to use `ToolsPanelItem`.
16
16
 
17
17
  ## Props
18
18
 
19
+ ### `defaultShown`: `boolean`
20
+
21
+ For optional items only, this determines whether the item is shown on first
22
+ render even when `hasValue()` is `false`. An item is always shown while it has
23
+ a value, so this only controls the initial state of items without one.
24
+
25
+ - Required: No
26
+ - Default: `false`
27
+
19
28
  ### `hasValue`: `() => boolean`
20
29
 
21
30
  This is called when building the `ToolsPanel` menu to determine the item's
@@ -36,9 +45,10 @@ panel's menu.
36
45
 
37
46
  The supplied label is dual purpose.
38
47
  It is used as:
48
+
39
49
  1. the human-readable label for the panel's dropdown menu
40
50
  2. a key to locate the corresponding item in the panel's menu context to
41
- determine if the panel item should be displayed.
51
+ determine if the panel item should be displayed.
42
52
 
43
53
  A panel item's `label` should be unique among all items within a single panel.
44
54
 
@@ -57,6 +67,37 @@ A callback to take action when this item is selected in the `ToolsPanel` menu.
57
67
 
58
68
  - Required: No
59
69
 
70
+ ### `onShownChange`: `( isShown: boolean ) => void`
71
+
72
+ A callback executed when the user shows or hides the item via the panel's menu,
73
+ passed `true` when it was shown and `false` when it was hidden.
74
+
75
+ Unlike `onDeselect`, this fires whether or not the item has a value, and only in
76
+ response to an explicit menu action. Visibility changes with another cause do
77
+ not trigger it, such as an item becoming visible because it received a value or
78
+ because `defaultShown` was set, or hiding because `Reset all` ran.
79
+
80
+ Items flagged with `isShownByDefault` are always visible and stay so when
81
+ toggled off, so this is never called for them.
82
+
83
+ - Required: No
84
+
85
+ #### Choosing between `onShownChange`, `onSelect` and `onDeselect`
86
+
87
+ `onSelect` and `onDeselect` are unchanged, so a single menu action may call one
88
+ of them alongside `onShownChange`:
89
+
90
+ | User action | Callbacks called |
91
+ | ---------------------------------------- | -------------------------------------- |
92
+ | Shows an optional item that has no value | `onShownChange( true )`, `onSelect` |
93
+ | Hides an optional item that has no value | `onShownChange( false )` |
94
+ | Hides an optional item that has a value | `onShownChange( false )`, `onDeselect` |
95
+
96
+ Use `onShownChange` to track or persist whether the user wants an item visible,
97
+ and `onDeselect` to reset the control's value. They answer different questions,
98
+ so avoid wiring the same handler to both, which would handle one menu action
99
+ twice.
100
+
60
101
  ### `panelId`: `string | null`
61
102
 
62
103
  Panel items will ensure they are only registering with their intended panel by