@instructure/ui-table 11.6.0 → 11.6.1-snapshot-129

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 (303) hide show
  1. package/CHANGELOG.md +43 -308
  2. package/es/Table/{Body → v1/Body}/index.js +2 -2
  3. package/es/Table/{Cell → v1/Cell}/index.js +2 -2
  4. package/es/Table/{ColHeader → v1/ColHeader}/index.js +1 -1
  5. package/es/Table/{Head → v1/Head}/index.js +2 -2
  6. package/es/Table/{Row → v1/Row}/index.js +2 -2
  7. package/es/Table/{RowHeader → v1/RowHeader}/index.js +2 -2
  8. package/es/Table/{index.js → v1/index.js} +2 -2
  9. package/es/Table/v2/Body/index.js +73 -0
  10. package/es/Table/v2/Body/props.js +26 -0
  11. package/es/Table/v2/Body/styles.js +45 -0
  12. package/es/Table/v2/Cell/index.js +68 -0
  13. package/es/Table/v2/Cell/props.js +26 -0
  14. package/es/Table/v2/Cell/styles.js +54 -0
  15. package/es/Table/v2/ColHeader/index.js +117 -0
  16. package/es/Table/v2/ColHeader/props.js +26 -0
  17. package/es/Table/v2/ColHeader/styles.js +93 -0
  18. package/es/Table/v2/Head/index.js +173 -0
  19. package/es/Table/v2/Head/props.js +26 -0
  20. package/es/Table/v2/Head/styles.js +45 -0
  21. package/es/Table/v2/Row/index.js +82 -0
  22. package/es/Table/v2/Row/props.js +26 -0
  23. package/es/Table/v2/Row/styles.js +67 -0
  24. package/es/Table/v2/RowHeader/index.js +68 -0
  25. package/es/Table/v2/RowHeader/props.js +26 -0
  26. package/es/Table/v2/RowHeader/styles.js +54 -0
  27. package/es/Table/v2/TableContext.js +46 -0
  28. package/es/Table/v2/index.js +135 -0
  29. package/es/Table/v2/props.js +26 -0
  30. package/es/Table/v2/styles.js +58 -0
  31. package/es/exports/a.js +31 -0
  32. package/es/exports/b.js +31 -0
  33. package/lib/Table/{Body → v1/Body}/index.js +4 -4
  34. package/lib/Table/{Cell → v1/Cell}/index.js +4 -4
  35. package/lib/Table/{ColHeader → v1/ColHeader}/index.js +4 -4
  36. package/lib/Table/{Head → v1/Head}/index.js +5 -5
  37. package/lib/Table/{Row → v1/Row}/index.js +4 -4
  38. package/lib/Table/{RowHeader → v1/RowHeader}/index.js +4 -4
  39. package/lib/Table/{index.js → v1/index.js} +4 -4
  40. package/lib/Table/v2/Body/index.js +79 -0
  41. package/lib/Table/v2/Body/props.js +31 -0
  42. package/lib/Table/v2/Body/styles.js +51 -0
  43. package/lib/Table/v2/Cell/index.js +74 -0
  44. package/lib/Table/v2/Cell/props.js +31 -0
  45. package/lib/Table/v2/Cell/styles.js +60 -0
  46. package/lib/Table/v2/ColHeader/index.js +122 -0
  47. package/lib/Table/v2/ColHeader/props.js +31 -0
  48. package/lib/Table/v2/ColHeader/styles.js +99 -0
  49. package/lib/Table/v2/Head/index.js +179 -0
  50. package/lib/Table/v2/Head/props.js +31 -0
  51. package/lib/Table/v2/Head/styles.js +51 -0
  52. package/lib/Table/v2/Row/index.js +88 -0
  53. package/lib/Table/v2/Row/props.js +31 -0
  54. package/lib/Table/v2/Row/styles.js +73 -0
  55. package/lib/Table/v2/RowHeader/index.js +74 -0
  56. package/lib/Table/v2/RowHeader/props.js +31 -0
  57. package/lib/Table/v2/RowHeader/styles.js +60 -0
  58. package/lib/Table/v2/TableContext.js +51 -0
  59. package/lib/Table/v2/index.js +140 -0
  60. package/lib/Table/v2/props.js +31 -0
  61. package/lib/Table/v2/styles.js +64 -0
  62. package/lib/{index.js → exports/a.js} +9 -9
  63. package/lib/exports/b.js +61 -0
  64. package/package.json +43 -21
  65. package/src/Table/{Body → v1/Body}/index.tsx +2 -2
  66. package/src/Table/{Cell → v1/Cell}/index.tsx +2 -2
  67. package/src/Table/{ColHeader → v1/ColHeader}/index.tsx +1 -1
  68. package/src/Table/{Head → v1/Head}/index.tsx +3 -3
  69. package/src/Table/{README.md → v1/README.md} +1 -1
  70. package/src/Table/{Row → v1/Row}/index.tsx +2 -2
  71. package/src/Table/{RowHeader → v1/RowHeader}/index.tsx +2 -2
  72. package/src/Table/{index.tsx → v1/index.tsx} +2 -2
  73. package/src/Table/v2/Body/index.tsx +91 -0
  74. package/src/Table/v2/Body/props.ts +50 -0
  75. package/src/Table/v2/Body/styles.ts +51 -0
  76. package/src/Table/v2/Cell/index.tsx +83 -0
  77. package/src/Table/v2/Cell/props.ts +54 -0
  78. package/src/Table/v2/Cell/styles.ts +60 -0
  79. package/src/Table/v2/ColHeader/index.tsx +118 -0
  80. package/src/Table/v2/ColHeader/props.ts +93 -0
  81. package/src/Table/v2/ColHeader/styles.ts +95 -0
  82. package/src/Table/v2/Head/index.tsx +200 -0
  83. package/src/Table/v2/Head/props.ts +64 -0
  84. package/src/Table/v2/Head/styles.ts +51 -0
  85. package/src/Table/v2/README.md +1155 -0
  86. package/src/Table/v2/Row/index.tsx +105 -0
  87. package/src/Table/v2/Row/props.ts +67 -0
  88. package/src/Table/v2/Row/styles.ts +78 -0
  89. package/src/Table/v2/RowHeader/index.tsx +84 -0
  90. package/src/Table/v2/RowHeader/props.ts +48 -0
  91. package/src/Table/v2/RowHeader/styles.ts +60 -0
  92. package/src/Table/v2/TableContext.ts +66 -0
  93. package/src/Table/v2/index.tsx +160 -0
  94. package/src/Table/v2/props.ts +98 -0
  95. package/src/Table/v2/styles.ts +63 -0
  96. package/src/exports/a.ts +39 -0
  97. package/src/exports/b.ts +39 -0
  98. package/tsconfig.build.tsbuildinfo +1 -1
  99. package/types/Table/v1/Body/index.d.ts.map +1 -0
  100. package/types/Table/v1/Body/props.d.ts.map +1 -0
  101. package/types/Table/v1/Body/styles.d.ts.map +1 -0
  102. package/types/Table/v1/Body/theme.d.ts.map +1 -0
  103. package/types/Table/v1/Cell/index.d.ts.map +1 -0
  104. package/types/Table/v1/Cell/props.d.ts.map +1 -0
  105. package/types/Table/v1/Cell/styles.d.ts.map +1 -0
  106. package/types/Table/v1/Cell/theme.d.ts.map +1 -0
  107. package/types/Table/v1/ColHeader/index.d.ts.map +1 -0
  108. package/types/Table/v1/ColHeader/props.d.ts.map +1 -0
  109. package/types/Table/v1/ColHeader/styles.d.ts.map +1 -0
  110. package/types/Table/v1/ColHeader/theme.d.ts.map +1 -0
  111. package/types/Table/v1/Head/index.d.ts.map +1 -0
  112. package/types/Table/v1/Head/props.d.ts.map +1 -0
  113. package/types/Table/v1/Head/styles.d.ts.map +1 -0
  114. package/types/Table/v1/Head/theme.d.ts.map +1 -0
  115. package/types/Table/v1/Row/index.d.ts.map +1 -0
  116. package/types/Table/v1/Row/props.d.ts.map +1 -0
  117. package/types/Table/v1/Row/styles.d.ts.map +1 -0
  118. package/types/Table/v1/Row/theme.d.ts.map +1 -0
  119. package/types/Table/v1/RowHeader/index.d.ts.map +1 -0
  120. package/types/Table/v1/RowHeader/props.d.ts.map +1 -0
  121. package/types/Table/v1/RowHeader/styles.d.ts.map +1 -0
  122. package/types/Table/v1/RowHeader/theme.d.ts.map +1 -0
  123. package/types/Table/v1/TableContext.d.ts.map +1 -0
  124. package/types/Table/v1/index.d.ts.map +1 -0
  125. package/types/Table/v1/props.d.ts.map +1 -0
  126. package/types/Table/v1/styles.d.ts.map +1 -0
  127. package/types/Table/v1/theme.d.ts.map +1 -0
  128. package/types/Table/v2/Body/index.d.ts +24 -0
  129. package/types/Table/v2/Body/index.d.ts.map +1 -0
  130. package/types/Table/v2/Body/props.d.ts +20 -0
  131. package/types/Table/v2/Body/props.d.ts.map +1 -0
  132. package/types/Table/v2/Body/styles.d.ts +13 -0
  133. package/types/Table/v2/Body/styles.d.ts.map +1 -0
  134. package/types/Table/v2/Cell/index.d.ts +29 -0
  135. package/types/Table/v2/Cell/index.d.ts.map +1 -0
  136. package/types/Table/v2/Cell/props.d.ts +23 -0
  137. package/types/Table/v2/Cell/props.d.ts.map +1 -0
  138. package/types/Table/v2/Cell/styles.d.ts +14 -0
  139. package/types/Table/v2/Cell/styles.d.ts.map +1 -0
  140. package/types/Table/v2/ColHeader/index.d.ts +37 -0
  141. package/types/Table/v2/ColHeader/index.d.ts.map +1 -0
  142. package/types/Table/v2/ColHeader/props.d.ts +47 -0
  143. package/types/Table/v2/ColHeader/props.d.ts.map +1 -0
  144. package/types/Table/v2/ColHeader/styles.d.ts +15 -0
  145. package/types/Table/v2/ColHeader/styles.d.ts.map +1 -0
  146. package/types/Table/v2/Head/index.d.ts +38 -0
  147. package/types/Table/v2/Head/index.d.ts.map +1 -0
  148. package/types/Table/v2/Head/props.d.ts +34 -0
  149. package/types/Table/v2/Head/props.d.ts.map +1 -0
  150. package/types/Table/v2/Head/styles.d.ts +13 -0
  151. package/types/Table/v2/Head/styles.d.ts.map +1 -0
  152. package/types/Table/v2/Row/index.d.ts +27 -0
  153. package/types/Table/v2/Row/index.d.ts.map +1 -0
  154. package/types/Table/v2/Row/props.d.ts +29 -0
  155. package/types/Table/v2/Row/props.d.ts.map +1 -0
  156. package/types/Table/v2/Row/styles.d.ts +19 -0
  157. package/types/Table/v2/Row/styles.d.ts.map +1 -0
  158. package/types/Table/v2/RowHeader/index.d.ts +28 -0
  159. package/types/Table/v2/RowHeader/index.d.ts.map +1 -0
  160. package/types/Table/v2/RowHeader/props.d.ts +17 -0
  161. package/types/Table/v2/RowHeader/props.d.ts.map +1 -0
  162. package/types/Table/v2/RowHeader/styles.d.ts +14 -0
  163. package/types/Table/v2/RowHeader/styles.d.ts.map +1 -0
  164. package/types/Table/v2/TableContext.d.ts +36 -0
  165. package/types/Table/v2/TableContext.d.ts.map +1 -0
  166. package/types/Table/v2/index.d.ts +45 -0
  167. package/types/Table/v2/index.d.ts.map +1 -0
  168. package/types/Table/v2/props.d.ts +53 -0
  169. package/types/Table/v2/props.d.ts.map +1 -0
  170. package/types/Table/v2/styles.d.ts +15 -0
  171. package/types/Table/v2/styles.d.ts.map +1 -0
  172. package/types/exports/a.d.ts +16 -0
  173. package/types/exports/a.d.ts.map +1 -0
  174. package/types/exports/b.d.ts +16 -0
  175. package/types/exports/b.d.ts.map +1 -0
  176. package/es/index.js +0 -31
  177. package/src/index.ts +0 -39
  178. package/types/Table/Body/index.d.ts.map +0 -1
  179. package/types/Table/Body/props.d.ts.map +0 -1
  180. package/types/Table/Body/styles.d.ts.map +0 -1
  181. package/types/Table/Body/theme.d.ts.map +0 -1
  182. package/types/Table/Cell/index.d.ts.map +0 -1
  183. package/types/Table/Cell/props.d.ts.map +0 -1
  184. package/types/Table/Cell/styles.d.ts.map +0 -1
  185. package/types/Table/Cell/theme.d.ts.map +0 -1
  186. package/types/Table/ColHeader/index.d.ts.map +0 -1
  187. package/types/Table/ColHeader/props.d.ts.map +0 -1
  188. package/types/Table/ColHeader/styles.d.ts.map +0 -1
  189. package/types/Table/ColHeader/theme.d.ts.map +0 -1
  190. package/types/Table/Head/index.d.ts.map +0 -1
  191. package/types/Table/Head/props.d.ts.map +0 -1
  192. package/types/Table/Head/styles.d.ts.map +0 -1
  193. package/types/Table/Head/theme.d.ts.map +0 -1
  194. package/types/Table/Row/index.d.ts.map +0 -1
  195. package/types/Table/Row/props.d.ts.map +0 -1
  196. package/types/Table/Row/styles.d.ts.map +0 -1
  197. package/types/Table/Row/theme.d.ts.map +0 -1
  198. package/types/Table/RowHeader/index.d.ts.map +0 -1
  199. package/types/Table/RowHeader/props.d.ts.map +0 -1
  200. package/types/Table/RowHeader/styles.d.ts.map +0 -1
  201. package/types/Table/RowHeader/theme.d.ts.map +0 -1
  202. package/types/Table/TableContext.d.ts.map +0 -1
  203. package/types/Table/index.d.ts.map +0 -1
  204. package/types/Table/props.d.ts.map +0 -1
  205. package/types/Table/styles.d.ts.map +0 -1
  206. package/types/Table/theme.d.ts.map +0 -1
  207. package/types/index.d.ts +0 -16
  208. package/types/index.d.ts.map +0 -1
  209. /package/es/Table/{Body → v1/Body}/props.js +0 -0
  210. /package/es/Table/{Body → v1/Body}/styles.js +0 -0
  211. /package/es/Table/{Body → v1/Body}/theme.js +0 -0
  212. /package/es/Table/{Cell → v1/Cell}/props.js +0 -0
  213. /package/es/Table/{Cell → v1/Cell}/styles.js +0 -0
  214. /package/es/Table/{Cell → v1/Cell}/theme.js +0 -0
  215. /package/es/Table/{ColHeader → v1/ColHeader}/props.js +0 -0
  216. /package/es/Table/{ColHeader → v1/ColHeader}/styles.js +0 -0
  217. /package/es/Table/{ColHeader → v1/ColHeader}/theme.js +0 -0
  218. /package/es/Table/{Head → v1/Head}/props.js +0 -0
  219. /package/es/Table/{Head → v1/Head}/styles.js +0 -0
  220. /package/es/Table/{Head → v1/Head}/theme.js +0 -0
  221. /package/es/Table/{Row → v1/Row}/props.js +0 -0
  222. /package/es/Table/{Row → v1/Row}/styles.js +0 -0
  223. /package/es/Table/{Row → v1/Row}/theme.js +0 -0
  224. /package/es/Table/{RowHeader → v1/RowHeader}/props.js +0 -0
  225. /package/es/Table/{RowHeader → v1/RowHeader}/styles.js +0 -0
  226. /package/es/Table/{RowHeader → v1/RowHeader}/theme.js +0 -0
  227. /package/es/Table/{TableContext.js → v1/TableContext.js} +0 -0
  228. /package/es/Table/{props.js → v1/props.js} +0 -0
  229. /package/es/Table/{styles.js → v1/styles.js} +0 -0
  230. /package/es/Table/{theme.js → v1/theme.js} +0 -0
  231. /package/lib/Table/{Body → v1/Body}/props.js +0 -0
  232. /package/lib/Table/{Body → v1/Body}/styles.js +0 -0
  233. /package/lib/Table/{Body → v1/Body}/theme.js +0 -0
  234. /package/lib/Table/{Cell → v1/Cell}/props.js +0 -0
  235. /package/lib/Table/{Cell → v1/Cell}/styles.js +0 -0
  236. /package/lib/Table/{Cell → v1/Cell}/theme.js +0 -0
  237. /package/lib/Table/{ColHeader → v1/ColHeader}/props.js +0 -0
  238. /package/lib/Table/{ColHeader → v1/ColHeader}/styles.js +0 -0
  239. /package/lib/Table/{ColHeader → v1/ColHeader}/theme.js +0 -0
  240. /package/lib/Table/{Head → v1/Head}/props.js +0 -0
  241. /package/lib/Table/{Head → v1/Head}/styles.js +0 -0
  242. /package/lib/Table/{Head → v1/Head}/theme.js +0 -0
  243. /package/lib/Table/{Row → v1/Row}/props.js +0 -0
  244. /package/lib/Table/{Row → v1/Row}/styles.js +0 -0
  245. /package/lib/Table/{Row → v1/Row}/theme.js +0 -0
  246. /package/lib/Table/{RowHeader → v1/RowHeader}/props.js +0 -0
  247. /package/lib/Table/{RowHeader → v1/RowHeader}/styles.js +0 -0
  248. /package/lib/Table/{RowHeader → v1/RowHeader}/theme.js +0 -0
  249. /package/lib/Table/{TableContext.js → v1/TableContext.js} +0 -0
  250. /package/lib/Table/{props.js → v1/props.js} +0 -0
  251. /package/lib/Table/{styles.js → v1/styles.js} +0 -0
  252. /package/lib/Table/{theme.js → v1/theme.js} +0 -0
  253. /package/src/Table/{Body → v1/Body}/props.ts +0 -0
  254. /package/src/Table/{Body → v1/Body}/styles.ts +0 -0
  255. /package/src/Table/{Body → v1/Body}/theme.ts +0 -0
  256. /package/src/Table/{Cell → v1/Cell}/props.ts +0 -0
  257. /package/src/Table/{Cell → v1/Cell}/styles.ts +0 -0
  258. /package/src/Table/{Cell → v1/Cell}/theme.ts +0 -0
  259. /package/src/Table/{ColHeader → v1/ColHeader}/props.ts +0 -0
  260. /package/src/Table/{ColHeader → v1/ColHeader}/styles.ts +0 -0
  261. /package/src/Table/{ColHeader → v1/ColHeader}/theme.ts +0 -0
  262. /package/src/Table/{Head → v1/Head}/props.ts +0 -0
  263. /package/src/Table/{Head → v1/Head}/styles.ts +0 -0
  264. /package/src/Table/{Head → v1/Head}/theme.ts +0 -0
  265. /package/src/Table/{Row → v1/Row}/props.ts +0 -0
  266. /package/src/Table/{Row → v1/Row}/styles.ts +0 -0
  267. /package/src/Table/{Row → v1/Row}/theme.ts +0 -0
  268. /package/src/Table/{RowHeader → v1/RowHeader}/props.ts +0 -0
  269. /package/src/Table/{RowHeader → v1/RowHeader}/styles.ts +0 -0
  270. /package/src/Table/{RowHeader → v1/RowHeader}/theme.ts +0 -0
  271. /package/src/Table/{TableContext.ts → v1/TableContext.ts} +0 -0
  272. /package/src/Table/{props.ts → v1/props.ts} +0 -0
  273. /package/src/Table/{styles.ts → v1/styles.ts} +0 -0
  274. /package/src/Table/{theme.ts → v1/theme.ts} +0 -0
  275. /package/types/Table/{Body → v1/Body}/index.d.ts +0 -0
  276. /package/types/Table/{Body → v1/Body}/props.d.ts +0 -0
  277. /package/types/Table/{Body → v1/Body}/styles.d.ts +0 -0
  278. /package/types/Table/{Body → v1/Body}/theme.d.ts +0 -0
  279. /package/types/Table/{Cell → v1/Cell}/index.d.ts +0 -0
  280. /package/types/Table/{Cell → v1/Cell}/props.d.ts +0 -0
  281. /package/types/Table/{Cell → v1/Cell}/styles.d.ts +0 -0
  282. /package/types/Table/{Cell → v1/Cell}/theme.d.ts +0 -0
  283. /package/types/Table/{ColHeader → v1/ColHeader}/index.d.ts +0 -0
  284. /package/types/Table/{ColHeader → v1/ColHeader}/props.d.ts +0 -0
  285. /package/types/Table/{ColHeader → v1/ColHeader}/styles.d.ts +0 -0
  286. /package/types/Table/{ColHeader → v1/ColHeader}/theme.d.ts +0 -0
  287. /package/types/Table/{Head → v1/Head}/index.d.ts +0 -0
  288. /package/types/Table/{Head → v1/Head}/props.d.ts +0 -0
  289. /package/types/Table/{Head → v1/Head}/styles.d.ts +0 -0
  290. /package/types/Table/{Head → v1/Head}/theme.d.ts +0 -0
  291. /package/types/Table/{Row → v1/Row}/index.d.ts +0 -0
  292. /package/types/Table/{Row → v1/Row}/props.d.ts +0 -0
  293. /package/types/Table/{Row → v1/Row}/styles.d.ts +0 -0
  294. /package/types/Table/{Row → v1/Row}/theme.d.ts +0 -0
  295. /package/types/Table/{RowHeader → v1/RowHeader}/index.d.ts +0 -0
  296. /package/types/Table/{RowHeader → v1/RowHeader}/props.d.ts +0 -0
  297. /package/types/Table/{RowHeader → v1/RowHeader}/styles.d.ts +0 -0
  298. /package/types/Table/{RowHeader → v1/RowHeader}/theme.d.ts +0 -0
  299. /package/types/Table/{TableContext.d.ts → v1/TableContext.d.ts} +0 -0
  300. /package/types/Table/{index.d.ts → v1/index.d.ts} +0 -0
  301. /package/types/Table/{props.d.ts → v1/props.d.ts} +0 -0
  302. /package/types/Table/{styles.d.ts → v1/styles.d.ts} +0 -0
  303. /package/types/Table/{theme.d.ts → v1/theme.d.ts} +0 -0
