@wordpress/components 25.11.1-next.f8d8eceb.0 → 25.13.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 (317) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/build/angle-picker-control/index.js +0 -1
  3. package/build/angle-picker-control/index.js.map +1 -1
  4. package/build/custom-select-control-v2/index.js +87 -0
  5. package/build/custom-select-control-v2/index.js.map +1 -0
  6. package/build/custom-select-control-v2/styles.js +85 -0
  7. package/build/custom-select-control-v2/styles.js.map +1 -0
  8. package/build/custom-select-control-v2/types.js +6 -0
  9. package/build/custom-select-control-v2/types.js.map +1 -0
  10. package/build/disclosure/index.js +37 -7
  11. package/build/disclosure/index.js.map +1 -1
  12. package/build/disclosure/types.js +6 -0
  13. package/build/disclosure/types.js.map +1 -0
  14. package/build/divider/component.js +5 -3
  15. package/build/divider/component.js.map +1 -1
  16. package/build/divider/types.js.map +1 -1
  17. package/build/dropdown-menu-v2-ariakit/index.js +74 -35
  18. package/build/dropdown-menu-v2-ariakit/index.js.map +1 -1
  19. package/build/dropdown-menu-v2-ariakit/styles.js +82 -59
  20. package/build/dropdown-menu-v2-ariakit/styles.js.map +1 -1
  21. package/build/dropdown-menu-v2-ariakit/types.js.map +1 -1
  22. package/build/form-token-field/index.js +6 -2
  23. package/build/form-token-field/index.js.map +1 -1
  24. package/build/form-token-field/token-input.js.map +1 -1
  25. package/build/form-token-field/types.js.map +1 -1
  26. package/build/gradient-picker/index.js +1 -1
  27. package/build/gradient-picker/index.js.map +1 -1
  28. package/build/heading/hook.js +6 -3
  29. package/build/heading/hook.js.map +1 -1
  30. package/build/heading/types.js.map +1 -1
  31. package/build/index.native.js +8 -1
  32. package/build/index.native.js.map +1 -1
  33. package/build/mobile/audio-player/index.native.js +8 -9
  34. package/build/mobile/audio-player/index.native.js.map +1 -1
  35. package/build/mobile/global-styles-context/utils.native.js +44 -3
  36. package/build/mobile/global-styles-context/utils.native.js.map +1 -1
  37. package/build/mobile/utils/alignments.native.js +1 -1
  38. package/build/mobile/utils/alignments.native.js.map +1 -1
  39. package/build/private-apis.js +3 -2
  40. package/build/private-apis.js.map +1 -1
  41. package/build/radio-group/context.js +22 -0
  42. package/build/radio-group/context.js.map +1 -0
  43. package/build/radio-group/index.js +27 -23
  44. package/build/radio-group/index.js.map +1 -1
  45. package/build/radio-group/radio.js +58 -0
  46. package/build/radio-group/radio.js.map +1 -0
  47. package/build/radio-group/types.js +6 -0
  48. package/build/radio-group/types.js.map +1 -0
  49. package/build/slot-fill/bubbles-virtually/slot-fill-provider.js +1 -1
  50. package/build/slot-fill/bubbles-virtually/slot-fill-provider.js.map +1 -1
  51. package/build/slot-fill/types.js.map +1 -1
  52. package/build/tabs/index.js +6 -4
  53. package/build/tabs/index.js.map +1 -1
  54. package/build/tabs/styles.js +14 -7
  55. package/build/tabs/styles.js.map +1 -1
  56. package/build/tabs/tab.js +5 -7
  57. package/build/tabs/tab.js.map +1 -1
  58. package/build/tabs/tablist.js +3 -5
  59. package/build/tabs/tablist.js.map +1 -1
  60. package/build/tabs/tabpanel.js +7 -10
  61. package/build/tabs/tabpanel.js.map +1 -1
  62. package/build/tabs/types.js.map +1 -1
  63. package/build/text/types.js.map +1 -1
  64. package/build/text-control/index.js +5 -1
  65. package/build/text-control/index.js.map +1 -1
  66. package/build/text-control/types.js.map +1 -1
  67. package/build/toggle-group-control/toggle-group-control/component.js +4 -2
  68. package/build/toggle-group-control/toggle-group-control/component.js.map +1 -1
  69. package/build/toggle-group-control/toggle-group-control/styles.js +13 -9
  70. package/build/toggle-group-control/toggle-group-control/styles.js.map +1 -1
  71. package/build/toggle-group-control/types.js.map +1 -1
  72. package/build/tools-panel/tools-panel-item/hook.js +5 -1
  73. package/build/tools-panel/tools-panel-item/hook.js.map +1 -1
  74. package/build-module/angle-picker-control/index.js +0 -1
  75. package/build-module/angle-picker-control/index.js.map +1 -1
  76. package/build-module/custom-select-control-v2/index.js +74 -0
  77. package/build-module/custom-select-control-v2/index.js.map +1 -0
  78. package/build-module/custom-select-control-v2/styles.js +71 -0
  79. package/build-module/custom-select-control-v2/styles.js.map +1 -0
  80. package/build-module/custom-select-control-v2/types.js +2 -0
  81. package/build-module/custom-select-control-v2/types.js.map +1 -0
  82. package/build-module/disclosure/index.js +33 -8
  83. package/build-module/disclosure/index.js.map +1 -1
  84. package/build-module/disclosure/types.js +2 -0
  85. package/build-module/disclosure/types.js.map +1 -0
  86. package/build-module/divider/component.js +3 -3
  87. package/build-module/divider/component.js.map +1 -1
  88. package/build-module/divider/types.js.map +1 -1
  89. package/build-module/dropdown-menu-v2-ariakit/index.js +72 -34
  90. package/build-module/dropdown-menu-v2-ariakit/index.js.map +1 -1
  91. package/build-module/dropdown-menu-v2-ariakit/styles.js +69 -40
  92. package/build-module/dropdown-menu-v2-ariakit/styles.js.map +1 -1
  93. package/build-module/dropdown-menu-v2-ariakit/types.js.map +1 -1
  94. package/build-module/form-token-field/index.js +6 -2
  95. package/build-module/form-token-field/index.js.map +1 -1
  96. package/build-module/form-token-field/token-input.js.map +1 -1
  97. package/build-module/form-token-field/types.js.map +1 -1
  98. package/build-module/gradient-picker/index.js +1 -1
  99. package/build-module/gradient-picker/index.js.map +1 -1
  100. package/build-module/heading/hook.js +6 -3
  101. package/build-module/heading/hook.js.map +1 -1
  102. package/build-module/heading/types.js.map +1 -1
  103. package/build-module/index.native.js +1 -1
  104. package/build-module/index.native.js.map +1 -1
  105. package/build-module/mobile/audio-player/index.native.js +9 -10
  106. package/build-module/mobile/audio-player/index.native.js.map +1 -1
  107. package/build-module/mobile/global-styles-context/utils.native.js +43 -4
  108. package/build-module/mobile/global-styles-context/utils.native.js.map +1 -1
  109. package/build-module/mobile/utils/alignments.native.js +1 -1
  110. package/build-module/mobile/utils/alignments.native.js.map +1 -1
  111. package/build-module/private-apis.js +4 -3
  112. package/build-module/private-apis.js.map +1 -1
  113. package/build-module/radio-group/context.js +14 -0
  114. package/build-module/radio-group/context.js.map +1 -0
  115. package/build-module/radio-group/index.js +24 -23
  116. package/build-module/radio-group/index.js.map +1 -1
  117. package/build-module/radio-group/radio.js +46 -0
  118. package/build-module/radio-group/radio.js.map +1 -0
  119. package/build-module/radio-group/types.js +2 -0
  120. package/build-module/radio-group/types.js.map +1 -0
  121. package/build-module/slot-fill/bubbles-virtually/slot-fill-provider.js +1 -1
  122. package/build-module/slot-fill/bubbles-virtually/slot-fill-provider.js.map +1 -1
  123. package/build-module/slot-fill/types.js.map +1 -1
  124. package/build-module/tabs/index.js +7 -5
  125. package/build-module/tabs/index.js.map +1 -1
  126. package/build-module/tabs/styles.js +11 -5
  127. package/build-module/tabs/styles.js.map +1 -1
  128. package/build-module/tabs/tab.js +7 -9
  129. package/build-module/tabs/tab.js.map +1 -1
  130. package/build-module/tabs/tablist.js +3 -5
  131. package/build-module/tabs/tablist.js.map +1 -1
  132. package/build-module/tabs/tabpanel.js +9 -10
  133. package/build-module/tabs/tabpanel.js.map +1 -1
  134. package/build-module/tabs/types.js.map +1 -1
  135. package/build-module/text/types.js.map +1 -1
  136. package/build-module/text-control/index.js +6 -1
  137. package/build-module/text-control/index.js.map +1 -1
  138. package/build-module/text-control/types.js.map +1 -1
  139. package/build-module/toggle-group-control/toggle-group-control/component.js +4 -2
  140. package/build-module/toggle-group-control/toggle-group-control/component.js.map +1 -1
  141. package/build-module/toggle-group-control/toggle-group-control/styles.js +13 -9
  142. package/build-module/toggle-group-control/toggle-group-control/styles.js.map +1 -1
  143. package/build-module/toggle-group-control/types.js.map +1 -1
  144. package/build-module/tools-panel/tools-panel-item/hook.js +6 -2
  145. package/build-module/tools-panel/tools-panel-item/hook.js.map +1 -1
  146. package/build-style/style-rtl.css +52 -8
  147. package/build-style/style.css +52 -8
  148. package/build-types/angle-picker-control/index.d.ts.map +1 -1
  149. package/build-types/box-control/stories/index.story.d.ts +1944 -0
  150. package/build-types/box-control/stories/index.story.d.ts.map +1 -0
  151. package/build-types/card/card-divider/component.d.ts +1 -1
  152. package/build-types/card/card-divider/hook.d.ts +162 -162
  153. package/build-types/color-palette/styles.d.ts +4 -1
  154. package/build-types/color-palette/styles.d.ts.map +1 -1
  155. package/build-types/custom-select-control-v2/index.d.ts +6 -0
  156. package/build-types/custom-select-control-v2/index.d.ts.map +1 -0
  157. package/build-types/custom-select-control-v2/stories/index.story.d.ts +19 -0
  158. package/build-types/custom-select-control-v2/stories/index.story.d.ts.map +1 -0
  159. package/build-types/custom-select-control-v2/styles.d.ts +47 -0
  160. package/build-types/custom-select-control-v2/styles.d.ts.map +1 -0
  161. package/build-types/custom-select-control-v2/types.d.ts +57 -0
  162. package/build-types/custom-select-control-v2/types.d.ts.map +1 -0
  163. package/build-types/date-time/date/styles.d.ts +4 -1
  164. package/build-types/date-time/date/styles.d.ts.map +1 -1
  165. package/build-types/disclosure/index.d.ts +7 -1
  166. package/build-types/disclosure/index.d.ts.map +1 -1
  167. package/build-types/disclosure/types.d.ts +12 -0
  168. package/build-types/disclosure/types.d.ts.map +1 -0
  169. package/build-types/divider/component.d.ts +5 -1
  170. package/build-types/divider/component.d.ts.map +1 -1
  171. package/build-types/divider/stories/index.story.d.ts.map +1 -1
  172. package/build-types/divider/styles.d.ts +1 -1
  173. package/build-types/divider/types.d.ts +2 -2
  174. package/build-types/divider/types.d.ts.map +1 -1
  175. package/build-types/dropdown-menu-v2-ariakit/index.d.ts +11 -2
  176. package/build-types/dropdown-menu-v2-ariakit/index.d.ts.map +1 -1
  177. package/build-types/dropdown-menu-v2-ariakit/stories/index.story.d.ts.map +1 -1
  178. package/build-types/dropdown-menu-v2-ariakit/styles.d.ts +26 -18
  179. package/build-types/dropdown-menu-v2-ariakit/styles.d.ts.map +1 -1
  180. package/build-types/dropdown-menu-v2-ariakit/types.d.ts +1 -7
  181. package/build-types/dropdown-menu-v2-ariakit/types.d.ts.map +1 -1
  182. package/build-types/form-token-field/index.d.ts.map +1 -1
  183. package/build-types/form-token-field/token-input.d.ts.map +1 -1
  184. package/build-types/form-token-field/types.d.ts +1 -1
  185. package/build-types/form-token-field/types.d.ts.map +1 -1
  186. package/build-types/heading/component.d.ts +4 -1
  187. package/build-types/heading/component.d.ts.map +1 -1
  188. package/build-types/heading/hook.d.ts.map +1 -1
  189. package/build-types/heading/types.d.ts +20 -1
  190. package/build-types/heading/types.d.ts.map +1 -1
  191. package/build-types/navigation/styles/navigation-styles.d.ts +4 -1
  192. package/build-types/navigation/styles/navigation-styles.d.ts.map +1 -1
  193. package/build-types/palette-edit/styles.d.ts +4 -1
  194. package/build-types/palette-edit/styles.d.ts.map +1 -1
  195. package/build-types/private-apis.d.ts.map +1 -1
  196. package/build-types/radio-group/context.d.ts +10 -0
  197. package/build-types/radio-group/context.d.ts.map +1 -0
  198. package/build-types/radio-group/index.d.ts +7 -9
  199. package/build-types/radio-group/index.d.ts.map +1 -1
  200. package/build-types/radio-group/radio.d.ts +8 -0
  201. package/build-types/radio-group/radio.d.ts.map +1 -0
  202. package/build-types/radio-group/stories/index.story.d.ts +14 -0
  203. package/build-types/radio-group/stories/index.story.d.ts.map +1 -0
  204. package/build-types/radio-group/types.d.ts +40 -0
  205. package/build-types/radio-group/types.d.ts.map +1 -0
  206. package/build-types/slot-fill/bubbles-virtually/slot.d.ts +1 -1
  207. package/build-types/slot-fill/types.d.ts +16 -6
  208. package/build-types/slot-fill/types.d.ts.map +1 -1
  209. package/build-types/tabs/index.d.ts +4 -3
  210. package/build-types/tabs/index.d.ts.map +1 -1
  211. package/build-types/tabs/stories/index.story.d.ts.map +1 -1
  212. package/build-types/tabs/styles.d.ts +10 -0
  213. package/build-types/tabs/styles.d.ts.map +1 -1
  214. package/build-types/tabs/tab.d.ts +1 -1
  215. package/build-types/tabs/tab.d.ts.map +1 -1
  216. package/build-types/tabs/tablist.d.ts +1 -1
  217. package/build-types/tabs/tablist.d.ts.map +1 -1
  218. package/build-types/tabs/tabpanel.d.ts +4 -1
  219. package/build-types/tabs/tabpanel.d.ts.map +1 -1
  220. package/build-types/tabs/types.d.ts +7 -31
  221. package/build-types/tabs/types.d.ts.map +1 -1
  222. package/build-types/text/types.d.ts +15 -2
  223. package/build-types/text/types.d.ts.map +1 -1
  224. package/build-types/text-control/index.d.ts +2 -1
  225. package/build-types/text-control/index.d.ts.map +1 -1
  226. package/build-types/text-control/types.d.ts +6 -0
  227. package/build-types/text-control/types.d.ts.map +1 -1
  228. package/build-types/toggle-group-control/toggle-group-control/component.d.ts +1 -0
  229. package/build-types/toggle-group-control/toggle-group-control/component.d.ts.map +1 -1
  230. package/build-types/toggle-group-control/toggle-group-control/styles.d.ts +2 -2
  231. package/build-types/toggle-group-control/toggle-group-control/styles.d.ts.map +1 -1
  232. package/build-types/toggle-group-control/types.d.ts +6 -0
  233. package/build-types/toggle-group-control/types.d.ts.map +1 -1
  234. package/build-types/tools-panel/tools-panel-item/hook.d.ts.map +1 -1
  235. package/package.json +19 -20
  236. package/src/angle-picker-control/index.tsx +0 -1
  237. package/src/box-control/stories/index.story.tsx +82 -0
  238. package/src/button/style.scss +10 -2
  239. package/src/combobox-control/README.md +1 -3
  240. package/src/custom-select-control-v2/README.md +73 -0
  241. package/src/custom-select-control-v2/index.tsx +99 -0
  242. package/src/custom-select-control-v2/stories/index.story.tsx +149 -0
  243. package/src/custom-select-control-v2/styles.ts +76 -0
  244. package/src/custom-select-control-v2/types.ts +63 -0
  245. package/src/disclosure/index.tsx +44 -0
  246. package/src/disclosure/types.tsx +10 -0
  247. package/src/divider/component.tsx +3 -3
  248. package/src/divider/stories/index.story.tsx +8 -0
  249. package/src/divider/types.ts +2 -2
  250. package/src/dropdown-menu/style.scss +4 -0
  251. package/src/dropdown-menu-v2-ariakit/README.md +19 -12
  252. package/src/dropdown-menu-v2-ariakit/index.tsx +116 -51
  253. package/src/dropdown-menu-v2-ariakit/stories/index.story.tsx +205 -94
  254. package/src/dropdown-menu-v2-ariakit/styles.ts +153 -117
  255. package/src/dropdown-menu-v2-ariakit/test/index.tsx +5 -36
  256. package/src/dropdown-menu-v2-ariakit/types.ts +1 -8
  257. package/src/form-toggle/style.scss +37 -7
  258. package/src/form-token-field/index.tsx +11 -3
  259. package/src/form-token-field/token-input.tsx +1 -3
  260. package/src/form-token-field/types.ts +1 -0
  261. package/src/gradient-picker/index.tsx +1 -1
  262. package/src/heading/README.md +6 -1
  263. package/src/heading/hook.ts +6 -3
  264. package/src/heading/types.ts +23 -1
  265. package/src/index.native.js +1 -0
  266. package/src/mobile/audio-player/index.native.js +9 -13
  267. package/src/mobile/global-styles-context/utils.native.js +52 -3
  268. package/src/mobile/utils/alignments.native.js +1 -0
  269. package/src/navigable-container/README.md +1 -1
  270. package/src/private-apis.ts +4 -2
  271. package/src/radio-group/context.tsx +18 -0
  272. package/src/radio-group/index.tsx +65 -0
  273. package/src/radio-group/radio.tsx +55 -0
  274. package/src/radio-group/stories/index.story.tsx +90 -0
  275. package/src/radio-group/types.ts +39 -0
  276. package/src/slot-fill/README.md +1 -1
  277. package/src/slot-fill/bubbles-virtually/slot-fill-provider.tsx +1 -1
  278. package/src/slot-fill/types.ts +18 -6
  279. package/src/tabs/README.md +3 -33
  280. package/src/tabs/index.tsx +12 -2
  281. package/src/tabs/stories/index.story.tsx +17 -1
  282. package/src/tabs/styles.ts +16 -0
  283. package/src/tabs/tab.tsx +10 -10
  284. package/src/tabs/tablist.tsx +21 -20
  285. package/src/tabs/tabpanel.tsx +26 -25
  286. package/src/tabs/test/index.tsx +90 -16
  287. package/src/tabs/types.ts +7 -32
  288. package/src/text/README.md +5 -1
  289. package/src/text/types.ts +15 -2
  290. package/src/text-control/index.tsx +5 -1
  291. package/src/text-control/style.scss +5 -0
  292. package/src/text-control/types.ts +6 -0
  293. package/src/toggle-control/README.md +2 -2
  294. package/src/toggle-group-control/toggle-group-control/component.tsx +8 -2
  295. package/src/toggle-group-control/toggle-group-control/styles.ts +13 -4
  296. package/src/toggle-group-control/types.ts +6 -0
  297. package/src/toolbar/toolbar-button/style.scss +0 -5
  298. package/src/tools-panel/tools-panel-item/hook.ts +11 -2
  299. package/tsconfig.tsbuildinfo +1 -1
  300. package/build/radio-group/radio/index.js +0 -49
  301. package/build/radio-group/radio/index.js.map +0 -1
  302. package/build/radio-group/radio-context/index.js +0 -18
  303. package/build/radio-group/radio-context/index.js.map +0 -1
  304. package/build-module/radio-group/radio/index.js +0 -40
  305. package/build-module/radio-group/radio/index.js.map +0 -1
  306. package/build-module/radio-group/radio-context/index.js +0 -10
  307. package/build-module/radio-group/radio-context/index.js.map +0 -1
  308. package/build-types/radio-group/radio/index.d.ts +0 -7
  309. package/build-types/radio-group/radio/index.d.ts.map +0 -1
  310. package/build-types/radio-group/radio-context/index.d.ts +0 -6
  311. package/build-types/radio-group/radio-context/index.d.ts.map +0 -1
  312. package/src/box-control/stories/index.story.js +0 -75
  313. package/src/disclosure/index.js +0 -11
  314. package/src/radio-group/index.js +0 -51
  315. package/src/radio-group/radio/index.js +0 -40
  316. package/src/radio-group/radio-context/index.js +0 -11
  317. package/src/radio-group/stories/index.story.js +0 -83
