@kylincloud/flamegraph 0.35.6

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 (267) hide show
  1. package/CHANGELOG.md +1211 -0
  2. package/LICENSE +202 -0
  3. package/README.md +251 -0
  4. package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts +2 -0
  5. package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts.map +1 -0
  6. package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts +17 -0
  7. package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts.map +1 -0
  8. package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts +14 -0
  9. package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts.map +1 -0
  10. package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts +9 -0
  11. package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts.map +1 -0
  12. package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts +8 -0
  13. package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts.map +1 -0
  14. package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts +96 -0
  15. package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts.map +1 -0
  16. package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts +27 -0
  17. package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts.map +1 -0
  18. package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts +7 -0
  19. package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts.map +1 -0
  20. package/dist/FlameGraph/FlameGraphComponent/Header.d.ts +12 -0
  21. package/dist/FlameGraph/FlameGraphComponent/Header.d.ts.map +1 -0
  22. package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts +18 -0
  23. package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts.map +1 -0
  24. package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts +2 -0
  25. package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts.map +1 -0
  26. package/dist/FlameGraph/FlameGraphComponent/color.d.ts +20 -0
  27. package/dist/FlameGraph/FlameGraphComponent/color.d.ts.map +1 -0
  28. package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts +11 -0
  29. package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts.map +1 -0
  30. package/dist/FlameGraph/FlameGraphComponent/constants.d.ts +6 -0
  31. package/dist/FlameGraph/FlameGraphComponent/constants.d.ts.map +1 -0
  32. package/dist/FlameGraph/FlameGraphComponent/index.d.ts +37 -0
  33. package/dist/FlameGraph/FlameGraphComponent/index.d.ts.map +1 -0
  34. package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts +2 -0
  35. package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts.map +1 -0
  36. package/dist/FlameGraph/FlameGraphComponent/testData.d.ts +53 -0
  37. package/dist/FlameGraph/FlameGraphComponent/testData.d.ts.map +1 -0
  38. package/dist/FlameGraph/FlameGraphComponent/utils.d.ts +6 -0
  39. package/dist/FlameGraph/FlameGraphComponent/utils.d.ts.map +1 -0
  40. package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts +2 -0
  41. package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts.map +1 -0
  42. package/dist/FlameGraph/FlameGraphRenderer.d.ts +86 -0
  43. package/dist/FlameGraph/FlameGraphRenderer.d.ts.map +1 -0
  44. package/dist/FlameGraph/decode.d.ts +27 -0
  45. package/dist/FlameGraph/decode.d.ts.map +1 -0
  46. package/dist/FlameGraph/normalize.d.ts +6 -0
  47. package/dist/FlameGraph/normalize.d.ts.map +1 -0
  48. package/dist/FlameGraph/uniqueness.d.ts +3 -0
  49. package/dist/FlameGraph/uniqueness.d.ts.map +1 -0
  50. package/dist/FlamegraphRenderer.d.ts +19 -0
  51. package/dist/FlamegraphRenderer.d.ts.map +1 -0
  52. package/dist/Icons.d.ts +9 -0
  53. package/dist/Icons.d.ts.map +1 -0
  54. package/dist/ProfilerTable.d.ts +21 -0
  55. package/dist/ProfilerTable.d.ts.map +1 -0
  56. package/dist/SharedQueryInput.d.ts +10 -0
  57. package/dist/SharedQueryInput.d.ts.map +1 -0
  58. package/dist/Toolbar.d.ts +31 -0
  59. package/dist/Toolbar.d.ts.map +1 -0
  60. package/dist/Tooltip/FlamegraphTooltip.d.ts +59 -0
  61. package/dist/Tooltip/FlamegraphTooltip.d.ts.map +1 -0
  62. package/dist/Tooltip/LeftClickIcon.d.ts +2 -0
  63. package/dist/Tooltip/LeftClickIcon.d.ts.map +1 -0
  64. package/dist/Tooltip/RightClickIcon.d.ts +2 -0
  65. package/dist/Tooltip/RightClickIcon.d.ts.map +1 -0
  66. package/dist/Tooltip/TableTooltip.d.ts +12 -0
  67. package/dist/Tooltip/TableTooltip.d.ts.map +1 -0
  68. package/dist/Tooltip/Tooltip.d.ts +29 -0
  69. package/dist/Tooltip/Tooltip.d.ts.map +1 -0
  70. package/dist/convert/convertJaegerTraceToProfile.d.ts +3 -0
  71. package/dist/convert/convertJaegerTraceToProfile.d.ts.map +1 -0
  72. package/dist/convert/diffTwoProfiles.d.ts +3 -0
  73. package/dist/convert/diffTwoProfiles.d.ts.map +1 -0
  74. package/dist/convert/flamebearersToTree.d.ts +11 -0
  75. package/dist/convert/flamebearersToTree.d.ts.map +1 -0
  76. package/dist/convert/sandwichViewProfiles.d.ts +14 -0
  77. package/dist/convert/sandwichViewProfiles.d.ts.map +1 -0
  78. package/dist/convert/subtract.d.ts +3 -0
  79. package/dist/convert/subtract.d.ts.map +1 -0
  80. package/dist/convert/testData.d.ts +50 -0
  81. package/dist/convert/testData.d.ts.map +1 -0
  82. package/dist/convert/toGraphviz.d.ts +3 -0
  83. package/dist/convert/toGraphviz.d.ts.map +1 -0
  84. package/dist/fitMode/fitMode.d.ts +42 -0
  85. package/dist/fitMode/fitMode.d.ts.map +1 -0
  86. package/dist/format/format.d.ts +42 -0
  87. package/dist/format/format.d.ts.map +1 -0
  88. package/dist/i18n.d.ts +55 -0
  89. package/dist/i18n.d.ts.map +1 -0
  90. package/dist/index.cjs.css +792 -0
  91. package/dist/index.cjs.js +5087 -0
  92. package/dist/index.d.ts +4 -0
  93. package/dist/index.d.ts.map +1 -0
  94. package/dist/index.esm.css +792 -0
  95. package/dist/index.esm.js +5079 -0
  96. package/dist/index.node.d.ts +9 -0
  97. package/dist/index.node.d.ts.map +1 -0
  98. package/dist/logo-v3-small-T5VXIMRR.svg +32 -0
  99. package/dist/models/decode.d.ts +3 -0
  100. package/dist/models/decode.d.ts.map +1 -0
  101. package/dist/models/flamebearer.d.ts +63 -0
  102. package/dist/models/flamebearer.d.ts.map +1 -0
  103. package/dist/models/groups.d.ts +37 -0
  104. package/dist/models/groups.d.ts.map +1 -0
  105. package/dist/models/index.d.ts +8 -0
  106. package/dist/models/index.d.ts.map +1 -0
  107. package/dist/models/profile.d.ts +152 -0
  108. package/dist/models/profile.d.ts.map +1 -0
  109. package/dist/models/spyName.d.ts +8 -0
  110. package/dist/models/spyName.d.ts.map +1 -0
  111. package/dist/models/trace.d.ts +357 -0
  112. package/dist/models/trace.d.ts.map +1 -0
  113. package/dist/models/units.d.ts +6 -0
  114. package/dist/models/units.d.ts.map +1 -0
  115. package/dist/search.d.ts +2 -0
  116. package/dist/search.d.ts.map +1 -0
  117. package/dist/shims/Box.d.ts +38 -0
  118. package/dist/shims/Box.d.ts.map +1 -0
  119. package/dist/shims/Button.d.ts +26 -0
  120. package/dist/shims/Button.d.ts.map +1 -0
  121. package/dist/shims/Dropdown.d.ts +30 -0
  122. package/dist/shims/Dropdown.d.ts.map +1 -0
  123. package/dist/shims/Input.d.ts +19 -0
  124. package/dist/shims/Input.d.ts.map +1 -0
  125. package/dist/shims/LoadingSpinner.d.ts +7 -0
  126. package/dist/shims/LoadingSpinner.d.ts.map +1 -0
  127. package/dist/shims/Menu.d.ts +4 -0
  128. package/dist/shims/Menu.d.ts.map +1 -0
  129. package/dist/shims/NoData.d.ts +2 -0
  130. package/dist/shims/NoData.d.ts.map +1 -0
  131. package/dist/shims/Table.d.ts +52 -0
  132. package/dist/shims/Table.d.ts.map +1 -0
  133. package/dist/shims/Tooltip.d.ts +9 -0
  134. package/dist/shims/Tooltip.d.ts.map +1 -0
  135. package/package.json +84 -0
  136. package/src/FlameGraph/FlameGraphComponent/CheckIcon.tsx +27 -0
  137. package/src/FlameGraph/FlameGraphComponent/ContextMenu.module.scss +10 -0
  138. package/src/FlameGraph/FlameGraphComponent/ContextMenu.spec.tsx +84 -0
  139. package/src/FlameGraph/FlameGraphComponent/ContextMenu.tsx +86 -0
  140. package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.module.css +8 -0
  141. package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.tsx +47 -0
  142. package/src/FlameGraph/FlameGraphComponent/DiffLegend.module.css +21 -0
  143. package/src/FlameGraph/FlameGraphComponent/DiffLegend.tsx +52 -0
  144. package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.module.css +40 -0
  145. package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.tsx +129 -0
  146. package/src/FlameGraph/FlameGraphComponent/Flamegraph.spec.ts +552 -0
  147. package/src/FlameGraph/FlameGraphComponent/Flamegraph.ts +446 -0
  148. package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.spec.tsx +233 -0
  149. package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.ts +478 -0
  150. package/src/FlameGraph/FlameGraphComponent/GraphVizPane.tsx +56 -0
  151. package/src/FlameGraph/FlameGraphComponent/GraphVizPanel.module.scss +55 -0
  152. package/src/FlameGraph/FlameGraphComponent/Header.module.css +27 -0
  153. package/src/FlameGraph/FlameGraphComponent/Header.tsx +71 -0
  154. package/src/FlameGraph/FlameGraphComponent/Highlight.module.css +7 -0
  155. package/src/FlameGraph/FlameGraphComponent/Highlight.spec.tsx +53 -0
  156. package/src/FlameGraph/FlameGraphComponent/Highlight.tsx +94 -0
  157. package/src/FlameGraph/FlameGraphComponent/LogoLink.module.scss +10 -0
  158. package/src/FlameGraph/FlameGraphComponent/LogoLink.tsx +101 -0
  159. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-collapses-small-blocks-into-one-1-snap.png +0 -0
  160. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-diff-mode-1-snap.png +0 -0
  161. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-highlighted-flamegraph-1-snap.png +0 -0
  162. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-normal-flamegraph-1-snap.png +0 -0
  163. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-selected-node-1-snap.png +0 -0
  164. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-also-zooms-1-snap.png +0 -0
  165. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-in-the-beginning-1-snap.png +0 -0
  166. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-when-node-is-not-in-the-beginning-1-snap.png +0 -0
  167. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-zoom-top-level-1-snap.png +0 -0
  168. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-complex-flamegraph-1-snap.png +0 -0
  169. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-double-diff-flamegraph-1-snap.png +0 -0
  170. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-double-flamegraph-1-snap.png +0 -0
  171. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-flamegraph-1-snap.png +0 -0
  172. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-flamegraph-1-snap.png +0 -0
  173. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-tree-1-snap.png +0 -0
  174. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-flamegraph-1-snap.png +0 -0
  175. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-with-fit-mode-tail-1-snap.png +0 -0
  176. package/src/FlameGraph/FlameGraphComponent/canvas.module.css +6 -0
  177. package/src/FlameGraph/FlameGraphComponent/color.spec.ts +308 -0
  178. package/src/FlameGraph/FlameGraphComponent/color.ts +167 -0
  179. package/src/FlameGraph/FlameGraphComponent/colorPalette.ts +58 -0
  180. package/src/FlameGraph/FlameGraphComponent/constants.ts +5 -0
  181. package/src/FlameGraph/FlameGraphComponent/index.spec.tsx +291 -0
  182. package/src/FlameGraph/FlameGraphComponent/index.tsx +411 -0
  183. package/src/FlameGraph/FlameGraphComponent/murmur3.ts +97 -0
  184. package/src/FlameGraph/FlameGraphComponent/styles.module.scss +10 -0
  185. package/src/FlameGraph/FlameGraphComponent/testData.ts +427 -0
  186. package/src/FlameGraph/FlameGraphComponent/utils.ts +31 -0
  187. package/src/FlameGraph/FlameGraphComponent/viewTypes.ts +6 -0
  188. package/src/FlameGraph/FlameGraphRenderer.tsx +603 -0
  189. package/src/FlameGraph/FlamegraphRenderer.module.scss +93 -0
  190. package/src/FlameGraph/decode.ts +78 -0
  191. package/src/FlameGraph/normalize.spec.ts +76 -0
  192. package/src/FlameGraph/normalize.ts +60 -0
  193. package/src/FlameGraph/testData.json +423 -0
  194. package/src/FlameGraph/uniqueness.spec.ts +16 -0
  195. package/src/FlameGraph/uniqueness.ts +84 -0
  196. package/src/FlamegraphRenderer.tsx +61 -0
  197. package/src/Icons.tsx +74 -0
  198. package/src/ProfilerTable.tsx +527 -0
  199. package/src/SharedQueryInput.module.scss +82 -0
  200. package/src/SharedQueryInput.tsx +127 -0
  201. package/src/Toolbar.module.scss +117 -0
  202. package/src/Toolbar.spec.tsx +217 -0
  203. package/src/Toolbar.tsx +471 -0
  204. package/src/Tooltip/FlamegraphTooltip.spec.tsx +81 -0
  205. package/src/Tooltip/FlamegraphTooltip.tsx +257 -0
  206. package/src/Tooltip/LeftClickIcon.tsx +18 -0
  207. package/src/Tooltip/RightClickIcon.tsx +18 -0
  208. package/src/Tooltip/TableTooltip.spec.tsx +44 -0
  209. package/src/Tooltip/TableTooltip.tsx +145 -0
  210. package/src/Tooltip/Tooltip.module.scss +71 -0
  211. package/src/Tooltip/Tooltip.spec.tsx +395 -0
  212. package/src/Tooltip/Tooltip.tsx +336 -0
  213. package/src/__snapshots__/Toolbar.spec.tsx.snap +297 -0
  214. package/src/convert/convertJaegerTraceToProfile.ts +97 -0
  215. package/src/convert/diffTwoProfiles.ts +81 -0
  216. package/src/convert/flamebearersToTree.ts +78 -0
  217. package/src/convert/sandwichViewProfiles.spec.ts +65 -0
  218. package/src/convert/sandwichViewProfiles.ts +191 -0
  219. package/src/convert/subtract.ts +87 -0
  220. package/src/convert/testData.ts +145 -0
  221. package/src/convert/toGraphviz.ts +485 -0
  222. package/src/fitMode/fitMode.spec.ts +93 -0
  223. package/src/fitMode/fitMode.ts +122 -0
  224. package/src/format/format.spec.ts +291 -0
  225. package/src/format/format.ts +303 -0
  226. package/src/globals.d.ts +13 -0
  227. package/src/i18n.tsx +293 -0
  228. package/src/index.node.ts +19 -0
  229. package/src/index.spec.tsx +383 -0
  230. package/src/index.tsx +10 -0
  231. package/src/logo-v3-small.svg +32 -0
  232. package/src/models/decode.ts +45 -0
  233. package/src/models/flamebearer.ts +86 -0
  234. package/src/models/groups.ts +14 -0
  235. package/src/models/index.ts +7 -0
  236. package/src/models/profile.spec.ts +32 -0
  237. package/src/models/profile.ts +48 -0
  238. package/src/models/spyName.spec.ts +18 -0
  239. package/src/models/spyName.ts +32 -0
  240. package/src/models/trace.ts +45 -0
  241. package/src/models/units.spec.ts +21 -0
  242. package/src/models/units.ts +24 -0
  243. package/src/sass/_common.scss +206 -0
  244. package/src/sass/_css-variables.scss +201 -0
  245. package/src/sass/_mixins.scss +15 -0
  246. package/src/sass/_sanitize.scss +407 -0
  247. package/src/sass/_variables.scss +53 -0
  248. package/src/sass/flamegraph.scss +18 -0
  249. package/src/search.spec.ts +11 -0
  250. package/src/search.ts +4 -0
  251. package/src/shameful-any.d.ts +2 -0
  252. package/src/shims/Box.module.scss +57 -0
  253. package/src/shims/Box.tsx +105 -0
  254. package/src/shims/Button.module.scss +129 -0
  255. package/src/shims/Button.tsx +128 -0
  256. package/src/shims/Dropdown.module.scss +63 -0
  257. package/src/shims/Dropdown.tsx +96 -0
  258. package/src/shims/Input.module.scss +15 -0
  259. package/src/shims/Input.tsx +55 -0
  260. package/src/shims/LoadingSpinner.tsx +19 -0
  261. package/src/shims/Menu.tsx +9 -0
  262. package/src/shims/NoData.module.scss +6 -0
  263. package/src/shims/NoData.tsx +11 -0
  264. package/src/shims/Table.module.scss +82 -0
  265. package/src/shims/Table.spec.tsx +121 -0
  266. package/src/shims/Table.tsx +252 -0
  267. package/src/shims/Tooltip.tsx +51 -0