@@ -0,0 +1,1155 @@
1
+ ---
2
+ describes: Table
3
+ ---
4
+
5
+ ### Table layout
6
+
7
+ In stacked layout, column header is rendered in each cell, but not in row header.
8
+
9
+ > If you are using [TruncateText](TruncateText) (or CSS ellipsis) in your Table, set the
10
+ > layout to `fixed` or `stacked`, or the text truncation will not work. To force long strings to break and not
11
+ > exceed the bounds of the table cell, use `fixed` or `stacked`, together with the [Text](Text) component:
12
+ > `<Text wrap="break-word">[long string]</Text>`.
13
+
14
+ ```js
15
+ ---
16
+ type: example
17
+ ---
18
+ const Example = () => {
19
+ const [layout, setLayout] = useState('auto')
20
+ const [hover, setHover] = useState(false)
21
+
22
+ const handleChange = (field, value) => {
23
+ if (field === 'layout') {
24
+ setLayout(value)
25
+ } else if (field === 'hover') {
26
+ setHover(value)
27
+ }
28
+ }
29
+
30
+ const renderOptions = () => (
31
+ <Flex alignItems="start">
32
+ <Flex.Item margin="small">
33
+ <RadioInputGroup
34
+ name="layout"
35
+ description="layout"
36
+ value={layout}
37
+ onChange={(e, value) => handleChange('layout', value)}
38
+ >
39
+ <RadioInput label="auto" value="auto" />
40
+ <RadioInput label="fixed" value="fixed" />
41
+ <RadioInput label="stacked" value="stacked" />
42
+ </RadioInputGroup>
43
+ </Flex.Item>
44
+ <Flex.Item margin="small">
45
+ <Checkbox
46
+ label="hover"
47
+ checked={hover}
48
+ onChange={(e, value) => handleChange('hover', !hover)}
49
+ />
50
+ </Flex.Item>
51
+ </Flex>
52
+ )
53
+
54
+ return (
55
+ <div>
56
+ {renderOptions()}
57
+ <Table caption="Top rated movies" layout={layout} hover={hover}>
58
+ <Table.Head>
59
+ <Table.Row>
60
+ <Table.ColHeader id="Rank">Rank</Table.ColHeader>
61
+ <Table.ColHeader id="Title">Title</Table.ColHeader>
62
+ <Table.ColHeader id="Year">Year</Table.ColHeader>
63
+ <Table.ColHeader id="Rating">Rating</Table.ColHeader>
64
+ </Table.Row>
65
+ </Table.Head>
66
+ <Table.Body>
67
+ <Table.Row>
68
+ <Table.RowHeader>1</Table.RowHeader>
69
+ <Table.Cell>The Shawshank Redemption</Table.Cell>
70
+ <Table.Cell>1994</Table.Cell>
71
+ <Table.Cell>9.3</Table.Cell>
72
+ </Table.Row>
73
+ <Table.Row>
74
+ <Table.RowHeader>2</Table.RowHeader>
75
+ <Table.Cell>The Godfather</Table.Cell>
76
+ <Table.Cell>1972</Table.Cell>
77
+ <Table.Cell>9.2</Table.Cell>
78
+ </Table.Row>
79
+ <Table.Row>
80
+ <Table.RowHeader>3</Table.RowHeader>
81
+ <Table.Cell>The Godfather: Part II</Table.Cell>
82
+ <Table.Cell>1974</Table.Cell>
83
+ <Table.Cell>9.0</Table.Cell>
84
+ </Table.Row>
85
+ </Table.Body>
86
+ </Table>
87
+ </div>
88
+ )
89
+ }
90
+
91
+ render(<Example />)
92
+ ```
93
+
94
+ ### Column width and alignment
95
+
96
+ Each column (`ColHeader`) can have a custom width, and each cell (`ColHeader`, `RowHeader` or `Cell`)
97
+ can be aligned differently.
98
+
99
+ ```js
100
+ ---
101
+ type: example
102
+ ---
103
+ const Example = ({ headers, rows }) => {
104
+ return (
105
+ <Responsive
106
+ query={{
107
+ small: { maxWidth: '40rem' },
108
+ large: { minWidth: '41rem' }
109
+ }}
110
+ props={{
111
+ small: { layout: 'stacked' },
112
+ large: { layout: 'fixed' }
113
+ }}
114
+ >
115
+ {({ layout }) => (
116
+ <div>
117
+ <Table caption="Top rated movies" layout={layout}>
118
+ <Table.Head>
119
+ <Table.Row>
120
+ {(headers || []).map(({ id, text, width, textAlign }) => (
121
+ <Table.ColHeader
122
+ key={id}
123
+ id={id}
124
+ width={width}
125
+ textAlign={textAlign}
126
+ >
127
+ {text}
128
+ </Table.ColHeader>
129
+ ))}
130
+ </Table.Row>
131
+ </Table.Head>
132
+ <Table.Body>
133
+ {rows.map((row) => (
134
+ <Table.Row key={row.id}>
135
+ {headers.map(({ id, renderCell, textAlign }) => (
136
+ <Table.Cell
137
+ key={id}
138
+ textAlign={layout === 'stacked' ? 'start' : textAlign}
139
+ >
140
+ {renderCell ? renderCell(row[id], layout) : row[id]}
141
+ </Table.Cell>
142
+ ))}
143
+ </Table.Row>
144
+ ))}
145
+ </Table.Body>
146
+ </Table>
147
+ </div>
148
+ )}
149
+ </Responsive>
150
+ )
151
+ }
152
+
153
+ const renderSummary = (summary, layout) =>
154
+ layout === 'stacked' ? (
155
+ summary
156
+ ) : (
157
+ <TruncateText truncate="word" ellipsis="...">
158
+ {summary}
159
+ </TruncateText>
160
+ )
161
+
162
+ render(
163
+ <Example
164
+ headers={[
165
+ {
166
+ id: 'Title',
167
+ text: 'Title',
168
+ width: '25%',
169
+ textAlign: 'start'
170
+ },
171
+ {
172
+ id: 'Year',
173
+ text: 'Year',
174
+ width: '15%',
175
+ textAlign: 'start'
176
+ },
177
+ {
178
+ id: 'Summary',
179
+ text: 'Summary',
180
+ width: '40%',
181
+ renderCell: renderSummary,
182
+ textAlign: 'start'
183
+ },
184
+ {
185
+ id: 'BoxOffice',
186
+ text: 'Box Office',
187
+ width: '20%',
188
+ textAlign: 'end'
189
+ }
190
+ ]}
191
+ rows={[
192
+ {
193
+ id: '1',
194
+ Title: 'The Shawshank Redemption',
195
+ Year: 1994,
196
+ Summary:
197
+ 'Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.',
198
+ BoxOffice: '$28,341,469'
199
+ },
200
+ {
201
+ id: '2',
202
+ Title: 'The Godfather',
203
+ Year: 1972,
204
+ Summary:
205
+ 'The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.',
206
+ BoxOffice: '$133,698,921'
207
+ },
208
+ {
209
+ id: '3',
210
+ Title: 'The Godfather: Part II',
211
+ Year: 1974,
212
+ Summary:
213
+ 'The early life and career of Vito Corleone in 1920s New York City is portrayed, while his son, Michael, expands and tightens his grip on the family crime syndicate.',
214
+ BoxOffice: '$47,542,841'
215
+ }
216
+ ]}
217
+ />
218
+ )
219
+ ```
220
+
221
+ ### A sortable table using our Responsive component
222
+
223
+ Resize the window to see how column headers transition into a `Select` for sorting table content when the traditional Table Header is no longer clickable. The Table layout itself switches from the horizontal view to the stacked view allowing content to be viewed without horizontal scrolling. See [Responsive](Responsive) for more examples.
224
+
225
+ By default, the options in the `Select` for sorting in stacked layout are generated from the `id` property of the `Table.ColHeader` components. If you want to display custom strings, use the `stackedSortByLabel` property.
226
+
227
+ ```js
228
+ ---
229
+ type: example
230
+ ---
231
+ const SortableTable = ({ caption, headers, rows }) => {
232
+ const initialColWidth = {}
233
+ headers.forEach((header) => {
234
+ initialColWidth[header.id] = 'start'
235
+ })
236
+
237
+ const [sortBy, setSortBy] = useState(headers && headers[0] && headers[0].id)
238
+ const [ascending, setAscending] = useState(true)
239
+ const [colTextAligns, setColTextAligns] = useState(initialColWidth)
240
+
241
+ const sortedRows = useMemo(() => {
242
+ if (!sortBy) return rows
243
+
244
+ const sorted = [...rows].sort((a, b) => {
245
+ return a[sortBy] > b[sortBy] ? 1 : a[sortBy] < b[sortBy] ? -1 : 0
246
+ })
247
+
248
+ return ascending ? sorted : sorted.reverse()
249
+ }, [sortBy, ascending, rows])
250
+
251
+ const handleSort = (event, { id }) => {
252
+ if (id === sortBy) {
253
+ setAscending(!ascending)
254
+ } else {
255
+ setSortBy(id)
256
+ setAscending(true)
257
+ }
258
+ }
259
+
260
+ const handleColTextAlignChange = (id, value) => {
261
+ setColTextAligns((prevState) => ({
262
+ ...prevState,
263
+ [id]: value
264
+ }))
265
+ }
266
+
267
+ const renderHeaderRow = (direction) => (
268
+ <Table.Row>
269
+ {(headers || []).map(({ id, text, width }) => (
270
+ <Table.ColHeader
271
+ key={id}
272
+ id={id}
273
+ width={width}
274
+ {...(direction && {
275
+ textAlign: colTextAligns[id],
276
+ stackedSortByLabel: text,
277
+ onRequestSort: handleSort,
278
+ sortDirection: id === sortBy ? direction : 'none'
279
+ })}
280
+ >
281
+ {id === sortBy ? (
282
+ text
283
+ ) : (
284
+ <>
285
+ <span aria-hidden="true">{text}</span>
286
+ <ScreenReaderContent>sort by {text}</ScreenReaderContent>
287
+ </>
288
+ )}
289
+ </Table.ColHeader>
290
+ ))}
291
+ </Table.Row>
292
+ )
293
+
294
+ const renderOptions = () => (
295
+ <ToggleGroup
296
+ size="small"
297
+ toggleLabel="Set text-align for columns"
298
+ summary="Set text-align for columns"
299
+ background="default"
300
+ >
301
+ <Table caption="Set text-align for columns">
302
+ <Table.Head>{renderHeaderRow()}</Table.Head>
303
+ <Table.Body>
304
+ <Table.Row>
305
+ {Object.entries(colTextAligns).map(([headerId, textAlign]) => (
306
+ <Table.Cell key={headerId}>
307
+ <RadioInputGroup
308
+ description={
309
+ <ScreenReaderContent>
310
+ Set text-align for column: {headerId}
311
+ </ScreenReaderContent>
312
+ }
313
+ name={`columnTextAlign_${headerId}`}
314
+ value={textAlign}
315
+ margin="0 0 small"
316
+ size="small"
317
+ onChange={(e, value) =>
318
+ handleColTextAlignChange(headerId, value)
319
+ }
320
+ >
321
+ <RadioInput label="start" value="start" />
322
+ <RadioInput label="center" value="center" />
323
+ <RadioInput label="end" value="end" />
324
+ </RadioInputGroup>
325
+ </Table.Cell>
326
+ ))}
327
+ </Table.Row>
328
+ </Table.Body>
329
+ </Table>
330
+ </ToggleGroup>
331
+ )
332
+
333
+ const direction = ascending ? 'ascending' : 'descending'
334
+
335
+ return (
336
+ <Responsive
337
+ query={{
338
+ small: { maxWidth: '40rem' },
339
+ large: { minWidth: '41rem' }
340
+ }}
341
+ props={{
342
+ small: { layout: 'stacked' },
343
+ large: { layout: 'auto' }
344
+ }}
345
+ >
346
+ {(props) => (
347
+ <div>
348
+ {props.layout !== 'stacked' && (
349
+ <View display="block" margin="0 0 medium">
350
+ {renderOptions()}
351
+ </View>
352
+ )}
353
+
354
+ <Table
355
+ caption={`${caption}: sorted by ${sortBy} in ${direction} order`}
356
+ {...props}
357
+ >
358
+ <Table.Head renderSortLabel="Sort by">
359
+ {renderHeaderRow(direction)}
360
+ </Table.Head>
361
+ <Table.Body>
362
+ {sortedRows.map((row) => (
363
+ <Table.Row key={row.id}>
364
+ {headers.map(({ id, renderCell }) => (
365
+ <Table.Cell key={id} textAlign={colTextAligns[id]}>
366
+ {renderCell ? renderCell(row[id]) : row[id]}
367
+ </Table.Cell>
368
+ ))}
369
+ </Table.Row>
370
+ ))}
371
+ </Table.Body>
372
+ </Table>
373
+ <Alert
374
+ liveRegion={() => document.getElementById('flash-messages')}
375
+ liveRegionPoliteness="polite"
376
+ screenReaderOnly
377
+ >
378
+ {`Sorted by ${sortBy} in ${direction} order`}
379
+ </Alert>
380
+ </div>
381
+ )}
382
+ </Responsive>
383
+ )
384
+ }
385
+
386
+ render(
387
+ <SortableTable
388
+ caption="Top rated movies"
389
+ headers={[
390
+ {
391
+ id: 'rank',
392
+ text: 'Rank',
393
+ width: '15%'
394
+ },
395
+ {
396
+ id: 'title',
397
+ text: 'Title',
398
+ width: '55%'
399
+ },
400
+ {
401
+ id: 'year',
402
+ text: 'Year',
403
+ width: '15%'
404
+ },
405
+ {
406
+ id: 'rating',
407
+ text: 'Rating',
408
+ width: '15%',
409
+ renderCell: (rating) => rating.toFixed(1)
410
+ }
411
+ ]}
412
+ rows={[
413
+ {
414
+ id: '1',
415
+ rank: 1,
416
+ title: 'The Shawshank Redemption',
417
+ year: 1994,
418
+ rating: 9.3
419
+ },
420
+ {
421
+ id: '2',
422
+ rank: 2,
423
+ title: 'The Godfather',
424
+ year: 1972,
425
+ rating: 9.2
426
+ },
427
+ {
428
+ id: '3',
429
+ rank: 3,
430
+ title: 'The Godfather: Part II',
431
+ year: 1974,
432
+ rating: 9.0
433
+ },
434
+ {
435
+ id: '4',
436
+ rank: 4,
437
+ title: 'The Dark Knight',
438
+ year: 2008,
439
+ rating: 9.0
440
+ },
441
+ {
442
+ id: '5',
443
+ rank: 5,
444
+ title: '12 Angry Men',
445
+ year: 1957,
446
+ rating: 8.9
447
+ }
448
+ ]}
449
+ />
450
+ )
451
+ ```
452
+
453
+ ### A sortable table with selection and pagination
454
+
455
+ The composition order is important. `SelectableTable` -> `PaginatedTable` -> `SortableTable`, so
456
+ that selection does not re-paginate or re-sort the table, and pagination does not re-sort the table.
457
+
458
+ ```js
459
+ ---
460
+ type: example
461
+ ---
462
+ const SelectableTable = ({
463
+ caption,
464
+ headers,
465
+ rows,
466
+ onSort,
467
+ sortBy,
468
+ ascending,
469
+ rowIds
470
+ }) => {
471
+ const [selected, setSelected] = useState(new Set())
472
+
473
+ const handleSelectAll = (allSelected) => {
474
+ setSelected(allSelected ? new Set() : new Set(rowIds))
475
+ }
476
+
477
+ const handleSelectRow = (rowSelected, rowId) => {
478
+ const copy = new Set(selected)
479
+ if (rowSelected) {
480
+ copy.delete(rowId)
481
+ } else {
482
+ copy.add(rowId)
483
+ }
484
+ setSelected(copy)
485
+ }
486
+
487
+ const allSelected =
488
+ selected.size > 0 && rowIds.every((id) => selected.has(id))
489
+ const someSelected = selected.size > 0 && !allSelected
490
+ const direction = ascending ? 'ascending' : 'descending'
491
+
492
+ return (
493
+ <Responsive
494
+ query={{
495
+ small: { maxWidth: '40rem' },
496
+ large: { minWidth: '41rem' }
497
+ }}
498
+ props={{
499
+ small: { layout: 'stacked' },
500
+ large: { layout: 'auto' }
501
+ }}
502
+ >
503
+ {(props) => (
504
+ <div>
505
+ <View as="div" padding="small" background="primary-inverse">
506
+ {`${selected.size} of ${rowIds.length} selected`}
507
+ </View>
508
+ <Table
509
+ caption={`${caption}: sorted by ${sortBy} in ${direction} order`}
510
+ {...props}
511
+ >
512
+ <Table.Head
513
+ renderSortLabel={
514
+ <ScreenReaderContent>Sort by</ScreenReaderContent>
515
+ }
516
+ >
517
+ <Table.Row>
518
+ <Table.ColHeader id="select" aria-label="Select">
519
+ <Checkbox
520
+ label={
521
+ <ScreenReaderContent>Select all rows</ScreenReaderContent>
522
+ }
523
+ onChange={() => handleSelectAll(allSelected)}
524
+ checked={allSelected}
525
+ indeterminate={someSelected}
526
+ />
527
+ </Table.ColHeader>
528
+ {(headers || []).map(({ id, text, width }) => (
529
+ <Table.ColHeader
530
+ key={id}
531
+ id={id}
532
+ width={width}
533
+ onRequestSort={onSort}
534
+ sortDirection={id === sortBy ? direction : 'none'}
535
+ >
536
+ {id === sortBy ? (
537
+ text
538
+ ) : (
539
+ <>
540
+ <span aria-hidden="true">{text}</span>
541
+ <ScreenReaderContent>
542
+ sort by {text}
543
+ </ScreenReaderContent>
544
+ </>
545
+ )}
546
+ </Table.ColHeader>
547
+ ))}
548
+ </Table.Row>
549
+ </Table.Head>
550
+ <Table.Body>
551
+ {(rows || []).map((row) => {
552
+ const rowSelected = selected.has(row.id)
553
+
554
+ return (
555
+ <Table.Row key={row.id}>
556
+ <Table.RowHeader>
557
+ <Checkbox
558
+ label={
559
+ <ScreenReaderContent>
560
+ Select row
561
+ </ScreenReaderContent>
562
+ }
563
+ onChange={() => handleSelectRow(rowSelected, row.id)}
564
+ checked={rowSelected}
565
+ />
566
+ </Table.RowHeader>
567
+ {(headers || []).map(({ id, renderCell }) => (
568
+ <Table.Cell key={id}>
569
+ {renderCell ? renderCell(row[id]) : row[id]}
570
+ </Table.Cell>
571
+ ))}
572
+ </Table.Row>
573
+ )
574
+ })}
575
+ </Table.Body>
576
+ </Table>
577
+ <Alert
578
+ liveRegion={() => document.getElementById('flash-messages')}
579
+ liveRegionPoliteness="polite"
580
+ screenReaderOnly
581
+ >
582
+ {`${selected.size} of ${rowIds.length} selected`}
583
+ </Alert>
584
+ </div>
585
+ )}
586
+ </Responsive>
587
+ )
588
+ }
589
+
590
+ const PaginatedTable = ({
591
+ caption,
592
+ headers,
593
+ rows,
594
+ onSort,
595
+ sortBy,
596
+ ascending,
597
+ perPage
598
+ }) => {
599
+ const [page, setPage] = useState(0)
600
+
601
+ const handleClick = (page) => {
602
+ setPage(page)
603
+ }
604
+
605
+ const handleSort = (event, options) => {
606
+ setPage(0)
607
+ onSort(event, options)
608
+ }
609
+
610
+ const startIndex = page * perPage
611
+ const slicedRows = rows.slice(startIndex, startIndex + perPage)
612
+ const pageCount = perPage && Math.ceil(rows.length / perPage)
613
+
614
+ return (
615
+ <div>
616
+ <SelectableTable
617
+ caption={caption}
618
+ headers={headers}
619
+ rows={slicedRows}
620
+ onSort={handleSort}
621
+ sortBy={sortBy}
622
+ ascending={ascending}
623
+ rowIds={rows.map((row) => row.id)}
624
+ />
625
+ {pageCount > 1 && (
626
+ <Pagination
627
+ variant="compact"
628
+ labelNext="Next Page"
629
+ labelPrev="Previous Page"
630
+ margin="large"
631
+ >
632
+ {Array.from(Array(pageCount), (item, index) => (
633
+ <Pagination.Page
634
+ key={index}
635
+ onClick={() => handleClick(index)}
636
+ current={index === page}
637
+ >
638
+ {index + 1}
639
+ </Pagination.Page>
640
+ ))}
641
+ </Pagination>
642
+ )}
643
+ <Alert
644
+ liveRegion={() => document.getElementById('flash-messages')}
645
+ liveRegionPoliteness="polite"
646
+ screenReaderOnly
647
+ >
648
+ {`Table page ${page + 1} of ${pageCount}`}
649
+ </Alert>
650
+ </div>
651
+ )
652
+ }
653
+
654
+ const SortableTable = ({ caption, headers, rows, perPage }) => {
655
+ const [sortBy, setSortBy] = useState(headers && headers[0] && headers[0].id)
656
+ const [ascending, setAscending] = useState(true)
657
+
658
+ const sortedRows = useMemo(() => {
659
+ if (!sortBy) return rows
660
+
661
+ const sorted = [...rows].sort((a, b) => {
662
+ return a[sortBy] > b[sortBy] ? 1 : a[sortBy] < b[sortBy] ? -1 : 0
663
+ })
664
+
665
+ return ascending ? sorted : sorted.reverse()
666
+ }, [sortBy, ascending, rows])
667
+
668
+ const handleSort = (event, { id }) => {
669
+ if (id === sortBy) {
670
+ setAscending(!ascending)
671
+ } else {
672
+ setSortBy(id)
673
+ setAscending(true)
674
+ }
675
+ }
676
+
677
+ return (
678
+ <div>
679
+ <PaginatedTable
680
+ caption={caption}
681
+ headers={headers}
682
+ rows={sortedRows}
683
+ onSort={handleSort}
684
+ sortBy={sortBy}
685
+ ascending={ascending}
686
+ perPage={perPage}
687
+ />
688
+ <Alert
689
+ liveRegion={() => document.getElementById('flash-messages')}
690
+ liveRegionPoliteness="polite"
691
+ screenReaderOnly
692
+ >
693
+ {`Sorted by ${sortBy} in ${
694
+ ascending ? 'ascending' : 'descending'
695
+ } order`}
696
+ </Alert>
697
+ </div>
698
+ )
699
+ }
700
+
701
+ const renderRating = (rating) => (
702
+ <Rating label="Rating" valueNow={rating} valueMax={10} iconCount={5} />
703
+ )
704
+
705
+ render(
706
+ <SortableTable
707
+ caption="Top rated movies"
708
+ headers={[
709
+ {
710
+ id: 'Rank',
711
+ text: 'Rank'
712
+ },
713
+ {
714
+ id: 'Title',
715
+ text: 'Title',
716
+ width: '40%'
717
+ },
718
+ {
719
+ id: 'Year',
720
+ text: 'Year'
721
+ },
722
+ {
723
+ id: 'Rating',
724
+ text: 'Rating',
725
+ renderCell: renderRating
726
+ }
727
+ ]}
728
+ rows={[
729
+ {
730
+ id: '1',
731
+ Rank: 1,
732
+ Title: 'The Shawshank Redemption',
733
+ Year: 1994,
734
+ Rating: 9.3
735
+ },
736
+ {
737
+ id: '2',
738
+ Rank: 2,
739
+ Title: 'The Godfather',
740
+ Year: 1972,
741
+ Rating: 9.2
742
+ },
743
+ {
744
+ id: '3',
745
+ Rank: 3,
746
+ Title: 'The Godfather: Part II',
747
+ Year: 1974,
748
+ Rating: 9.0
749
+ },
750
+ {
751
+ id: '4',
752
+ Rank: 4,
753
+ Title: 'The Dark Knight',
754
+ Year: 2008,
755
+ Rating: 9.0
756
+ },
757
+ {
758
+ id: '5',
759
+ Rank: 5,
760
+ Title: '12 Angry Men',
761
+ Year: 1957,
762
+ Rating: 8.9
763
+ }
764
+ ]}
765
+ perPage={3}
766
+ />
767
+ )
768
+ ```
769
+
770
+ ### Using Custom Components as Children
771
+
772
+ In some cases you might want to use custom components in a `Table`, e.g. a HOC for `Table.Row` or `Table.Cell`. This is generally not recommended, but sometimes it could be beneficial for codesplitting or writing cleaner code for larger and more complex Tables.
773
+
774
+ > Do not replace `Table.Body` and `Table.Head` with custom components
775
+
776
+ Wrapper HOCs are simple, just return the original component:
777
+
778
+ ```js
779
+ ---
780
+ type: example
781
+ ---
782
+ const CustomTableCell = ({ children, ...props }) => (
783
+ <Table.Cell {...props}>{children}</Table.Cell>
784
+ )
785
+
786
+ const CustomTableRow = ({ children, ...props }) => (
787
+ <Table.Row {...props}>
788
+ <Table.RowHeader>1</Table.RowHeader>
789
+ <Table.Cell>The Shawshank Redemption</Table.Cell>
790
+ <Table.Cell>1994</Table.Cell>
791
+ <CustomTableCell>9.3</CustomTableCell>
792
+ </Table.Row>
793
+ )
794
+
795
+ const Example = () => {
796
+ const [layout, setLayout] = useState('auto')
797
+ const [hover, setHover] = useState(false)
798
+
799
+ const handleChange = (field, value) => {
800
+ if (field === 'layout') {
801
+ setLayout(value)
802
+ } else if (field === 'hover') {
803
+ setHover(!hover)
804
+ }
805
+ }
806
+
807
+ const renderOptions = () => (
808
+ <Flex alignItems="start">
809
+ <Flex.Item margin="small">
810
+ <RadioInputGroup
811
+ name="layout2"
812
+ description="layout2"
813
+ value={layout}
814
+ onChange={(e, value) => handleChange('layout', value)}
815
+ >
816
+ <RadioInput label="auto" value="auto" />
817
+ <RadioInput label="fixed" value="fixed" />
818
+ <RadioInput label="stacked" value="stacked" />
819
+ </RadioInputGroup>
820
+ </Flex.Item>
821
+ <Flex.Item margin="small">
822
+ <Checkbox
823
+ label="hover"
824
+ checked={hover}
825
+ onChange={(e, value) => handleChange('hover', !hover)}
826
+ />
827
+ </Flex.Item>
828
+ </Flex>
829
+ )
830
+
831
+ return (
832
+ <div>
833
+ {renderOptions()}
834
+ <Table caption="Top rated movies" layout={layout} hover={hover}>
835
+ <Table.Head>
836
+ <Table.Row>
837
+ <Table.ColHeader id="Rank">Rank</Table.ColHeader>
838
+ <Table.ColHeader id="Title">Title</Table.ColHeader>
839
+ <Table.ColHeader id="Year">Year</Table.ColHeader>
840
+ <Table.ColHeader id="Rating">Rating</Table.ColHeader>
841
+ </Table.Row>
842
+ </Table.Head>
843
+ <Table.Body>
844
+ <CustomTableRow />
845
+ <Table.Row>
846
+ <Table.RowHeader>2</Table.RowHeader>
847
+ <Table.Cell>The Godfather</Table.Cell>
848
+ <Table.Cell>1972</Table.Cell>
849
+ <Table.Cell>9.2</Table.Cell>
850
+ </Table.Row>
851
+ <Table.Row>
852
+ <Table.RowHeader>3</Table.RowHeader>
853
+ <Table.Cell>The Godfather: Part II</Table.Cell>
854
+ <Table.Cell>1974</Table.Cell>
855
+ <Table.Cell>9.0</Table.Cell>
856
+ </Table.Row>
857
+ </Table.Body>
858
+ </Table>
859
+ </div>
860
+ )
861
+ }
862
+
863
+ render(<Example />)
864
+ ```
865
+
866
+ #### Fully custom components
867
+
868
+ If you want to use fully custom components you have to pay attention to the following:
869
+
870
+ - Render them as the appropriate HTML Table tags (`tr`, `th`, ...)
871
+ - Read the `hover` prop from [TableContext](TableContext) to customize hover behaviour
872
+ - A11y: Row header cells must have the `scope='row'` HTML attribute
873
+ - A11y: Column header cells must have the `scope='col'` and `aria-sort` (if sortable) HTML attribute
874
+
875
+ Basic fully custom table:
876
+
877
+ ```js
878
+ ---
879
+ type: example
880
+ ---
881
+ const CustomTableCell = ({ children, ...props }) => (
882
+ <td {...props}>{children}</td>
883
+ )
884
+
885
+ const CustomTableRow = ({ children, ...props }) => {
886
+ const { hover } = useContext(TableContext)
887
+ const [isHovered, setIsHovered] = useState(false)
888
+
889
+ const rowStyle =
890
+ hover && isHovered
891
+ ? { backgroundColor: 'SeaGreen' }
892
+ : { backgroundColor: 'white' }
893
+
894
+ return (
895
+ <tr
896
+ style={rowStyle}
897
+ onMouseOver={() => setIsHovered(true)}
898
+ onMouseOut={() => setIsHovered(false)}
899
+ >
900
+ {children}
901
+ </tr>
902
+ )
903
+ }
904
+
905
+ const Example = () => {
906
+ const [layout, setLayout] = useState('auto')
907
+ const [hover, setHover] = useState(false)
908
+
909
+ const handleChange = (field, value) => {
910
+ if (field === 'layout') {
911
+ setLayout(value)
912
+ } else if (field === 'hover') {
913
+ setHover(!hover)
914
+ }
915
+ }
916
+
917
+ const renderOptions = () => (
918
+ <Flex alignItems="start">
919
+ <Flex.Item margin="small">
920
+ <RadioInputGroup
921
+ name="Layout"
922
+ description="Layout"
923
+ value={layout}
924
+ onChange={(e, value) => handleChange('layout', value)}
925
+ >
926
+ <RadioInput label="auto" value="auto" />
927
+ <RadioInput label="fixed" value="fixed" />
928
+ </RadioInputGroup>
929
+ </Flex.Item>
930
+ <Flex.Item margin="small">
931
+ <Checkbox
932
+ label="hover"
933
+ checked={hover}
934
+ onChange={(e, value) => handleChange('hover', !hover)}
935
+ />
936
+ </Flex.Item>
937
+ </Flex>
938
+ )
939
+
940
+ return (
941
+ <div>
942
+ {renderOptions()}
943
+ <Table caption="Top rated movies" layout={layout} hover={hover}>
944
+ <Table.Head>
945
+ <CustomTableRow>
946
+ <CustomTableCell scope="col">Rank</CustomTableCell>
947
+ <CustomTableCell scope="col">Title</CustomTableCell>
948
+ <CustomTableCell scope="col">Year</CustomTableCell>
949
+ <CustomTableCell scope="col">Rating</CustomTableCell>
950
+ </CustomTableRow>
951
+ </Table.Head>
952
+ <Table.Body>
953
+ <CustomTableRow>
954
+ <CustomTableCell scope="row">1</CustomTableCell>
955
+ <CustomTableCell>The Godfather</CustomTableCell>
956
+ <CustomTableCell>1972</CustomTableCell>
957
+ <CustomTableCell>9.2</CustomTableCell>
958
+ </CustomTableRow>
959
+ <CustomTableRow>
960
+ <CustomTableCell scope="row">2</CustomTableCell>
961
+ <CustomTableCell>The Godfather: Part II</CustomTableCell>
962
+ <CustomTableCell>1974</CustomTableCell>
963
+ <CustomTableCell>9.0</CustomTableCell>
964
+ </CustomTableRow>
965
+ </Table.Body>
966
+ </Table>
967
+ </div>
968
+ )
969
+ }
970
+
971
+ render(<Example />)
972
+ ```
973
+
974
+ #### Fully custom components with `stacked` layout
975
+
976
+ This layout for small screens displays the table as a list. To accomplish this the headers are passed down to cells (in [TableContext](TableContext)), so they can display what column they are rendering.
977
+ In this layout for accessibility not render HTML table tags, just plain DOM elements (e.g. `div`) and use the appropriate ARIA role to signify that it's actually a `Table` (e.g. [`cell`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/cell_role), [`row`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/row_role), [`rowheader`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/rowheader_role)).
978
+ Also you need the following props on the components:
979
+
980
+ ##### Table rows
981
+
982
+ - It should read the `headers` array from [TableContext](TableContext) and pass its nth element to its nth child (if they have such prop).
983
+
984
+ ##### The children of the first row in `Table.Head` (`Table.ColHeader` by default)
985
+
986
+ - If the table is sortable the Table needs `id`, `onRequestSort`, `sortDirection` and `stackedSortByLabel` props to render a `Select` to choose how to sort the `Table` (see the props of `Table.ColHeader` for types)
987
+
988
+ ##### Table cells
989
+
990
+ - It needs to have an optional `header` prop and should display its value so the user knows which column the cell's value belongs to (you can read whether the table is using `stacked` layout from [TableContext](TableContext).
991
+
992
+ Custom table with `stacked` layout support:
993
+
994
+ ```js
995
+ ---
996
+ type: example
997
+ ---
998
+ const CustomTableCell = ({ children, header }) => {
999
+ const { isStacked } = useContext(TableContext)
1000
+ if (isStacked) {
1001
+ let headerTxt
1002
+ if (typeof header === 'function') {
1003
+ headerTxt = React.createElement(header)
1004
+ } else {
1005
+ headerTxt = header
1006
+ }
1007
+ return (
1008
+ <div role="cell">
1009
+ {headerTxt && headerTxt}
1010
+ {headerTxt && ': '}
1011
+ {children}
1012
+ </div>
1013
+ )
1014
+ }
1015
+ return <td>{children}</td>
1016
+ }
1017
+
1018
+ const CustomTableRow = ({ children }) => {
1019
+ const { hover, headers, isStacked } = useContext(TableContext)
1020
+ const [isHovered, setIsHovered] = useState(false)
1021
+
1022
+ const Tag = isStacked ? 'div' : 'tr'
1023
+ const rowStyle =
1024
+ hover && isHovered
1025
+ ? { backgroundColor: 'SeaGreen' }
1026
+ : { backgroundColor: 'white' }
1027
+
1028
+ return (
1029
+ <Tag
1030
+ style={rowStyle}
1031
+ role={isStacked ? 'row' : undefined}
1032
+ onMouseOver={() => setIsHovered(true)}
1033
+ onMouseOut={() => setIsHovered(false)}
1034
+ >
1035
+ {React.Children.toArray(children)
1036
+ .filter(React.isValidElement)
1037
+ .map((child, index) => {
1038
+ return React.cloneElement(child, {
1039
+ key: child.props.name,
1040
+ // used by `CustomTableCell` to render its column title in `stacked` layout
1041
+ header: headers && headers[index]
1042
+ })
1043
+ })}
1044
+ </Tag>
1045
+ )
1046
+ }
1047
+
1048
+ const Example = () => {
1049
+ const [layout, setLayout] = useState('auto')
1050
+ const [hover, setHover] = useState(false)
1051
+
1052
+ const handleChange = (field, value) => {
1053
+ if (field === 'layout') {
1054
+ setLayout(value)
1055
+ } else if (field === 'hover') {
1056
+ setHover(!hover)
1057
+ }
1058
+ }
1059
+
1060
+ const renderOptions = () => (
1061
+ <Flex alignItems="start">
1062
+ <Flex.Item margin="small">
1063
+ <RadioInputGroup
1064
+ name="customStackedLayout"
1065
+ description="Layout"
1066
+ value={layout}
1067
+ onChange={(e, value) => handleChange('layout', value)}
1068
+ >
1069
+ <RadioInput label="auto" value="auto" />
1070
+ <RadioInput label="fixed" value="fixed" />
1071
+ <RadioInput label="stacked" value="stacked" />
1072
+ </RadioInputGroup>
1073
+ </Flex.Item>
1074
+ <Flex.Item margin="small">
1075
+ <Checkbox
1076
+ label="hover"
1077
+ checked={hover}
1078
+ onChange={(e, value) => handleChange('hover', !hover)}
1079
+ />
1080
+ </Flex.Item>
1081
+ </Flex>
1082
+ )
1083
+
1084
+ return (
1085
+ <div>
1086
+ {renderOptions()}
1087
+ <Table caption="Top rated movies" layout={layout} hover={hover}>
1088
+ <Table.Head>
1089
+ <CustomTableRow>
1090
+ <CustomTableCell scope="col">Rank</CustomTableCell>
1091
+ <CustomTableCell scope="col">Title</CustomTableCell>
1092
+ <CustomTableCell scope="col">Year</CustomTableCell>
1093
+ <CustomTableCell scope="col">Rating</CustomTableCell>
1094
+ </CustomTableRow>
1095
+ </Table.Head>
1096
+ <Table.Body>
1097
+ <CustomTableRow>
1098
+ <CustomTableCell scope="row">1</CustomTableCell>
1099
+ <CustomTableCell>The Godfather</CustomTableCell>
1100
+ <CustomTableCell>1972</CustomTableCell>
1101
+ <CustomTableCell>9.2</CustomTableCell>
1102
+ </CustomTableRow>
1103
+ <CustomTableRow>
1104
+ <CustomTableCell scope="row">2</CustomTableCell>
1105
+ <CustomTableCell>The Godfather: Part II</CustomTableCell>
1106
+ <CustomTableCell>1974</CustomTableCell>
1107
+ <CustomTableCell>9.0</CustomTableCell>
1108
+ </CustomTableRow>
1109
+ </Table.Body>
1110
+ </Table>
1111
+ </div>
1112
+ )
1113
+ }
1114
+
1115
+ render(<Example />)
1116
+ ```
1117
+
1118
+ ### Guidelines
1119
+
1120
+ ```js
1121
+ ---
1122
+ type: embed
1123
+ ---
1124
+ <Guidelines>
1125
+ <Figure recommendation="yes" title="Do">
1126
+ <Figure.Item>Column headers can be sortable</Figure.Item>
1127
+ <Figure.Item>Use the <Link href="#CondensedButton">CondensedButton</Link> and <strong>small</strong> inputs inside cells</Figure.Item>
1128
+ <Figure.Item>Actions must relate to that row only</Figure.Item>
1129
+ <Figure.Item>Use horizontal and vertical alignment that makes sense with the content</Figure.Item>
1130
+ <Figure.Item>Start align US dates and apply i18n</Figure.Item>
1131
+ <Figure.Item>Start align text and alphanumeric</Figure.Item>
1132
+ <Figure.Item>Start align currency</Figure.Item>
1133
+ <Figure.Item>End align quantities and decimals</Figure.Item>
1134
+ <Figure.Item>Table headers should have the same start or end alignment as their column contents</Figure.Item>
1135
+ </Figure>
1136
+ <Figure recommendation="no" title="Don't">
1137
+ <Figure.Item>Center align more than 4 characters/numbers, choose start or end</Figure.Item>
1138
+ <Figure.Item>Use to create a layout</Figure.Item>
1139
+ <Figure.Item>Use zebra stripes</Figure.Item>
1140
+ <Figure.Item>Bottom align ever</Figure.Item>
1141
+ </Figure>
1142
+ </Guidelines>
1143
+ ```
1144
+
1145
+ ```js
1146
+ ---
1147
+ type: embed
1148
+ ---
1149
+ <Guidelines>
1150
+ <Figure recommendation="a11y" title="Accessibility">
1151
+ <Figure.Item>In the case of a Table with selection, to prevent screenreaders from announcing the label of the select all checkbox when navigating in the rows, an aria-label should be added to the corresponding Table.Colheader like in our example above.
1152
+ </Figure.Item>
1153
+ </Figure>
1154
+ </Guidelines>
1155
+ ```