@@ -20,6 +20,14 @@ import Button from '../../button';
20
20
  const meta: Meta< typeof Tabs > = {
21
21
  title: 'Components (Experimental)/Tabs',
22
22
  component: Tabs,
23
+ subcomponents: {
24
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
25
+ 'Tabs.TabList': Tabs.TabList,
26
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
27
+ 'Tabs.Tab': Tabs.Tab,
28
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
29
+ 'Tabs.TabPanel': Tabs.TabPanel,
30
+ },
23
31
  parameters: {
24
32
  actions: { argTypesRegex: '^on.*' },
25
33
  controls: { expanded: true },
@@ -42,8 +50,16 @@ const Template: StoryFn< typeof Tabs > = ( props ) => {
42
50
  <Tabs.TabPanel id={ 'tab2' }>
43
51
  <p>Selected tab: Tab 2</p>
44
52
  </Tabs.TabPanel>
45
- <Tabs.TabPanel id={ 'tab3' }>
53
+ <Tabs.TabPanel id={ 'tab3' } focusable={ false }>
46
54
  <p>Selected tab: Tab 3</p>
55
+ <p>
56
+ This tabpanel has its <code>focusable</code> prop set to
57
+ <code> false</code>, so it won&apos;t get a tab stop.
58
+ <br />
59
+ Instead, the [Tab] key will move focus to the first
60
+ focusable element within the panel.
61
+ </p>
62
+ <Button variant="primary">I&apos;m a button!</Button>
47
63
  </Tabs.TabPanel>
48
64
  </Tabs>
49
65
  );
@@ -101,3 +101,19 @@ export const Tab = styled( Ariakit.Tab )`
101
101
  }
102
102
  }
103
103
  `;
104
+
105
+ export const TabPanel = styled( Ariakit.TabPanel )`
106
+ &:focus {
107
+ box-shadow: none;
108
+ outline: none;
109
+ }
110
+
111
+ &:focus-visible {
112
+ border-radius: 2px;
113
+ box-shadow: 0 0 0 var( --wp-admin-border-width-focus )
114
+ ${ COLORS.theme.accent };
115
+ // Windows high contrast mode.
116
+ outline: 2px solid transparent;
117
+ outline-offset: 0;
118
+ }
119
+ `;
package/src/tabs/tab.tsx CHANGED
@@ -2,23 +2,24 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
 
5
- import { useContext, forwardRef } from '@wordpress/element';
5
+ import { forwardRef } from '@wordpress/element';
6
6
 
7
7
  /**
8
8
  * Internal dependencies
9
9
  */
10
10
  import type { TabProps } from './types';
11
11
  import warning from '@wordpress/warning';
12
- import { TabsContext } from './context';
12
+ import { useTabsContext } from './context';
13
13
  import { Tab as StyledTab } from './styles';
14
+ import type { WordPressComponentProps } from '../context';
14
15
 
15
- export const Tab = forwardRef< HTMLButtonElement, TabProps >( function Tab(
16
- { children, id, className, disabled, render, style },
17
- ref
18
- ) {
19
- const context = useContext( TabsContext );
16
+ export const Tab = forwardRef<
17
+ HTMLButtonElement,
18
+ WordPressComponentProps< TabProps, 'button', false >
19
+ >( function Tab( { children, id, disabled, render, ...otherProps }, ref ) {
20
+ const context = useTabsContext();
20
21
  if ( ! context ) {
21
- warning( '`Tabs.TabList` must be wrapped in a `Tabs` component.' );
22
+ warning( '`Tabs.Tab` must be wrapped in a `Tabs` component.' );
22
23
  return null;
23
24
  }
24
25
  const { store, instanceId } = context;
@@ -28,10 +29,9 @@ export const Tab = forwardRef< HTMLButtonElement, TabProps >( function Tab(
28
29
  ref={ ref }
29
30
  store={ store }
30
31
  id={ instancedTabId }
31
- className={ className }
32
- style={ style }
33
32
  disabled={ disabled }
34
33
  render={ render }
34
+ { ...otherProps }
35
35
  >
36
36
  { children }
37
37
  </StyledTab>
@@ -16,25 +16,26 @@ import { forwardRef } from '@wordpress/element';
16
16
  import type { TabListProps } from './types';
17
17
  import { useTabsContext } from './context';
18
18
  import { TabListWrapper } from './styles';
19
+ import type { WordPressComponentProps } from '../context';
19
20
 
20
- export const TabList = forwardRef< HTMLDivElement, TabListProps >(
21
- function TabList( { children, className, style }, ref ) {
22
- const context = useTabsContext();
23
- if ( ! context ) {
24
- warning( '`Tabs.TabList` must be wrapped in a `Tabs` component.' );
25
- return null;
26
- }
27
- const { store } = context;
28
- return (
29
- <Ariakit.TabList
30
- ref={ ref }
31
- style={ style }
32
- store={ store }
33
- className={ className }
34
- render={ <TabListWrapper /> }
35
- >
36
- { children }
37
- </Ariakit.TabList>
38
- );
21
+ export const TabList = forwardRef<
22
+ HTMLDivElement,
23
+ WordPressComponentProps< TabListProps, 'div', false >
24
+ >( function TabList( { children, ...otherProps }, ref ) {
25
+ const context = useTabsContext();
26
+ if ( ! context ) {
27
+ warning( '`Tabs.TabList` must be wrapped in a `Tabs` component.' );
28
+ return null;
39
29
  }
40
- );
30
+ const { store } = context;
31
+ return (
32
+ <Ariakit.TabList
33
+ ref={ ref }
34
+ store={ store }
35
+ render={ <TabListWrapper /> }
36
+ { ...otherProps }
37
+ >
38
+ { children }
39
+ </Ariakit.TabList>
40
+ );
41
+ } );
@@ -1,42 +1,43 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- // eslint-disable-next-line no-restricted-imports
5
- import * as Ariakit from '@ariakit/react';
6
4
 
7
5
  /**
8
6
  * WordPress dependencies
9
7
  */
10
8
 
11
- import { forwardRef, useContext } from '@wordpress/element';
9
+ import { forwardRef } from '@wordpress/element';
12
10
 
13
11
  /**
14
12
  * Internal dependencies
15
13
  */
16
14
  import type { TabPanelProps } from './types';
15
+ import { TabPanel as StyledTabPanel } from './styles';
17
16
 
18
17
  import warning from '@wordpress/warning';
19
- import { TabsContext } from './context';
18
+ import { useTabsContext } from './context';
19
+ import type { WordPressComponentProps } from '../context';
20
20
 
21
- export const TabPanel = forwardRef< HTMLDivElement, TabPanelProps >(
22
- function TabPanel( { children, id, className, style }, ref ) {
23
- const context = useContext( TabsContext );
24
- if ( ! context ) {
25
- warning( '`Tabs.TabPanel` must be wrapped in a `Tabs` component.' );
26
- return null;
27
- }
28
- const { store, instanceId } = context;
29
-
30
- return (
31
- <Ariakit.TabPanel
32
- ref={ ref }
33
- style={ style }
34
- store={ store }
35
- id={ `${ instanceId }-${ id }-view` }
36
- className={ className }
37
- >
38
- { children }
39
- </Ariakit.TabPanel>
40
- );
21
+ export const TabPanel = forwardRef<
22
+ HTMLDivElement,
23
+ WordPressComponentProps< TabPanelProps, 'div', false >
24
+ >( function TabPanel( { children, id, focusable = true, ...otherProps }, ref ) {
25
+ const context = useTabsContext();
26
+ if ( ! context ) {
27
+ warning( '`Tabs.TabPanel` must be wrapped in a `Tabs` component.' );
28
+ return null;
41
29
  }
42
- );
30
+ const { store, instanceId } = context;
31
+
32
+ return (
33
+ <StyledTabPanel
34
+ ref={ ref }
35
+ store={ store }
36
+ id={ `${ instanceId }-${ id }-view` }
37
+ focusable={ focusable }
38
+ { ...otherProps }
39
+ >
40
+ { children }
41
+ </StyledTabPanel>
42
+ );
43
+ } );
@@ -7,7 +7,6 @@ import userEvent from '@testing-library/user-event';
7
7
  /**
8
8
  * WordPress dependencies
9
9
  */
10
- import { wordpress, category, media } from '@wordpress/icons';
11
10
  import { useState } from '@wordpress/element';
12
11
 
13
12
  /**
@@ -15,7 +14,6 @@ import { useState } from '@wordpress/element';
15
14
  */
16
15
  import Tabs from '..';
17
16
  import type { TabsProps } from '../types';
18
- import type { IconType } from '../../icon';
19
17
 
20
18
  type Tab = {
21
19
  id: string;
@@ -23,9 +21,11 @@ type Tab = {
23
21
  content: React.ReactNode;
24
22
  tab: {
25
23
  className?: string;
26
- icon?: IconType;
27
24
  disabled?: boolean;
28
25
  };
26
+ tabpanel?: {
27
+ focusable?: boolean;
28
+ };
29
29
  };
30
30
 
31
31
  const TABS: Tab[] = [
@@ -33,19 +33,19 @@ const TABS: Tab[] = [
33
33
  id: 'alpha',
34
34
  title: 'Alpha',
35
35
  content: 'Selected tab: Alpha',
36
- tab: { className: 'alpha-class', icon: wordpress },
36
+ tab: { className: 'alpha-class' },
37
37
  },
38
38
  {
39
39
  id: 'beta',
40
40
  title: 'Beta',
41
41
  content: 'Selected tab: Beta',
42
- tab: { className: 'beta-class', icon: category },
42
+ tab: { className: 'beta-class' },
43
43
  },
44
44
  {
45
45
  id: 'gamma',
46
46
  title: 'Gamma',
47
47
  content: 'Selected tab: Gamma',
48
- tab: { className: 'gamma-class', icon: media },
48
+ tab: { className: 'gamma-class' },
49
49
  },
50
50
  ];
51
51
 
@@ -55,17 +55,15 @@ const TABS_WITH_DELTA: Tab[] = [
55
55
  id: 'delta',
56
56
  title: 'Delta',
57
57
  content: 'Selected tab: Delta',
58
- tab: { className: 'delta-class', icon: media },
58
+ tab: { className: 'delta-class' },
59
59
  },
60
60
  ];
61
61
 
62
62
  const UncontrolledTabs = ( {
63
63
  tabs,
64
- showTabIcons = false,
65
64
  ...props
66
65
  }: Omit< TabsProps, 'children' > & {
67
66
  tabs: Tab[];
68
- showTabIcons?: boolean;
69
67
  } ) => {
70
68
  return (
71
69
  <Tabs { ...props }>
@@ -76,14 +74,17 @@ const UncontrolledTabs = ( {
76
74
  id={ tabObj.id }
77
75
  className={ tabObj.tab.className }
78
76
  disabled={ tabObj.tab.disabled }
79
- icon={ showTabIcons ? tabObj.tab.icon : undefined }
80
77
  >
81
- { showTabIcons ? null : tabObj.title }
78
+ { tabObj.title }
82
79
  </Tabs.Tab>
83
80
  ) ) }
84
81
  </Tabs.TabList>
85
82
  { tabs.map( ( tabObj ) => (
86
- <Tabs.TabPanel key={ tabObj.id } id={ tabObj.id }>
83
+ <Tabs.TabPanel
84
+ key={ tabObj.id }
85
+ id={ tabObj.id }
86
+ focusable={ tabObj.tabpanel?.focusable }
87
+ >
87
88
  { tabObj.content }
88
89
  </Tabs.TabPanel>
89
90
  ) ) }
@@ -93,11 +94,9 @@ const UncontrolledTabs = ( {
93
94
 
94
95
  const ControlledTabs = ( {
95
96
  tabs,
96
- showTabIcons = false,
97
97
  ...props
98
98
  }: Omit< TabsProps, 'children' > & {
99
99
  tabs: Tab[];
100
- showTabIcons?: boolean;
101
100
  } ) => {
102
101
  const [ selectedTabId, setSelectedTabId ] = useState<
103
102
  string | undefined | null
@@ -119,9 +118,8 @@ const ControlledTabs = ( {
119
118
  id={ tabObj.id }
120
119
  className={ tabObj.tab.className }
121
120
  disabled={ tabObj.tab.disabled }
122
- icon={ showTabIcons ? tabObj.tab.icon : undefined }
123
121
  >
124
- { showTabIcons ? null : tabObj.title }
122
+ { tabObj.title }
125
123
  </Tabs.Tab>
126
124
  ) ) }
127
125
  </Tabs.TabList>
@@ -184,6 +182,63 @@ describe( 'Tabs', () => {
184
182
  );
185
183
  } );
186
184
  } );
185
+ describe( 'Focus Behavior', () => {
186
+ it( 'should focus on the related TabPanel when pressing the Tab key', async () => {
187
+ const user = userEvent.setup();
188
+
189
+ render( <UncontrolledTabs tabs={ TABS } /> );
190
+
191
+ const selectedTabPanel = await screen.findByRole( 'tabpanel' );
192
+
193
+ // Tab should initially focus the first tab in the tablist, which
194
+ // is Alpha.
195
+ await user.keyboard( '[Tab]' );
196
+ expect(
197
+ await screen.findByRole( 'tab', { name: 'Alpha' } )
198
+ ).toHaveFocus();
199
+
200
+ // By default the tabpanel should receive focus
201
+ await user.keyboard( '[Tab]' );
202
+ expect( selectedTabPanel ).toHaveFocus();
203
+ } );
204
+ it( 'should not focus on the related TabPanel when pressing the Tab key if `focusable: false` is set', async () => {
205
+ const user = userEvent.setup();
206
+
207
+ const TABS_WITH_ALPHA_FOCUSABLE_FALSE = TABS.map( ( tabObj ) =>
208
+ tabObj.id === 'alpha'
209
+ ? {
210
+ ...tabObj,
211
+ content: (
212
+ <>
213
+ Selected Tab: Alpha
214
+ <button>Alpha Button</button>
215
+ </>
216
+ ),
217
+ tabpanel: { focusable: false },
218
+ }
219
+ : tabObj
220
+ );
221
+
222
+ render(
223
+ <UncontrolledTabs tabs={ TABS_WITH_ALPHA_FOCUSABLE_FALSE } />
224
+ );
225
+
226
+ const alphaButton = await screen.findByRole( 'button', {
227
+ name: /alpha button/i,
228
+ } );
229
+
230
+ // Tab should initially focus the first tab in the tablist, which
231
+ // is Alpha.
232
+ await user.keyboard( '[Tab]' );
233
+ expect(
234
+ await screen.findByRole( 'tab', { name: 'Alpha' } )
235
+ ).toHaveFocus();
236
+ // Because the alpha tabpanel is set to `focusable: false`, pressing
237
+ // the Tab key should focus the button, not the tabpanel
238
+ await user.keyboard( '[Tab]' );
239
+ expect( alphaButton ).toHaveFocus();
240
+ } );
241
+ } );
187
242
 
188
243
  describe( 'Tab Attributes', () => {
189
244
  it( "should apply the tab's `className` to the tab button", async () => {
@@ -469,6 +524,15 @@ describe( 'Tabs', () => {
469
524
  await screen.findByRole( 'tab', { name: 'Alpha' } )
470
525
  ).toHaveFocus();
471
526
 
527
+ // This assertion ensures the component has had time to fully
528
+ // render, preventing flakiness.
529
+ // see https://github.com/WordPress/gutenberg/pull/55950
530
+ await waitFor( () =>
531
+ expect(
532
+ screen.getByRole( 'tab', { name: 'Beta' } )
533
+ ).toHaveAttribute( 'tabindex', '-1' )
534
+ );
535
+
472
536
  // Because all other tabs should have `tabindex=-1`, pressing Tab
473
537
  // should NOT move the focus to the next tab, which is Beta.
474
538
  // Instead, focus should go to the currently selected tabpanel (alpha).
@@ -792,6 +856,16 @@ describe( 'Tabs', () => {
792
856
  // onSelect should not be called since the disabled tab is
793
857
  // highlighted, but not selected.
794
858
  await user.keyboard( '[Tab]' );
859
+
860
+ // This assertion ensures focus has time to move to the first
861
+ // tab before the test proceeds, preventing flakiness.
862
+ // see https://github.com/WordPress/gutenberg/pull/55950
863
+ await waitFor( () =>
864
+ expect(
865
+ screen.getByRole( 'tab', { name: 'Alpha' } )
866
+ ).toHaveFocus()
867
+ );
868
+
795
869
  await user.keyboard( '[ArrowLeft]' );
796
870
  expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
797
871
 
package/src/tabs/types.ts CHANGED
@@ -4,11 +4,6 @@
4
4
  // eslint-disable-next-line no-restricted-imports
5
5
  import type * as Ariakit from '@ariakit/react';
6
6
 
7
- /**
8
- * Internal dependencies
9
- */
10
- import type { IconType } from '../icon';
11
-
12
7
  export type TabsContextProps =
13
8
  | {
14
9
  /**
@@ -78,14 +73,6 @@ export type TabListProps = {
78
73
  * The children elements, which should be a series of `Tabs.TabPanel` components.
79
74
  */
80
75
  children?: React.ReactNode;
81
- /**
82
- * The class name to apply to the tablist.
83
- */
84
- className?: string;
85
- /**
86
- * Custom CSS styles for the rendered tablist.
87
- */
88
- style?: React.CSSProperties;
89
76
  };
90
77
 
91
78
  export type TabProps = {
@@ -93,22 +80,10 @@ export type TabProps = {
93
80
  * The id of the tab, which is prepended with the `Tabs` instanceId.
94
81
  */
95
82
  id: string;
96
- /**
97
- * Custom CSS styles for the tab.
98
- */
99
- style?: React.CSSProperties;
100
83
  /**
101
84
  * The children elements, generally the text to display on the tab.
102
85
  */
103
86
  children?: React.ReactNode;
104
- /**
105
- * The class name to apply to the tab button.
106
- */
107
- className?: string;
108
- /**
109
- * The icon used for the tab button.
110
- */
111
- icon?: IconType;
112
87
  /**
113
88
  * Determines if the tab button should be disabled.
114
89
  *
@@ -128,15 +103,15 @@ export type TabPanelProps = {
128
103
  */
129
104
  children?: React.ReactNode;
130
105
  /**
131
- * A unique identifier for the TabPanel, which is used to generate a unique `id` for the underlying element.
106
+ * A unique identifier for the tabpanel, which is used to generate a unique `id` for the underlying element.
132
107
  */
133
108
  id: string;
134
109
  /**
135
- * The class name to apply to the tabpanel.
136
- */
137
- className?: string;
138
- /**
139
- * Custom CSS styles for the rendered `TabPanel` component.
110
+ * Determines whether or not the tabpanel element should be focusable.
111
+ * If `false`, pressing the tab key will skip over the tabpanel, and instead
112
+ * focus on the first focusable element in the panel (if there is one).
113
+ *
114
+ * @default true
140
115
  */
141
- style?: React.CSSProperties;
116
+ focusable?: boolean;
142
117
  };
@@ -132,6 +132,8 @@ function Example() {
132
132
 
133
133
  Sets `Text` to have `display: block`.
134
134
 
135
+ Note: text truncation only works when `isBlock` is `false`.
136
+
135
137
  ### isDestructive
136
138
 
137
139
  **Type**: `boolean`
@@ -196,7 +198,9 @@ function Example() {
196
198
 
197
199
  **Type**: `boolean`
198
200
 
199
- Enables text truncation. When `truncate` is set,we are able to truncate the long text in a variety of ways.
201
+ Enables text truncation. When `truncate` is set, we are able to truncate the long text in a variety of ways.
202
+
203
+ Note: text truncation won't work if the `isBlock` property is set to `true`
200
204
 
201
205
  ```jsx
202
206
  import { __experimentalText as Text } from '@wordpress/components';
package/src/text/types.ts CHANGED
@@ -46,10 +46,14 @@ export interface Props extends TruncateProps {
46
46
  isDestructive?: boolean;
47
47
  /**
48
48
  * Escape characters in `highlightWords` which are meaningful in regular expressions.
49
+ *
50
+ * @default false
49
51
  */
50
52
  highlightEscape?: boolean;
51
53
  /**
52
54
  * Determines if `highlightWords` should be case sensitive.
55
+ *
56
+ * @default false
53
57
  */
54
58
  highlightCaseSensitive?: boolean;
55
59
  /**
@@ -57,7 +61,10 @@ export interface Props extends TruncateProps {
57
61
  */
58
62
  highlightSanitize?: FindAllArgs[ 'sanitize' ];
59
63
  /**
60
- * Sets `Text` to have `display: block`.
64
+ * Sets `Text` to have `display: block`. Note: text truncation only works
65
+ * when `isBlock` is `false`.
66
+ *
67
+ * @default false
61
68
  */
62
69
  isBlock?: boolean;
63
70
  /**
@@ -73,11 +80,15 @@ export interface Props extends TruncateProps {
73
80
  */
74
81
  size?: CSSProperties[ 'fontSize' ] | TextSize;
75
82
  /**
76
- * Enables text truncation. When `truncate` is set,we are able to truncate the long text in a variety of ways.
83
+ * Enables text truncation. When `truncate` is set, we are able to truncate the long text in a variety of ways. Note: text truncation won't work if the `isBlock` property is set to `true`
84
+ *
85
+ * @default false
77
86
  */
78
87
  truncate?: boolean;
79
88
  /**
80
89
  * Uppercases the text content.
90
+ *
91
+ * @default false
81
92
  */
82
93
  upperCase?: boolean;
83
94
  /**
@@ -86,6 +97,8 @@ export interface Props extends TruncateProps {
86
97
  variant?: TextVariant;
87
98
  /**
88
99
  * Adjusts font-weight of the text.
100
+ *
101
+ * @default 'normal'
89
102
  */
90
103
  weight?: CSSProperties[ 'fontWeight' ] | TextWeight;
91
104
  /**
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { ChangeEvent, ForwardedRef } from 'react';
5
+ import classnames from 'classnames';
5
6
 
6
7
  /**
7
8
  * WordPress dependencies
@@ -22,6 +23,7 @@ function UnforwardedTextControl(
22
23
  ) {
23
24
  const {
24
25
  __nextHasNoMarginBottom,
26
+ __next40pxDefaultSize = false,
25
27
  label,
26
28
  hideLabelFromVision,
27
29
  value,
@@ -46,7 +48,9 @@ function UnforwardedTextControl(
46
48
  className={ className }
47
49
  >
48
50
  <input
49
- className="components-text-control__input"
51
+ className={ classnames( 'components-text-control__input', {
52
+ 'is-next-40px-default-size': __next40pxDefaultSize,
53
+ } ) }
50
54
  type={ type }
51
55
  id={ id }
52
56
  value={ value }
@@ -13,5 +13,10 @@
13
13
  .components-text-control__input[type="month"],
14
14
  .components-text-control__input[type="number"] {
15
15
  width: 100%;
16
+ height: $grid-unit-40;
16
17
  @include input-control;
18
+
19
+ &.is-next-40px-default-size {
20
+ height: $grid-unit-50;
21
+ }
17
22
  }
@@ -25,4 +25,10 @@ export type TextControlProps = Pick<
25
25
  * @default 'text'
26
26
  */
27
27
  type?: 'email' | 'number' | 'password' | 'tel' | 'text' | 'search' | 'url';
28
+ /**
29
+ * Start opting into the larger default height that will become the default size in a future version.
30
+ *
31
+ * @default false
32
+ */
33
+ __next40pxDefaultSize?: boolean;
28
34
  };
@@ -22,8 +22,8 @@ const MyToggleControl = () => {
22
22
  : 'No fixed background.'
23
23
  }
24
24
  checked={ hasFixedBackground }
25
- onChange={ () => {
26
- setHasFixedBackground( ( state ) => ! state );
25
+ onChange={ (newValue) => {
26
+ setHasFixedBackground( newValue );
27
27
  } }
28
28
  />
29
29
  );
@@ -31,6 +31,7 @@ function UnconnectedToggleGroupControl(
31
31
  ) {
32
32
  const {
33
33
  __nextHasNoMarginBottom = false,
34
+ __next40pxDefaultSize = false,
34
35
  className,
35
36
  isAdaptiveWidth = false,
36
37
  isBlock = false,
@@ -52,11 +53,16 @@ function UnconnectedToggleGroupControl(
52
53
  const classes = useMemo(
53
54
  () =>
54
55
  cx(
55
- styles.toggleGroupControl( { isBlock, isDeselectable, size } ),
56
+ styles.toggleGroupControl( {
57
+ isBlock,
58
+ isDeselectable,
59
+ size,
60
+ __next40pxDefaultSize,
61
+ } ),
56
62
  isBlock && styles.block,
57
63
  className
58
64
  ),
59
- [ className, cx, isBlock, isDeselectable, size ]
65
+ [ className, cx, isBlock, isDeselectable, size, __next40pxDefaultSize ]
60
66
  );
61
67
 
62
68
  const MainControl = isDeselectable