@@ -0,0 +1,121 @@
1
+ import React from 'react';
2
+ import { renderHook, act } from '@testing-library/react-hooks';
3
+ import { render, within, screen } from '@testing-library/react';
4
+ import Table, { useTableSort } from './Table';
5
+
6
+ const mockHeadRow = [
7
+ { name: 'self', label: 'test col2', sortable: 1 },
8
+ { name: 'name', label: 'test col1', sortable: 1 },
9
+ { name: 'total', label: 'test col3', sortable: 1 },
10
+ { name: 'selfLeft', label: 'test col4', sortable: 1 },
11
+ { name: 'selfRight', label: 'test col5', sortable: 1 },
12
+ { name: 'selfDiff', label: 'test col6', sortable: 1 },
13
+ { name: 'totalLeft', label: 'test col7', sortable: 1 },
14
+ { name: 'totalRight', label: 'test col8', sortable: 1 },
15
+ { name: 'totalDiff', label: 'test col9', sortable: 1 },
16
+ ];
17
+
18
+ describe('Hook: useTableSort', () => {
19
+ const render = () => renderHook(() => useTableSort(mockHeadRow)).result;
20
+
21
+ it('should return initial sort values', () => {
22
+ const hook = render();
23
+ expect(hook.current).toStrictEqual({
24
+ sortBy: 'self',
25
+ sortByDirection: 'desc',
26
+ updateSortParams: expect.any(Function),
27
+ });
28
+ });
29
+
30
+ it('should update sort direction', () => {
31
+ const hook = render();
32
+
33
+ expect(hook.current.sortByDirection).toBe('desc');
34
+ act(() => {
35
+ hook.current.updateSortParams('self');
36
+ });
37
+ expect(hook.current.sortByDirection).toBe('asc');
38
+ });
39
+
40
+ it('should update sort value and sort direction', () => {
41
+ const hook = render();
42
+
43
+ expect(hook.current).toMatchObject({
44
+ sortBy: 'self',
45
+ sortByDirection: 'desc',
46
+ });
47
+
48
+ act(() => {
49
+ hook.current.updateSortParams('name');
50
+ });
51
+ expect(hook.current).toMatchObject({
52
+ sortBy: 'name',
53
+ sortByDirection: 'desc',
54
+ });
55
+
56
+ act(() => {
57
+ hook.current.updateSortParams('name');
58
+ });
59
+ expect(hook.current).toMatchObject({
60
+ sortBy: 'name',
61
+ sortByDirection: 'asc',
62
+ });
63
+ });
64
+ });
65
+
66
+ describe('pagination', () => {
67
+ const header = [{ name: 'id', label: 'Id' }];
68
+ const rows = [
69
+ { cells: [{ value: 1 }] },
70
+ { cells: [{ value: 2 }] },
71
+ { cells: [{ value: 3 }] },
72
+ ];
73
+
74
+ it('does not paginate by default', async () => {
75
+ render(
76
+ <Table table={{ type: 'filled', headRow: header, bodyRows: rows }} />
77
+ );
78
+
79
+ const tbody = document.getElementsByTagName('tbody')[0];
80
+ const items = await within(tbody).findAllByRole('row');
81
+ expect(items).toHaveLength(rows.length);
82
+ });
83
+
84
+ it('paginates', async () => {
85
+ render(
86
+ <Table
87
+ itemsPerPage={1}
88
+ table={{
89
+ type: 'filled',
90
+ headRow: header,
91
+ bodyRows: rows,
92
+ }}
93
+ />
94
+ );
95
+
96
+ const tbody = document.getElementsByTagName('tbody')[0];
97
+
98
+ // First page
99
+ expect(screen.getByLabelText('Previous Page')).toBeDisabled();
100
+ expect(screen.getByLabelText('Next Page')).toBeEnabled();
101
+ let items = await within(tbody).findAllByRole('row');
102
+ expect(items).toHaveLength(1);
103
+ expect(items[0]).toHaveTextContent('1');
104
+
105
+ // Second page
106
+ screen.getByLabelText('Next Page').click();
107
+ expect(screen.getByLabelText('Previous Page')).toBeEnabled();
108
+ expect(screen.getByLabelText('Next Page')).toBeEnabled();
109
+ items = await within(tbody).findAllByRole('row');
110
+ expect(items).toHaveLength(1);
111
+ expect(items[0]).toHaveTextContent('2');
112
+
113
+ // Third page
114
+ screen.getByLabelText('Next Page').click();
115
+ expect(screen.getByLabelText('Previous Page')).toBeEnabled();
116
+ expect(screen.getByLabelText('Next Page')).toBeDisabled();
117
+ items = await within(tbody).findAllByRole('row');
118
+ expect(items).toHaveLength(1);
119
+ expect(items[0]).toHaveTextContent('3');
120
+ });
121
+ });
@@ -0,0 +1,252 @@
1
+ /* eslint-disable react/jsx-props-no-spreading */
2
+ import React, { useState, ReactNode, CSSProperties, RefObject } from 'react';
3
+ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons/faChevronLeft';
4
+ import { faChevronRight } from '@fortawesome/free-solid-svg-icons/faChevronRight';
5
+ import clsx from 'clsx';
6
+
7
+ // eslint-disable-next-line css-modules/no-unused-class
8
+ import styles from './Table.module.scss';
9
+ // import LoadingSpinner from './LoadingSpinner';
10
+ import Button from './Button';
11
+
12
+ interface CustomProp {
13
+ [k: string]: string | CSSProperties | ReactNode | number | undefined;
14
+ }
15
+
16
+ export interface Cell extends CustomProp {
17
+ value: ReactNode | string;
18
+ style?: CSSProperties;
19
+ }
20
+
21
+ interface HeadCell extends CustomProp {
22
+ name: string;
23
+ label: string;
24
+ sortable?: number;
25
+ default?: boolean;
26
+ }
27
+
28
+ export interface BodyRow {
29
+ 'data-row'?: string;
30
+ isRowSelected?: boolean;
31
+ isRowDisabled?: boolean;
32
+ cells: Cell[];
33
+ onClick?: () => void;
34
+ className?: string;
35
+ }
36
+
37
+ export type TableBodyType =
38
+ | {
39
+ type: 'not-filled';
40
+ value: string | ReactNode;
41
+ bodyClassName?: string;
42
+ }
43
+ | {
44
+ type: 'filled';
45
+ bodyRows: BodyRow[];
46
+ };
47
+
48
+ type Table = TableBodyType & {
49
+ headRow: HeadCell[];
50
+ };
51
+
52
+ interface TableSortProps {
53
+ sortBy: string;
54
+ updateSortParams: (v: string) => void;
55
+ sortByDirection: 'desc' | 'asc';
56
+ }
57
+
58
+ export const useTableSort = (headRow: HeadCell[]): TableSortProps => {
59
+ const defaultSortByCell =
60
+ headRow.filter((row) => row?.default)[0] || headRow[0];
61
+ const [sortBy, updateSortBy] = useState(defaultSortByCell.name);
62
+ const [sortByDirection, setSortByDirection] = useState<'desc' | 'asc'>(
63
+ 'desc'
64
+ );
65
+
66
+ const updateSortParams = (newSortBy: string) => {
67
+ let dir = sortByDirection;
68
+
69
+ if (sortBy === newSortBy) {
70
+ dir = dir === 'asc' ? 'desc' : 'asc';
71
+ } else {
72
+ dir = 'desc';
73
+ }
74
+
75
+ updateSortBy(newSortBy);
76
+ setSortByDirection(dir);
77
+ };
78
+
79
+ return { sortBy, sortByDirection, updateSortParams };
80
+ };
81
+
82
+ interface TableProps {
83
+ sortByDirection?: string;
84
+ sortBy?: string;
85
+ updateSortParams?: (newSortBy: string) => void;
86
+ table: Table;
87
+ tableBodyRef?: RefObject<HTMLTableSectionElement>;
88
+ className?: string;
89
+ isLoading?: boolean;
90
+ /* enables pagination */
91
+ itemsPerPage?: number;
92
+ }
93
+
94
+ function Table({
95
+ sortByDirection,
96
+ sortBy,
97
+ updateSortParams,
98
+ table,
99
+ tableBodyRef,
100
+ className,
101
+ isLoading,
102
+ itemsPerPage,
103
+ }: TableProps) {
104
+ const hasSort = sortByDirection && sortBy && updateSortParams;
105
+ const [currPage, setCurrPage] = useState(0);
106
+
107
+ return isLoading ? (
108
+ <div className={styles.loadingSpinner}>
109
+ {/* <LoadingSpinner /> */}
110
+ </div>
111
+ ) : (
112
+ <>
113
+ <table
114
+ className={clsx(styles.table, {
115
+ [className || '']: className,
116
+ })}
117
+ data-testid="table-ui"
118
+ >
119
+ <thead>
120
+ <tr>
121
+ {table.headRow.map(
122
+ ({ sortable, label, name, ...rest }, idx: number) =>
123
+ !sortable || table.type === 'not-filled' || !hasSort ? (
124
+ // eslint-disable-next-line react/no-array-index-key
125
+ <th key={idx} {...rest}>
126
+ {label}
127
+ </th>
128
+ ) : (
129
+ <th
130
+ {...rest}
131
+ // eslint-disable-next-line react/no-array-index-key
132
+ key={idx}
133
+ className={styles.sortable}
134
+ onClick={() => updateSortParams(name)}
135
+ >
136
+ {label}
137
+ <span
138
+ className={clsx(styles.sortArrow, {
139
+ [styles[sortByDirection]]: sortBy === name,
140
+ })}
141
+ />
142
+ </th>
143
+ )
144
+ )}
145
+ </tr>
146
+ </thead>
147
+ <tbody ref={tableBodyRef}>
148
+ {table.type === 'not-filled' ? (
149
+ <tr className={table?.bodyClassName}>
150
+ <td colSpan={table.headRow.length}>{table.value}</td>
151
+ </tr>
152
+ ) : (
153
+ paginate(table.bodyRows, currPage, itemsPerPage).map(
154
+ ({ cells, isRowSelected, isRowDisabled, className, ...rest }) => {
155
+ // The problem is that when you switch apps or time-range and the function
156
+ // names stay the same it leads to an issue where rows don't get re-rendered
157
+ // So we force a rerender each time.
158
+ const renderID = Math.random();
159
+
160
+ return (
161
+ <tr
162
+ key={renderID}
163
+ {...rest}
164
+ className={clsx(className, {
165
+ [styles.isRowSelected]: isRowSelected,
166
+ [styles.isRowDisabled]: isRowDisabled,
167
+ })}
168
+ >
169
+ {cells &&
170
+ cells.map(
171
+ ({ style, value, ...rest }: Cell, index: number) => (
172
+ // eslint-disable-next-line react/no-array-index-key
173
+ <td key={renderID + index} style={style} {...rest}>
174
+ {value}
175
+ </td>
176
+ )
177
+ )}
178
+ </tr>
179
+ );
180
+ }
181
+ )
182
+ )}
183
+ </tbody>
184
+ </table>
185
+ <PaginationNavigation
186
+ bodyRows={table.type === 'filled' ? table.bodyRows : undefined}
187
+ itemsPerPage={itemsPerPage}
188
+ currPage={currPage}
189
+ setCurrPage={setCurrPage}
190
+ />
191
+ </>
192
+ );
193
+ }
194
+
195
+ function paginate(
196
+ bodyRows: Extract<Table, { type: 'filled' }>['bodyRows'],
197
+ currPage: number,
198
+ itemsPerPage?: TableProps['itemsPerPage']
199
+ ) {
200
+ if (!itemsPerPage) {
201
+ return bodyRows;
202
+ }
203
+
204
+ return bodyRows.slice(currPage * itemsPerPage, itemsPerPage * (currPage + 1));
205
+ }
206
+
207
+ interface PaginationNavigationProps {
208
+ bodyRows?: Extract<Table, { type: 'filled' }>['bodyRows'];
209
+ currPage: number;
210
+ itemsPerPage?: TableProps['itemsPerPage'];
211
+ setCurrPage: (i: number) => void;
212
+ }
213
+
214
+ function PaginationNavigation({
215
+ itemsPerPage,
216
+ currPage,
217
+ setCurrPage,
218
+ bodyRows,
219
+ }: PaginationNavigationProps) {
220
+ if (!itemsPerPage) {
221
+ return null;
222
+ }
223
+
224
+ const isThereNextPage = bodyRows
225
+ ? paginate(bodyRows, currPage + 1, itemsPerPage).length > 0
226
+ : false;
227
+
228
+ const isTherePreviousPage = bodyRows
229
+ ? paginate(bodyRows, currPage - 1, itemsPerPage).length > 0
230
+ : false;
231
+
232
+ return (
233
+ <nav className={styles.pagination}>
234
+ <Button
235
+ aria-label="Previous Page"
236
+ disabled={!isTherePreviousPage}
237
+ kind="float"
238
+ icon={faChevronLeft}
239
+ onClick={() => setCurrPage(currPage - 1)}
240
+ />
241
+ <Button
242
+ disabled={!isThereNextPage}
243
+ aria-label="Next Page"
244
+ kind="float"
245
+ icon={faChevronRight}
246
+ onClick={() => setCurrPage(currPage + 1)}
247
+ />
248
+ </nav>
249
+ );
250
+ }
251
+
252
+ export default Table;
@@ -0,0 +1,51 @@
1
+ // src/shims/Tooltip.tsx
2
+ import React from 'react'
3
+
4
+ export interface TooltipProps {
5
+ placement?: string
6
+ title?: React.ReactNode
7
+ content?: React.ReactNode
8
+ children: React.ReactNode
9
+ }
10
+
11
+ /**
12
+ * 尝试把 ReactNode 转成纯文本,用于原生 title
13
+ * 足够应付大部分简单场景(字符串、数字、嵌套一两层标签等)
14
+ */
15
+ function nodeToPlainText(node: React.ReactNode): string | undefined {
16
+ if (node == null || node === false) return undefined
17
+
18
+ if (typeof node === 'string' || typeof node === 'number') {
19
+ return String(node)
20
+ }
21
+
22
+ if (Array.isArray(node)) {
23
+ const parts = node
24
+ .map((n) => nodeToPlainText(n))
25
+ .filter((s): s is string => !!s)
26
+ return parts.length ? parts.join(' ') : undefined
27
+ }
28
+
29
+ if (React.isValidElement(node)) {
30
+ return nodeToPlainText(node.props.children)
31
+ }
32
+
33
+ return undefined
34
+ }
35
+
36
+ export const Tooltip: React.FC<TooltipProps> = ({
37
+ placement, // 目前不用,但保留参数以兼容原版
38
+ title,
39
+ content,
40
+ children,
41
+ }) => {
42
+ const text = title ?? content
43
+ const titleStr = nodeToPlainText(text)
44
+
45
+ // 没有文本就不加 title,避免空 tooltip
46
+ if (!titleStr) {
47
+ return <>{children}</>
48
+ }
49
+
50
+ return <span title={titleStr}>{children}</span>
51
+ }