solid-recharts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (639) hide show
  1. package/NEXT_SESSION_PROMPT.md +114 -0
  2. package/package.json +28 -0
  3. package/src/animation/AnimatedItems.tsx +169 -0
  4. package/src/animation/AnimationController.ts +24 -0
  5. package/src/animation/AnimationControllerImpl.ts +37 -0
  6. package/src/animation/AnimationHandle.ts +273 -0
  7. package/src/animation/CSSTransitionAnimate.tsx +142 -0
  8. package/src/animation/JavascriptAnimate.tsx +107 -0
  9. package/src/animation/easing.ts +261 -0
  10. package/src/animation/matchBy.ts +211 -0
  11. package/src/animation/timeoutController.ts +51 -0
  12. package/src/animation/useAnimationController.tsx +31 -0
  13. package/src/animation/useAnimationStartSnapshot.ts +73 -0
  14. package/src/animation/util.ts +16 -0
  15. package/src/cartesian/Area.tsx +810 -0
  16. package/src/cartesian/AreaRevealShape.tsx +250 -0
  17. package/src/cartesian/Bar.tsx +1216 -0
  18. package/src/cartesian/BarStack.tsx +148 -0
  19. package/src/cartesian/Brush.tsx +653 -0
  20. package/src/cartesian/CartesianAxis.tsx +541 -0
  21. package/src/cartesian/CartesianGrid.tsx +605 -0
  22. package/src/cartesian/ErrorBar.tsx +319 -0
  23. package/src/cartesian/Funnel.tsx +594 -0
  24. package/src/cartesian/GraphicalItemClipPath.tsx +75 -0
  25. package/src/cartesian/Line.tsx +742 -0
  26. package/src/cartesian/LineDrawShape.tsx +190 -0
  27. package/src/cartesian/ReferenceArea.tsx +275 -0
  28. package/src/cartesian/ReferenceDot.tsx +286 -0
  29. package/src/cartesian/ReferenceLine.tsx +414 -0
  30. package/src/cartesian/RenderedTicksReporter.tsx +49 -0
  31. package/src/cartesian/Scatter.tsx +741 -0
  32. package/src/cartesian/XAxis.tsx +293 -0
  33. package/src/cartesian/YAxis.tsx +333 -0
  34. package/src/cartesian/ZAxis.tsx +80 -0
  35. package/src/cartesian/getCartesianPosition.ts +255 -0
  36. package/src/cartesian/getEquidistantTicks.ts +126 -0
  37. package/src/cartesian/getTicks.ts +225 -0
  38. package/src/cartesian/useAnimatedLineLength.ts +84 -0
  39. package/src/chart/AreaChart.tsx +30 -0
  40. package/src/chart/BarChart.tsx +27 -0
  41. package/src/chart/CartesianChart.tsx +91 -0
  42. package/src/chart/CategoricalChart.tsx +60 -0
  43. package/src/chart/ComposedChart.tsx +29 -0
  44. package/src/chart/FunnelChart.tsx +29 -0
  45. package/src/chart/LineChart.tsx +29 -0
  46. package/src/chart/PieChart.tsx +41 -0
  47. package/src/chart/PolarChart.tsx +97 -0
  48. package/src/chart/RadarChart.tsx +37 -0
  49. package/src/chart/RadialBarChart.tsx +39 -0
  50. package/src/chart/RechartsWrapper.tsx +486 -0
  51. package/src/chart/Sankey.tsx +992 -0
  52. package/src/chart/ScatterChart.tsx +29 -0
  53. package/src/chart/SunburstChart.tsx +394 -0
  54. package/src/chart/Treemap.tsx +874 -0
  55. package/src/chart/types.ts +37 -0
  56. package/src/component/ActivePoints.tsx +136 -0
  57. package/src/component/Cell.tsx +72 -0
  58. package/src/component/Cursor.tsx +249 -0
  59. package/src/component/Customized.tsx +8 -0
  60. package/src/component/DefaultLegendContent.tsx +205 -0
  61. package/src/component/DefaultTooltipContent.tsx +247 -0
  62. package/src/component/Dots.tsx +128 -0
  63. package/src/component/Label.tsx +507 -0
  64. package/src/component/LabelList.tsx +270 -0
  65. package/src/component/Legend.tsx +273 -0
  66. package/src/component/ResponsiveContainer.tsx +397 -0
  67. package/src/component/Text.tsx +436 -0
  68. package/src/component/Tooltip.tsx +309 -0
  69. package/src/component/TooltipBoundingBox.tsx +152 -0
  70. package/src/component/responsiveContainerUtils.ts +128 -0
  71. package/src/container/ClipPathProvider.tsx +40 -0
  72. package/src/container/Layer.tsx +38 -0
  73. package/src/container/RootSurface.tsx +124 -0
  74. package/src/container/Surface.tsx +70 -0
  75. package/src/context/ErrorBarContext.tsx +85 -0
  76. package/src/context/PanoramaContext.tsx +13 -0
  77. package/src/context/RegisterGraphicalItemId.tsx +41 -0
  78. package/src/context/accessibilityContext.tsx +10 -0
  79. package/src/context/brushUpdateContext.ts +14 -0
  80. package/src/context/chartDataContext.tsx +79 -0
  81. package/src/context/chartLayoutContext.tsx +158 -0
  82. package/src/context/legendPayloadContext.ts +11 -0
  83. package/src/context/legendPortalContext.ts +11 -0
  84. package/src/context/tooltipContext.ts +72 -0
  85. package/src/context/tooltipPortalContext.ts +14 -0
  86. package/src/context/useTooltipAxis.ts +24 -0
  87. package/src/hooks.ts +312 -0
  88. package/src/index.ts +288 -0
  89. package/src/polar/Pie.tsx +912 -0
  90. package/src/polar/PolarAngleAxis.tsx +494 -0
  91. package/src/polar/PolarGrid.tsx +269 -0
  92. package/src/polar/PolarRadiusAxis.tsx +422 -0
  93. package/src/polar/Radar.tsx +652 -0
  94. package/src/polar/RadialBar.tsx +767 -0
  95. package/src/polar/defaultPolarAngleAxisProps.tsx +33 -0
  96. package/src/polar/defaultPolarRadiusAxisProps.tsx +28 -0
  97. package/src/shape/Cross.tsx +83 -0
  98. package/src/shape/Curve.tsx +335 -0
  99. package/src/shape/Dot.tsx +49 -0
  100. package/src/shape/Polygon.tsx +168 -0
  101. package/src/shape/Rectangle.tsx +301 -0
  102. package/src/shape/Sector.tsx +304 -0
  103. package/src/shape/Symbols.tsx +144 -0
  104. package/src/shape/Trapezoid.tsx +228 -0
  105. package/src/state/RechartsReduxContext.tsx +8 -0
  106. package/src/state/RechartsStoreContext.ts +15 -0
  107. package/src/state/RechartsStoreProvider.tsx +27 -0
  108. package/src/state/ReportChartProps.tsx +19 -0
  109. package/src/state/ReportEventSettings.tsx +19 -0
  110. package/src/state/ReportMainChartProps.tsx +35 -0
  111. package/src/state/ReportPolarOptions.tsx +28 -0
  112. package/src/state/SetLegendPayload.ts +50 -0
  113. package/src/state/SetTooltipEntrySettings.tsx +46 -0
  114. package/src/state/brushSlice.ts +60 -0
  115. package/src/state/cartesianAxisSlice.ts +288 -0
  116. package/src/state/chartDataSlice.ts +115 -0
  117. package/src/state/errorBarSlice.ts +84 -0
  118. package/src/state/eventSettingsSlice.ts +41 -0
  119. package/src/state/externalEventsMiddleware.ts +136 -0
  120. package/src/state/graphicalItemsSlice.ts +162 -0
  121. package/src/state/hooks.ts +42 -0
  122. package/src/state/keyboardEventsMiddleware.ts +213 -0
  123. package/src/state/layoutSlice.ts +71 -0
  124. package/src/state/legendSlice.ts +107 -0
  125. package/src/state/mouseEventsMiddleware.ts +103 -0
  126. package/src/state/optionsSlice.ts +64 -0
  127. package/src/state/polarAxisSlice.ts +65 -0
  128. package/src/state/polarOptionsSlice.ts +54 -0
  129. package/src/state/reduxDevtoolsJsonStringifyReplacer.ts +15 -0
  130. package/src/state/referenceElementsSlice.ts +121 -0
  131. package/src/state/renderedTicksSlice.ts +61 -0
  132. package/src/state/rootPropsSlice.ts +70 -0
  133. package/src/state/selectors/areaSelectors.ts +169 -0
  134. package/src/state/selectors/arrayEqualityCheck.ts +8 -0
  135. package/src/state/selectors/axisSelectors.ts +2196 -0
  136. package/src/state/selectors/barSelectors.ts +319 -0
  137. package/src/state/selectors/barStackSelectors.ts +79 -0
  138. package/src/state/selectors/brushSelectors.ts +40 -0
  139. package/src/state/selectors/combiners/combineActiveLabel.ts +21 -0
  140. package/src/state/selectors/combiners/combineActiveTooltipIndex.ts +93 -0
  141. package/src/state/selectors/combiners/combineAllBarPositions.ts +131 -0
  142. package/src/state/selectors/combiners/combineAxisRangeWithReverse.ts +17 -0
  143. package/src/state/selectors/combiners/combineBarPosition.ts +24 -0
  144. package/src/state/selectors/combiners/combineBarSizeList.ts +75 -0
  145. package/src/state/selectors/combiners/combineCheckedDomain.ts +49 -0
  146. package/src/state/selectors/combiners/combineConfiguredScale.ts +121 -0
  147. package/src/state/selectors/combiners/combineCoordinateForDefaultIndex.ts +43 -0
  148. package/src/state/selectors/combiners/combineDisplayedStackedData.ts +50 -0
  149. package/src/state/selectors/combiners/combineInverseScaleFunction.ts +17 -0
  150. package/src/state/selectors/combiners/combineRealScaleType.ts +48 -0
  151. package/src/state/selectors/combiners/combineStackedData.ts +28 -0
  152. package/src/state/selectors/combiners/combineTooltipInteractionState.ts +86 -0
  153. package/src/state/selectors/combiners/combineTooltipPayload.ts +203 -0
  154. package/src/state/selectors/combiners/combineTooltipPayloadConfigurations.ts +45 -0
  155. package/src/state/selectors/containerSelectors.ts +12 -0
  156. package/src/state/selectors/dataSelectors.ts +90 -0
  157. package/src/state/selectors/funnelSelectors.ts +83 -0
  158. package/src/state/selectors/graphicalItemSelectors.ts +23 -0
  159. package/src/state/selectors/legendSelectors.ts +29 -0
  160. package/src/state/selectors/lineSelectors.ts +111 -0
  161. package/src/state/selectors/numberDomainEqualityCheck.ts +8 -0
  162. package/src/state/selectors/pickAxisId.ts +6 -0
  163. package/src/state/selectors/pickAxisType.ts +4 -0
  164. package/src/state/selectors/pieSelectors.ts +103 -0
  165. package/src/state/selectors/polarAxisSelectors.ts +202 -0
  166. package/src/state/selectors/polarGridSelectors.ts +24 -0
  167. package/src/state/selectors/polarScaleSelectors.ts +149 -0
  168. package/src/state/selectors/polarSelectors.ts +211 -0
  169. package/src/state/selectors/radarSelectors.ts +152 -0
  170. package/src/state/selectors/radialBarSelectors.ts +328 -0
  171. package/src/state/selectors/rootPropsSelectors.ts +36 -0
  172. package/src/state/selectors/scatterSelectors.ts +77 -0
  173. package/src/state/selectors/selectAllAxes.ts +10 -0
  174. package/src/state/selectors/selectChartOffset.ts +15 -0
  175. package/src/state/selectors/selectChartOffsetInternal.ts +111 -0
  176. package/src/state/selectors/selectPlotArea.ts +27 -0
  177. package/src/state/selectors/selectTooltipAxisId.ts +6 -0
  178. package/src/state/selectors/selectTooltipAxisType.ts +33 -0
  179. package/src/state/selectors/selectTooltipEventType.ts +45 -0
  180. package/src/state/selectors/selectTooltipPayloadSearcher.ts +7 -0
  181. package/src/state/selectors/selectTooltipSettings.ts +6 -0
  182. package/src/state/selectors/selectTooltipState.ts +5 -0
  183. package/src/state/selectors/tooltipSelectors.ts +690 -0
  184. package/src/state/selectors/touchSelectors.ts +29 -0
  185. package/src/state/store.ts +167 -0
  186. package/src/state/tooltipSlice.ts +289 -0
  187. package/src/state/types/AreaSettings.ts +17 -0
  188. package/src/state/types/BarSettings.ts +16 -0
  189. package/src/state/types/LineSettings.ts +12 -0
  190. package/src/state/types/PieSettings.ts +32 -0
  191. package/src/state/types/RadarSettings.ts +6 -0
  192. package/src/state/types/RadialBarSettings.ts +10 -0
  193. package/src/state/types/ScatterSettings.ts +13 -0
  194. package/src/state/types/StackedGraphicalItem.ts +33 -0
  195. package/src/state/unregisterOnCleanup.ts +26 -0
  196. package/src/state/zIndexSlice.ts +128 -0
  197. package/src/synchronisation/syncRegistry.ts +131 -0
  198. package/src/synchronisation/syncSelectors.ts +7 -0
  199. package/src/synchronisation/types.ts +42 -0
  200. package/src/synchronisation/useChartSynchronisation.tsx +372 -0
  201. package/src/types.ts +39 -0
  202. package/src/util/ActiveShapeUtils.tsx +80 -0
  203. package/src/util/BarUtils.tsx +74 -0
  204. package/src/util/CartesianUtils.ts +53 -0
  205. package/src/util/ChartUtils.ts +696 -0
  206. package/src/util/Constants.ts +42 -0
  207. package/src/util/CssPrefixUtils.ts +24 -0
  208. package/src/util/DOMUtils.ts +141 -0
  209. package/src/util/DataUtils.ts +205 -0
  210. package/src/util/FunnelUtils.tsx +37 -0
  211. package/src/util/Global.ts +15 -0
  212. package/src/util/IfOverflow.ts +17 -0
  213. package/src/util/LRUCache.ts +46 -0
  214. package/src/util/LogUtils.ts +26 -0
  215. package/src/util/PolarUtils.ts +134 -0
  216. package/src/util/RadialBarUtils.tsx +35 -0
  217. package/src/util/ReduceCSSCalc.ts +186 -0
  218. package/src/util/ShallowEqual.ts +43 -0
  219. package/src/util/TickUtils.ts +61 -0
  220. package/src/util/YAxisUtils.ts +49 -0
  221. package/src/util/adaptEventHandlers.ts +69 -0
  222. package/src/util/axisPropsAreEqual.ts +42 -0
  223. package/src/util/collectionUtils.ts +83 -0
  224. package/src/util/createCartesianCharts.tsx +101 -0
  225. package/src/util/createEventProxy.ts +24 -0
  226. package/src/util/createPolarCharts.tsx +86 -0
  227. package/src/util/cursor/getCursorPoints.ts +48 -0
  228. package/src/util/cursor/getCursorRectangle.ts +31 -0
  229. package/src/util/cursor/getRadialCursorPoints.ts +34 -0
  230. package/src/util/excludeEventProps.ts +172 -0
  231. package/src/util/getActiveCoordinate.ts +205 -0
  232. package/src/util/getAxisTypeBasedOnLayout.ts +21 -0
  233. package/src/util/getEveryNth.ts +30 -0
  234. package/src/util/getRadiusAndStrokeWidthFromDot.ts +35 -0
  235. package/src/util/getRelativeCoordinate.ts +84 -0
  236. package/src/util/getSliced.ts +21 -0
  237. package/src/util/isDomainSpecifiedByUser.ts +217 -0
  238. package/src/util/isWellBehavedNumber.ts +10 -0
  239. package/src/util/objectPath.ts +129 -0
  240. package/src/util/payload/getUniqPayload.ts +31 -0
  241. package/src/util/propsAreEqual.ts +137 -0
  242. package/src/util/resolveDefaultProps.tsx +8 -0
  243. package/src/util/round.ts +35 -0
  244. package/src/util/scale/CartesianScaleHelper.ts +72 -0
  245. package/src/util/scale/CustomScaleDefinition.ts +82 -0
  246. package/src/util/scale/RechartsScale.ts +131 -0
  247. package/src/util/scale/createCategoricalInverse.ts +83 -0
  248. package/src/util/scale/getNiceTickValues.ts +334 -0
  249. package/src/util/scale/index.ts +3 -0
  250. package/src/util/scale/util/DecimalLite.ts +194 -0
  251. package/src/util/scale/util/arithmetic.ts +56 -0
  252. package/src/util/stacks/getStackSeriesIdentifier.ts +20 -0
  253. package/src/util/stacks/stackTypes.ts +53 -0
  254. package/src/util/svgPropertiesAndEvents.ts +84 -0
  255. package/src/util/svgPropertiesNoEvents.ts +520 -0
  256. package/src/util/tooltip/translate.ts +179 -0
  257. package/src/util/typedDataKey.ts +8 -0
  258. package/src/util/types.ts +724 -0
  259. package/src/util/useAnimationId.tsx +31 -0
  260. package/src/util/useElementOffset.ts +164 -0
  261. package/src/util/useId.ts +20 -0
  262. package/src/util/usePrefersReducedMotion.ts +44 -0
  263. package/src/util/useUniqueId.ts +34 -0
  264. package/src/zIndex/DefaultZIndexes.tsx +87 -0
  265. package/src/zIndex/ZIndexLayer.tsx +126 -0
  266. package/src/zIndex/ZIndexPortal.tsx +69 -0
  267. package/src/zIndex/getZIndexFromUnknown.tsx +17 -0
  268. package/src/zIndex/zIndexSelectors.ts +46 -0
  269. package/test/_data.ts +408 -0
  270. package/test/animation/AnimationController.typed.spec.ts +53 -0
  271. package/test/animation/AnimationControllerImpl.spec.ts +271 -0
  272. package/test/animation/CSSTransitionAnimate.timing.spec.tsx +473 -0
  273. package/test/animation/JavascriptAnimate.spec.tsx +62 -0
  274. package/test/animation/RechartsAnimation.spec.ts +636 -0
  275. package/test/animation/easing.spec.ts +218 -0
  276. package/test/animation/legacyAnimationLengthChange.spec.tsx +803 -0
  277. package/test/animation/matchBy.spec.ts +467 -0
  278. package/test/animation/mockAnimationController.ts +40 -0
  279. package/test/animation/mockControllerAnimationManager.ts +143 -0
  280. package/test/animation/mockTimeoutController.ts +114 -0
  281. package/test/animation/timeoutController.spec.ts +138 -0
  282. package/test/animation/useAnimationStartSnapshot.spec.ts +147 -0
  283. package/test/animation/util.spec.ts +28 -0
  284. package/test/cartesian/Area.animation.spec.tsx +363 -0
  285. package/test/cartesian/Area.range.spec.tsx +54 -0
  286. package/test/cartesian/Area.spec.tsx +1202 -0
  287. package/test/cartesian/Area.stacked.render.spec.tsx +69 -0
  288. package/test/cartesian/Area.typed.spec.tsx +209 -0
  289. package/test/cartesian/Axis.integration.spec.tsx +158 -0
  290. package/test/cartesian/Bar/Bar.csstransition.spec.tsx +299 -0
  291. package/test/cartesian/Bar/Bar.spec.tsx +2314 -0
  292. package/test/cartesian/Bar/Bar.typed.spec.tsx +192 -0
  293. package/test/cartesian/Bar.animation.spec.tsx +426 -0
  294. package/test/cartesian/Bar.stackDomain.spec.tsx +66 -0
  295. package/test/cartesian/Bar.stacked.render.spec.tsx +53 -0
  296. package/test/cartesian/Bar.truncateByDomain.spec.tsx +237 -0
  297. package/test/cartesian/Bar.zeroDimensionFiltering.spec.tsx +336 -0
  298. package/test/cartesian/BarStack.spec.tsx +91 -0
  299. package/test/cartesian/Brush.keyboard.spec.tsx +88 -0
  300. package/test/cartesian/Brush.panorama.spec.tsx +41 -0
  301. package/test/cartesian/Brush.props.spec.tsx +63 -0
  302. package/test/cartesian/Brush.render.spec.tsx +117 -0
  303. package/test/cartesian/Brush.spec.tsx +522 -0
  304. package/test/cartesian/Brush.stacked.spec.tsx +221 -0
  305. package/test/cartesian/CartesianAxis.render.spec.tsx +50 -0
  306. package/test/cartesian/CartesianAxis.spec.tsx +396 -0
  307. package/test/cartesian/CartesianGrid.render.spec.tsx +39 -0
  308. package/test/cartesian/CartesianGrid.spec.tsx +2103 -0
  309. package/test/cartesian/ErrorBar.render.spec.tsx +69 -0
  310. package/test/cartesian/ErrorBar.spec.tsx +2414 -0
  311. package/test/cartesian/Funnel.animation.spec.tsx +241 -0
  312. package/test/cartesian/Funnel.spec.tsx +226 -0
  313. package/test/cartesian/Funnel.typed.spec.tsx +221 -0
  314. package/test/cartesian/GraphicalItemClipPath.scale-behavior.spec.tsx +627 -0
  315. package/test/cartesian/GraphicalItemClipPath.spec.tsx +427 -0
  316. package/test/cartesian/Line.animation.spec.tsx +643 -0
  317. package/test/cartesian/Line.sparseAnimationPath.spec.tsx +98 -0
  318. package/test/cartesian/Line.spec.tsx +471 -0
  319. package/test/cartesian/Line.typed.spec.tsx +152 -0
  320. package/test/cartesian/ReferenceArea.render.spec.tsx +63 -0
  321. package/test/cartesian/ReferenceArea.spec.tsx +663 -0
  322. package/test/cartesian/ReferenceArea.typed.spec.tsx +74 -0
  323. package/test/cartesian/ReferenceDot.render.spec.tsx +55 -0
  324. package/test/cartesian/ReferenceDot.spec.tsx +462 -0
  325. package/test/cartesian/ReferenceDot.typed.spec.tsx +76 -0
  326. package/test/cartesian/ReferenceLine/ReferenceLine.panorama.spec.tsx +184 -0
  327. package/test/cartesian/ReferenceLine/ReferenceLine.spec.tsx +744 -0
  328. package/test/cartesian/ReferenceLine/ReferenceLine.typed.spec.tsx +74 -0
  329. package/test/cartesian/ReferenceLine/getEndPoints.spec.ts +399 -0
  330. package/test/cartesian/ReferenceLine.render.spec.tsx +134 -0
  331. package/test/cartesian/Scatter.animation.spec.tsx +381 -0
  332. package/test/cartesian/Scatter.render.spec.tsx +61 -0
  333. package/test/cartesian/Scatter.spec.tsx +406 -0
  334. package/test/cartesian/Scatter.typed.spec.tsx +153 -0
  335. package/test/cartesian/XAxis/XAxis.barSize.spec.tsx +336 -0
  336. package/test/cartesian/XAxis/XAxis.brush.spec.tsx +266 -0
  337. package/test/cartesian/XAxis/XAxis.categorydomain.spec.tsx +872 -0
  338. package/test/cartesian/XAxis/XAxis.dataKey.spec.tsx +86 -0
  339. package/test/cartesian/XAxis/XAxis.datatypes.spec.tsx +127 -0
  340. package/test/cartesian/XAxis/XAxis.hide.spec.tsx +106 -0
  341. package/test/cartesian/XAxis/XAxis.multiaxis.spec.tsx +336 -0
  342. package/test/cartesian/XAxis/XAxis.numberdomain.spec.tsx +1466 -0
  343. package/test/cartesian/XAxis/XAxis.padding.spec.tsx +758 -0
  344. package/test/cartesian/XAxis/XAxis.padding_clip.spec.tsx +65 -0
  345. package/test/cartesian/XAxis/XAxis.state.spec.tsx +355 -0
  346. package/test/cartesian/XAxis/XAxis.tick.spec.tsx +493 -0
  347. package/test/cartesian/XAxis/XAxis.timescale.spec.tsx +233 -0
  348. package/test/cartesian/XAxis/XAxis.type.spec.tsx +107 -0
  349. package/test/cartesian/XAxis/XAxis.typed.spec.tsx +102 -0
  350. package/test/cartesian/XAxis/XAxis.vertical.spec.tsx +265 -0
  351. package/test/cartesian/XAxis.tickFormatter.spec.tsx +54 -0
  352. package/test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx +230 -0
  353. package/test/cartesian/YAxis/YAxis.7362.spec.tsx +104 -0
  354. package/test/cartesian/YAxis/YAxis.interval-zero.spec.tsx +88 -0
  355. package/test/cartesian/YAxis/YAxis.label.spec.tsx +219 -0
  356. package/test/cartesian/YAxis/YAxis.spec.tsx +1595 -0
  357. package/test/cartesian/YAxis/YAxis.tick.spec.tsx +428 -0
  358. package/test/cartesian/YAxis/YAxis.tickFormatter.spec.tsx +54 -0
  359. package/test/cartesian/YAxis/YAxis.ticks.spec.tsx +263 -0
  360. package/test/cartesian/YAxis/YAxis.type.spec.tsx +107 -0
  361. package/test/cartesian/YAxis/YAxis.typed.spec.tsx +102 -0
  362. package/test/cartesian/ZAxis.spec.tsx +300 -0
  363. package/test/cartesian/ZAxis.typed.spec.tsx +33 -0
  364. package/test/cartesian/__snapshots__/Brush.spec.tsx.snap +5 -0
  365. package/test/cartesian/axisLineAndStroke.spec.tsx +72 -0
  366. package/test/cartesian/getCartesianPosition.spec.ts +907 -0
  367. package/test/cartesian/getEquidistantTicks.spec.ts +88 -0
  368. package/test/cartesian/getTicks.spec.ts +378 -0
  369. package/test/cartesian/miscFeatureGaps.spec.tsx +89 -0
  370. package/test/cartesian/useAnimatedLineLength.spec.ts +168 -0
  371. package/test/chart/AccessibilityLayer.spec.tsx +569 -0
  372. package/test/chart/AccessibilityScans.spec.tsx +286 -0
  373. package/test/chart/AreaChart.spec.tsx +409 -0
  374. package/test/chart/AreaChart.stacked.spec.tsx +1407 -0
  375. package/test/chart/AreaChart.typed.spec.tsx +39 -0
  376. package/test/chart/BarChart.spec.tsx +3719 -0
  377. package/test/chart/BarChart.typed.spec.tsx +60 -0
  378. package/test/chart/CategoricalChart.spec.tsx +255 -0
  379. package/test/chart/ComposedChart.spec.tsx +199 -0
  380. package/test/chart/ComposedChart.typed.spec.tsx +39 -0
  381. package/test/chart/FunnelChart.spec.tsx +215 -0
  382. package/test/chart/FunnelChart.typed.spec.tsx +39 -0
  383. package/test/chart/LineChart.multiseries.spec.tsx +50 -0
  384. package/test/chart/LineChart.smoke.spec.tsx +50 -0
  385. package/test/chart/LineChart.spec.tsx +930 -0
  386. package/test/chart/LineChart.typed.spec.tsx +39 -0
  387. package/test/chart/NicheChartTooltip.spec.tsx +81 -0
  388. package/test/chart/PieChart.spec.tsx +793 -0
  389. package/test/chart/PieChart.typed.spec.tsx +39 -0
  390. package/test/chart/RadarChart.spec.tsx +530 -0
  391. package/test/chart/RadarChart.typed.spec.tsx +39 -0
  392. package/test/chart/RadialBarChart.5966.spec.tsx +48 -0
  393. package/test/chart/RadialBarChart.spec.tsx +679 -0
  394. package/test/chart/RadialBarChart.typed.spec.tsx +39 -0
  395. package/test/chart/RechartsWrapper.spec.tsx +83 -0
  396. package/test/chart/Sankey.content.spec.tsx +46 -0
  397. package/test/chart/Sankey.spec.tsx +952 -0
  398. package/test/chart/Sankey.typed.spec.tsx +29 -0
  399. package/test/chart/ScatterChart.spec.tsx +2613 -0
  400. package/test/chart/ScatterChart.typed.spec.tsx +39 -0
  401. package/test/chart/SunburstChart.spec.tsx +146 -0
  402. package/test/chart/Treemap.animation.spec.tsx +52 -0
  403. package/test/chart/Treemap.spec.tsx +1003 -0
  404. package/test/chart/Treemap.typed.spec.tsx +26 -0
  405. package/test/chart/chartEvents.spec.tsx +127 -0
  406. package/test/chart/responsive.spec.tsx +620 -0
  407. package/test/component/Cell.render.spec.tsx +155 -0
  408. package/test/component/Cell.spec.tsx +12 -0
  409. package/test/component/Cursor.spec.tsx +186 -0
  410. package/test/component/DefaultTooltipContent.spec.tsx +102 -0
  411. package/test/component/Label.render.spec.tsx +63 -0
  412. package/test/component/Label.spec.tsx +686 -0
  413. package/test/component/Label.typed.spec.tsx +46 -0
  414. package/test/component/LabelList.spec.tsx +188 -0
  415. package/test/component/Legend.itemSorter.spec.tsx +704 -0
  416. package/test/component/Legend.render.spec.tsx +92 -0
  417. package/test/component/Legend.spec.tsx +2213 -0
  418. package/test/component/Legend.typed.spec.tsx +22 -0
  419. package/test/component/ResponsiveContainer.spec.tsx +523 -0
  420. package/test/component/ResponsiveContainerDataTest.spec.tsx +51 -0
  421. package/test/component/Text.spec.tsx +415 -0
  422. package/test/component/Text.typed.spec.tsx +46 -0
  423. package/test/component/Tooltip/ActiveDot.spec.tsx +370 -0
  424. package/test/component/Tooltip/Tooltip.animation.spec.tsx +209 -0
  425. package/test/component/Tooltip/Tooltip.content.spec.tsx +128 -0
  426. package/test/component/Tooltip/Tooltip.formatter.spec.tsx +152 -0
  427. package/test/component/Tooltip/Tooltip.includeHidden.spec.tsx +79 -0
  428. package/test/component/Tooltip/Tooltip.multipleDataArrays.spec.tsx +165 -0
  429. package/test/component/Tooltip/Tooltip.offset.spec.tsx +113 -0
  430. package/test/component/Tooltip/Tooltip.state.spec.tsx +74 -0
  431. package/test/component/Tooltip/Tooltip.sync.spec.tsx +639 -0
  432. package/test/component/Tooltip/defaultIndex.spec.tsx +227 -0
  433. package/test/component/Tooltip/graphicalItem.formatter.spec.tsx +138 -0
  434. package/test/component/Tooltip/itemSorter.spec.tsx +2815 -0
  435. package/test/component/Tooltip/tooltipEventType.spec.tsx +344 -0
  436. package/test/component/Tooltip/tooltipMouseHoverSelectors.ts +41 -0
  437. package/test/component/Tooltip/tooltipTestHelpers.tsx +162 -0
  438. package/test/component/Tooltip.defaultIndex.spec.tsx +48 -0
  439. package/test/component/Tooltip.hover.spec.tsx +61 -0
  440. package/test/component/Tooltip.item.spec.tsx +54 -0
  441. package/test/component/Tooltip.keyboard.spec.tsx +51 -0
  442. package/test/component/Tooltip.payload.spec.tsx +1716 -0
  443. package/test/component/Tooltip.touch.spec.tsx +39 -0
  444. package/test/component/Tooltip.visibility.spec.tsx +1889 -0
  445. package/test/component/TooltipBoundingBox.spec.tsx +89 -0
  446. package/test/component/responsiveContainerUtils.spec.ts +205 -0
  447. package/test/container/ClipPath.spec.tsx +602 -0
  448. package/test/container/chartDimensions.spec.tsx +368 -0
  449. package/test/context/chartLayoutContext.spec.tsx +378 -0
  450. package/test/helper/ExpectAxisDomain.tsx +27 -0
  451. package/test/helper/assertNotNull.ts +6 -0
  452. package/test/helper/assertZIndexLayerOrder.ts +19 -0
  453. package/test/helper/createSelectorTestCase.tsx +176 -0
  454. package/test/helper/expectAnimatedPieAngles.ts +100 -0
  455. package/test/helper/expectAnimatedPiePaths.ts +78 -0
  456. package/test/helper/expectAreaCurve.ts +15 -0
  457. package/test/helper/expectAxisTicks.ts +48 -0
  458. package/test/helper/expectBars.ts +54 -0
  459. package/test/helper/expectFunctionReturning.ts +58 -0
  460. package/test/helper/expectLabel.ts +32 -0
  461. package/test/helper/expectLastCalledWith.ts +48 -0
  462. package/test/helper/expectLine.ts +20 -0
  463. package/test/helper/expectPieSectors.ts +80 -0
  464. package/test/helper/expectScale.ts +37 -0
  465. package/test/helper/expectScatterPoints.ts +52 -0
  466. package/test/helper/expectStackGroups.ts +144 -0
  467. package/test/helper/mockAxes.ts +22 -0
  468. package/test/helper/mockGetBoundingClientRect.ts +66 -0
  469. package/test/helper/mockHTMLElementProperty.ts +19 -0
  470. package/test/helper/mockTouchingElement.ts +27 -0
  471. package/test/helper/offsetHelpers.ts +19 -0
  472. package/test/helper/parameterizedTestCases.tsx +99 -0
  473. package/test/helper/produceState.ts +32 -0
  474. package/test/helper/render.ts +7 -0
  475. package/test/helper/selectorTestHelpers.tsx +63 -0
  476. package/test/helper/trim.spec.ts +35 -0
  477. package/test/helper/trim.ts +11 -0
  478. package/test/hooks/useActiveTooltipDataPoints.spec.tsx +358 -0
  479. package/test/hooks/useAxisDomain.spec.tsx +361 -0
  480. package/test/hooks/useAxisScale.spec.tsx +283 -0
  481. package/test/hooks/useAxisTicks.spec.tsx +130 -0
  482. package/test/hooks/useOffset.spec.tsx +248 -0
  483. package/test/hooks/useTooltipStateHooks.spec.tsx +109 -0
  484. package/test/polar/Pie/Pie-TwoLevelPieChart.spec.tsx +135 -0
  485. package/test/polar/Pie/Pie.spec.tsx +784 -0
  486. package/test/polar/Pie/Pie.typed.spec.tsx +78 -0
  487. package/test/polar/Pie.animation.spec.tsx +529 -0
  488. package/test/polar/PieWithLegend.animation.spec.tsx +142 -0
  489. package/test/polar/PolarAngleAxis/PolarAngleAxis.events.spec.tsx +76 -0
  490. package/test/polar/PolarAngleAxis/PolarAngleAxis.spec.tsx +2079 -0
  491. package/test/polar/PolarAngleAxis/PolarAngleAxis.typed.spec.tsx +102 -0
  492. package/test/polar/PolarAngleAxis.render.spec.tsx +79 -0
  493. package/test/polar/PolarGrid.render.spec.tsx +75 -0
  494. package/test/polar/PolarGrid.spec.tsx +918 -0
  495. package/test/polar/PolarLegendPerRow.render.spec.tsx +85 -0
  496. package/test/polar/PolarRadiusAxis.events.spec.tsx +65 -0
  497. package/test/polar/PolarRadiusAxis.render.spec.tsx +52 -0
  498. package/test/polar/PolarRadiusAxis.spec.tsx +991 -0
  499. package/test/polar/PolarRadiusAxis.typed.spec.tsx +84 -0
  500. package/test/polar/PolarTooltipLegend.render.spec.tsx +80 -0
  501. package/test/polar/Radar.animation.spec.tsx +570 -0
  502. package/test/polar/Radar.render.spec.tsx +71 -0
  503. package/test/polar/Radar.spec.tsx +265 -0
  504. package/test/polar/Radar.typed.spec.tsx +119 -0
  505. package/test/polar/RadialBar/RadialBar.spec.tsx +1521 -0
  506. package/test/polar/RadialBar/RadialBar.typed.spec.tsx +132 -0
  507. package/test/polar/RadialBar.animation.spec.tsx +375 -0
  508. package/test/polar/RadialBar.render.spec.tsx +66 -0
  509. package/test/polar/RadialBar.stacked.spec.tsx +57 -0
  510. package/test/setup.ts +84 -0
  511. package/test/shape/ActiveShape.spec.tsx +181 -0
  512. package/test/shape/Cross.spec.tsx +60 -0
  513. package/test/shape/Cross.typed.spec.tsx +26 -0
  514. package/test/shape/Curve.spec.tsx +368 -0
  515. package/test/shape/Curve.typed.spec.tsx +69 -0
  516. package/test/shape/Dot.spec.tsx +73 -0
  517. package/test/shape/Dot.typed.spec.tsx +30 -0
  518. package/test/shape/Polygon.spec.tsx +357 -0
  519. package/test/shape/Polygon.typed.spec.tsx +28 -0
  520. package/test/shape/Rectangle.animation.spec.tsx +451 -0
  521. package/test/shape/Rectangle.spec.tsx +68 -0
  522. package/test/shape/Rectangle.typed.spec.tsx +24 -0
  523. package/test/shape/Sector.spec.tsx +76 -0
  524. package/test/shape/Sector.typed.spec.tsx +24 -0
  525. package/test/shape/Symbols.spec.tsx +43 -0
  526. package/test/shape/Symbols.typed.spec.tsx +24 -0
  527. package/test/shape/Trapezoid.spec.tsx +50 -0
  528. package/test/shape/Trapezoid.typed.spec.tsx +24 -0
  529. package/test/shape/__snapshots__/Cross.spec.tsx.snap +62 -0
  530. package/test/shape/__snapshots__/Curve.spec.tsx.snap +460 -0
  531. package/test/shape/__snapshots__/Polygon.spec.tsx.snap +58 -0
  532. package/test/shape/__snapshots__/Rectangle.spec.tsx.snap +117 -0
  533. package/test/shape/__snapshots__/Sector.spec.tsx.snap +71 -0
  534. package/test/shape/__snapshots__/Trapezoid.spec.tsx.snap +64 -0
  535. package/test/state/chartDataSlice.spec.ts +53 -0
  536. package/test/state/externalEventsMiddleware.spec.ts +178 -0
  537. package/test/state/graphicalItemsSlice.spec.ts +92 -0
  538. package/test/state/hooks.spec.tsx +68 -0
  539. package/test/state/legendSlice.spec.ts +44 -0
  540. package/test/state/mouseEventsMiddleware.spec.ts +75 -0
  541. package/test/state/optionsSlice.spec.ts +19 -0
  542. package/test/state/reduxDevtoolsJsonStringifyReplacer.spec.ts +25 -0
  543. package/test/state/referenceElementsSlice.spec.ts +84 -0
  544. package/test/state/selectors/areaSelectors.spec.tsx +77 -0
  545. package/test/state/selectors/axisSelectors.spec.tsx +1418 -0
  546. package/test/state/selectors/barStackSelectors.spec.tsx +752 -0
  547. package/test/state/selectors/brushSelectors.spec.tsx +30 -0
  548. package/test/state/selectors/cartesianAxisSlice.spec.ts +96 -0
  549. package/test/state/selectors/combineNiceTicks.spec.ts +207 -0
  550. package/test/state/selectors/combiners/combineActiveTooltipIndex.spec.ts +61 -0
  551. package/test/state/selectors/combiners/combineAllBarPositions.spec.ts +39 -0
  552. package/test/state/selectors/combiners/combineConfiguredScale.spec.ts +56 -0
  553. package/test/state/selectors/combiners/combineDisplayedStackedData.spec.ts +123 -0
  554. package/test/state/selectors/combiners/combineRealScaleType.spec.ts +80 -0
  555. package/test/state/selectors/containerSelectors.spec.tsx +168 -0
  556. package/test/state/selectors/dataSelectors.spec.tsx +136 -0
  557. package/test/state/selectors/legendSelectors.spec.tsx +113 -0
  558. package/test/state/selectors/lineSelectors.spec.tsx +60 -0
  559. package/test/state/selectors/pieSelectors.spec.tsx +72 -0
  560. package/test/state/selectors/radarSelectors.spec.tsx +496 -0
  561. package/test/state/selectors/rootPropsSelectors.spec.tsx +169 -0
  562. package/test/state/selectors/scatterSelectors.spec.tsx +47 -0
  563. package/test/state/selectors/selectActiveTooltipIndex.spec.tsx +234 -0
  564. package/test/state/selectors/selectAllAxes.spec.tsx +18 -0
  565. package/test/state/selectors/selectAxisDomain.spec.tsx +21 -0
  566. package/test/state/selectors/selectAxisDomainIncludingNiceTicks.spec.tsx +19 -0
  567. package/test/state/selectors/selectAxisRangeWithReverse.spec.tsx +22 -0
  568. package/test/state/selectors/selectAxisScale.spec.tsx +91 -0
  569. package/test/state/selectors/selectBarRectangles.stackOffset.spec.tsx +503 -0
  570. package/test/state/selectors/selectBaseAxis.spec.tsx +56 -0
  571. package/test/state/selectors/selectCartesianItemsSettings.spec.tsx +57 -0
  572. package/test/state/selectors/selectChartOffset.spec.tsx +26 -0
  573. package/test/state/selectors/selectDisplayedData.spec.tsx +282 -0
  574. package/test/state/selectors/selectIsTooltipActive.spec.tsx +231 -0
  575. package/test/state/selectors/selectNumericalDomain.spec.tsx +21 -0
  576. package/test/state/selectors/selectRealScaleType.spec.tsx +19 -0
  577. package/test/state/selectors/selectStackGroups.spec.tsx +322 -0
  578. package/test/state/selectors/selectXAxisPosition.spec.tsx +22 -0
  579. package/test/state/selectors/selectYAxisPosition.spec.tsx +22 -0
  580. package/test/state/selectors/selectors.spec.tsx +1613 -0
  581. package/test/state/throttling.spec.ts +200 -0
  582. package/test/state/zIndexSlice.spec.ts +85 -0
  583. package/test/synchronisation/useChartSynchronisation.spec.tsx +225 -0
  584. package/test/util/BarUtils.spec.ts +39 -0
  585. package/test/util/CartesianUtils/CartesianUtils.spec.ts +261 -0
  586. package/test/util/CartesianUtils/rectWithPoints.spec.ts +48 -0
  587. package/test/util/ChartUtils/ChartUtils.spec.ts +491 -0
  588. package/test/util/ChartUtils/appendOffsetOfLegend.spec.ts +136 -0
  589. package/test/util/ChartUtils/appendOffsetOfLegend.spec.tsx +133 -0
  590. package/test/util/ChartUtils/getCateCoordinateOfLine.spec.ts +83 -0
  591. package/test/util/ChartUtils/getStackedData.spec.ts +416 -0
  592. package/test/util/ChartUtils/truncateByDomain.spec.ts +70 -0
  593. package/test/util/DataUtils.spec.ts +385 -0
  594. package/test/util/DomUtils.spec.tsx +117 -0
  595. package/test/util/FunnelUtils.spec.tsx +70 -0
  596. package/test/util/Global.spec.ts +11 -0
  597. package/test/util/LRUCache.spec.ts +81 -0
  598. package/test/util/LogUtils.spec.ts +45 -0
  599. package/test/util/PolarUtils.spec.ts +66 -0
  600. package/test/util/ReduceCssCalcPrototype.spec.ts +141 -0
  601. package/test/util/ShallowEqual.spec.ts +102 -0
  602. package/test/util/TickUtils.spec.ts +29 -0
  603. package/test/util/axisPropsAreEqual.spec.ts +93 -0
  604. package/test/util/createChartHelpers.spec.tsx +210 -0
  605. package/test/util/cursor/getCursorPoints.spec.ts +136 -0
  606. package/test/util/cursor/getCursorRectangle.spec.ts +50 -0
  607. package/test/util/cursor/getRadialCursorPoints.spec.ts +36 -0
  608. package/test/util/errorValue.spec.ts +69 -0
  609. package/test/util/getEveryNth.spec.ts +39 -0
  610. package/test/util/getRadiusAndStrokeWidthFromDot.spec.ts +51 -0
  611. package/test/util/getRelativeCoordinate.spec.ts +250 -0
  612. package/test/util/getSliced.spec.ts +45 -0
  613. package/test/util/isDomainSpecifiedByUser.spec.ts +207 -0
  614. package/test/util/isWellBehavedNumber.spec.ts +54 -0
  615. package/test/util/payload/getUniqPayload.spec.ts +56 -0
  616. package/test/util/propsAreEqual.spec.ts +75 -0
  617. package/test/util/round.spec.ts +112 -0
  618. package/test/util/scale/arithmetic.spec.ts +61 -0
  619. package/test/util/scale/createCategoricalInverse.spec.ts +84 -0
  620. package/test/util/scale/getNiceTickValues.spec.ts +421 -0
  621. package/test/util/scale/getTickValuesFixedDomain.spec.ts +155 -0
  622. package/test/util/svgPropertiesAndEvents.spec.tsx +206 -0
  623. package/test/util/svgPropertiesNoEvents.spec.tsx +228 -0
  624. package/test/util/tooltip/translate.spec.ts +396 -0
  625. package/test/util/useAnimationId.spec.tsx +59 -0
  626. package/test/util/useElementOffset.spec.tsx +173 -0
  627. package/test/util/usePrefersReducedMotion.spec.ts +54 -0
  628. package/test/util/useUniqueId.spec.ts +99 -0
  629. package/test/utils/diagnostics-allowlist.ts +18 -0
  630. package/test/utils/diagnostics.ts +58 -0
  631. package/test/zIndex/AllZIndexPortals.spec.tsx +83 -0
  632. package/test/zIndex/DefaultZIndexes.spec.ts +80 -0
  633. package/test/zIndex/DynamicZIndex.spec.tsx +102 -0
  634. package/test/zIndex/ZIndexLayer.portalOwner.spec.tsx +125 -0
  635. package/test/zIndex/componentZIndex.spec.tsx +255 -0
  636. package/test/zIndex/getZIndexFromUnknown.spec.ts +51 -0
  637. package/test/zIndex/multiChartZIndexIsolation.spec.tsx +90 -0
  638. package/test/zIndex/zIndexSelectors.spec.ts +172 -0
  639. package/tsconfig.json +11 -0
@@ -0,0 +1,2213 @@
1
+ // Ported from recharts 3.8.1 test/component/Legend.spec.tsx.
2
+ //
3
+ // Tier-A DOM/behavioral assertions are byte-identical to recharts; only the
4
+ // harness is adapted: React render -> Solid `render(() => <.../>)`; rerenders are
5
+ // driven by a Solid signal (no JSX `rerender`); the reference `expectLegendLabels`
6
+ // / `assertHasLegend` helpers and the legendType symbol tables are inlined here
7
+ // (the repo has no shared `expectLegendLabels` helper); `rechartsTestRender` ->
8
+ // `render` + `flush`. Custom `content` as a React element, `aria-label` on the
9
+ // Surface, and the `mockSequenceOfGetBoundingClientRect` regression test are not
10
+ // supported by the port (see reclassifiedBlocked) and are omitted.
11
+ import { createEffect, createSignal, flush, Show } from "solid-js";
12
+ import { describe, expect, it, test, vi } from "vitest";
13
+ import { fireEvent, render, screen } from "../helper/render";
14
+ import {
15
+ Area,
16
+ AreaChart,
17
+ Bar,
18
+ BarChart,
19
+ ComposedChart,
20
+ DefaultZIndexes,
21
+ Legend,
22
+ Line,
23
+ LineChart,
24
+ Pie,
25
+ PieChart,
26
+ Radar,
27
+ RadarChart,
28
+ RadialBar,
29
+ RadialBarChart,
30
+ Scatter,
31
+ ScatterChart,
32
+ } from "../../src";
33
+ import type { LegendProps, LegendType } from "../../src";
34
+ import { selectLegendPayload } from "../../src/state/selectors/legendSelectors";
35
+ import { mockGetBoundingClientRect } from "../helper/mockGetBoundingClientRect";
36
+ import { assertNotNull } from "../helper/assertNotNull";
37
+ import { createSelectorTestCase } from "../helper/createSelectorTestCase";
38
+ import { expectLastCalledWith } from "../helper/expectLastCalledWith";
39
+ import { useAppSelectorWithStableTest } from "../helper/selectorTestHelpers";
40
+ import { dataWithSpecialNameAndFillProperties, numericalData } from "../_data";
41
+
42
+ type LegendTypeTestCases = ReadonlyArray<{
43
+ legendType: LegendType;
44
+ selector: string;
45
+ expectedAttributes: Record<string, string>;
46
+ }>;
47
+
48
+ // ---- inlined helpers (reference: test/helper/expectLegendLabels.tsx) ----
49
+ function assertHasLegend(container: Element): ReadonlyArray<Element> {
50
+ expect(container.querySelectorAll(".recharts-default-legend")).toHaveLength(1);
51
+ return Array.from(container.querySelectorAll(".recharts-default-legend .recharts-legend-item"));
52
+ }
53
+
54
+ function expectLegendLabels(
55
+ container: Element,
56
+ expectedLabels: null | ReadonlyArray<{
57
+ textContent: string;
58
+ textColor?: string;
59
+ fill: string | null | undefined;
60
+ stroke?: string;
61
+ }>,
62
+ ): void {
63
+ assertNotNull(container);
64
+
65
+ if (expectedLabels == null) {
66
+ expect(container.querySelectorAll(".recharts-default-legend")).toHaveLength(0);
67
+ return;
68
+ }
69
+
70
+ const expectsStroke = expectedLabels.some((label) => label.stroke != null);
71
+ const expectsColor = expectedLabels.some((label) => label.textColor !== undefined);
72
+
73
+ const actualLabels = assertHasLegend(container).map((legend) => {
74
+ const textNode = legend.querySelector(".recharts-legend-item-text");
75
+ return {
76
+ textContent: legend.textContent,
77
+ textColor: expectsColor && textNode instanceof HTMLElement ? textNode.style.color : undefined,
78
+ fill: legend.querySelector(".recharts-legend-icon")?.getAttribute("fill"),
79
+ stroke: expectsStroke
80
+ ? legend.querySelector(".recharts-legend-icon")?.getAttribute("stroke")
81
+ : undefined,
82
+ };
83
+ });
84
+
85
+ expect(actualLabels).toEqual(expectedLabels);
86
+ }
87
+
88
+ /**
89
+ * Bar legend for some reason has no default color at all.
90
+ * I think that's because Bar has no default fill nor stroke.
91
+ */
92
+ const expectedLegendTypeSymbolsWithoutColor: LegendTypeTestCases = [
93
+ {
94
+ legendType: "circle",
95
+ selector: "path.recharts-symbols",
96
+ expectedAttributes: {
97
+ d: "M16,0A16,16,0,1,1,-16,0A16,16,0,1,1,16,0",
98
+ class: "recharts-symbols",
99
+ cx: "16",
100
+ cy: "16",
101
+ transform: "translate(16, 16)",
102
+ },
103
+ },
104
+ {
105
+ legendType: "cross",
106
+ selector: "path.recharts-symbols",
107
+ expectedAttributes: {
108
+ d: "M-16,-5.333L-5.333,-5.333L-5.333,-16L5.333,-16L5.333,-5.333L16,-5.333L16,5.333L5.333,5.333L5.333,16L-5.333,16L-5.333,5.333L-16,5.333Z",
109
+ class: "recharts-symbols",
110
+ cx: "16",
111
+ cy: "16",
112
+ transform: "translate(16, 16)",
113
+ },
114
+ },
115
+ {
116
+ legendType: "diamond",
117
+ selector: "path.recharts-symbols",
118
+ expectedAttributes: {
119
+ d: "M0,-16L9.238,0L0,16L-9.238,0Z",
120
+ class: "recharts-symbols",
121
+ cx: "16",
122
+ cy: "16",
123
+ transform: "translate(16, 16)",
124
+ },
125
+ },
126
+ {
127
+ legendType: "line",
128
+ selector: "path.recharts-legend-icon",
129
+ expectedAttributes: {
130
+ // d attribute path is sensitive to whitespace!
131
+ d: `M0,16h10.666666666666666A5.333333333333333,5.333333333333333,0,1,1,21.333333333333332,16H32M21.333333333333332,16A5.333333333333333,5.333333333333333,0,1,1,10.666666666666666,16`,
132
+ fill: "none",
133
+ "stroke-width": "4",
134
+ class: "recharts-legend-icon",
135
+ },
136
+ },
137
+ {
138
+ legendType: "plainline",
139
+ selector: "line.recharts-legend-icon",
140
+ expectedAttributes: {
141
+ class: "recharts-legend-icon",
142
+ x1: "0",
143
+ x2: "32",
144
+ y1: "16",
145
+ y2: "16",
146
+ fill: "none",
147
+ "stroke-width": "4",
148
+ },
149
+ },
150
+ {
151
+ legendType: "rect",
152
+ selector: "path.recharts-legend-icon",
153
+ expectedAttributes: {
154
+ d: "M0,4h32v24h-32z",
155
+ class: "recharts-legend-icon",
156
+ stroke: "none",
157
+ },
158
+ },
159
+ {
160
+ legendType: "square",
161
+ selector: "path.recharts-symbols",
162
+ expectedAttributes: {
163
+ d: "M-16,-16h32v32h-32Z",
164
+ class: "recharts-symbols",
165
+ cx: "16",
166
+ cy: "16",
167
+ transform: "translate(16, 16)",
168
+ },
169
+ },
170
+ {
171
+ legendType: "star",
172
+ selector: "path.recharts-symbols",
173
+ expectedAttributes: {
174
+ d: "M0,-16.823L3.777,-5.199L16,-5.199L6.111,1.986L9.889,13.61L0,6.426L-9.889,13.61L-6.111,1.986L-16,-5.199L-3.777,-5.199Z",
175
+ class: "recharts-symbols",
176
+ cx: "16",
177
+ cy: "16",
178
+ transform: "translate(16, 16)",
179
+ },
180
+ },
181
+ {
182
+ legendType: "triangle",
183
+ selector: "path.recharts-symbols",
184
+ expectedAttributes: {
185
+ d: "M0,-18.475L16,9.238L-16,9.238Z",
186
+ class: "recharts-symbols",
187
+ cx: "16",
188
+ cy: "16",
189
+ transform: "translate(16, 16)",
190
+ },
191
+ },
192
+ {
193
+ legendType: "wye",
194
+ selector: "path.recharts-symbols",
195
+ expectedAttributes: {
196
+ d: "M5.856,3.381L5.856,15.094L-5.856,15.094L-5.856,3.381L-16,-2.475L-10.144,-12.619L0,-6.762L10.144,-12.619L16,-2.475Z",
197
+ class: "recharts-symbols",
198
+ cx: "16",
199
+ cy: "16",
200
+ transform: "translate(16, 16)",
201
+ },
202
+ },
203
+ ];
204
+
205
+ const expectedLegendTypeSymbolsWithColor = (color: string): LegendTypeTestCases => [
206
+ {
207
+ legendType: "circle",
208
+ selector: "path.recharts-symbols",
209
+ expectedAttributes: {
210
+ d: "M16,0A16,16,0,1,1,-16,0A16,16,0,1,1,16,0",
211
+ class: "recharts-symbols",
212
+ cx: "16",
213
+ cy: "16",
214
+ transform: "translate(16, 16)",
215
+ fill: color,
216
+ },
217
+ },
218
+ {
219
+ legendType: "cross",
220
+ selector: "path.recharts-symbols",
221
+ expectedAttributes: {
222
+ d: "M-16,-5.333L-5.333,-5.333L-5.333,-16L5.333,-16L5.333,-5.333L16,-5.333L16,5.333L5.333,5.333L5.333,16L-5.333,16L-5.333,5.333L-16,5.333Z",
223
+ class: "recharts-symbols",
224
+ cx: "16",
225
+ cy: "16",
226
+ transform: "translate(16, 16)",
227
+ fill: color,
228
+ },
229
+ },
230
+ {
231
+ legendType: "diamond",
232
+ selector: "path.recharts-symbols",
233
+ expectedAttributes: {
234
+ d: "M0,-16L9.238,0L0,16L-9.238,0Z",
235
+ class: "recharts-symbols",
236
+ cx: "16",
237
+ cy: "16",
238
+ transform: "translate(16, 16)",
239
+ fill: color,
240
+ },
241
+ },
242
+ {
243
+ legendType: "line",
244
+ selector: "path.recharts-legend-icon",
245
+ expectedAttributes: {
246
+ // d attribute path is sensitive to whitespace!
247
+ d: `M0,16h10.666666666666666A5.333333333333333,5.333333333333333,0,1,1,21.333333333333332,16H32M21.333333333333332,16A5.333333333333333,5.333333333333333,0,1,1,10.666666666666666,16`,
248
+ fill: "none",
249
+ "stroke-width": "4",
250
+ class: "recharts-legend-icon",
251
+ stroke: color,
252
+ },
253
+ },
254
+ {
255
+ legendType: "plainline",
256
+ selector: "line.recharts-legend-icon",
257
+ expectedAttributes: {
258
+ class: "recharts-legend-icon",
259
+ x1: "0",
260
+ x2: "32",
261
+ y1: "16",
262
+ y2: "16",
263
+ fill: "none",
264
+ stroke: color,
265
+ "stroke-width": "4",
266
+ },
267
+ },
268
+ {
269
+ legendType: "rect",
270
+ selector: "path.recharts-legend-icon",
271
+ expectedAttributes: {
272
+ d: "M0,4h32v24h-32z",
273
+ class: "recharts-legend-icon",
274
+ stroke: "none",
275
+ fill: color,
276
+ },
277
+ },
278
+ {
279
+ legendType: "square",
280
+ selector: "path.recharts-symbols",
281
+ expectedAttributes: {
282
+ d: "M-16,-16h32v32h-32Z",
283
+ class: "recharts-symbols",
284
+ cx: "16",
285
+ cy: "16",
286
+ transform: "translate(16, 16)",
287
+ fill: color,
288
+ },
289
+ },
290
+ {
291
+ legendType: "star",
292
+ selector: "path.recharts-symbols",
293
+ expectedAttributes: {
294
+ d: "M0,-16.823L3.777,-5.199L16,-5.199L6.111,1.986L9.889,13.61L0,6.426L-9.889,13.61L-6.111,1.986L-16,-5.199L-3.777,-5.199Z",
295
+ class: "recharts-symbols",
296
+ cx: "16",
297
+ cy: "16",
298
+ transform: "translate(16, 16)",
299
+ fill: color,
300
+ },
301
+ },
302
+ {
303
+ legendType: "triangle",
304
+ selector: "path.recharts-symbols",
305
+ expectedAttributes: {
306
+ d: "M0,-18.475L16,9.238L-16,9.238Z",
307
+ class: "recharts-symbols",
308
+ cx: "16",
309
+ cy: "16",
310
+ transform: "translate(16, 16)",
311
+ fill: color,
312
+ },
313
+ },
314
+ {
315
+ legendType: "wye",
316
+ selector: "path.recharts-symbols",
317
+ expectedAttributes: {
318
+ d: "M5.856,3.381L5.856,15.094L-5.856,15.094L-5.856,3.381L-16,-2.475L-10.144,-12.619L0,-6.762L10.144,-12.619L16,-2.475Z",
319
+ class: "recharts-symbols",
320
+ cx: "16",
321
+ cy: "16",
322
+ transform: "translate(16, 16)",
323
+ fill: color,
324
+ },
325
+ },
326
+ ];
327
+
328
+ function assertExpectedAttributes(
329
+ container: HTMLElement,
330
+ selector: string,
331
+ expectedAttributes: Record<string, string>,
332
+ ): void {
333
+ const legendItem = assertHasLegend(container)[0];
334
+ assertNotNull(legendItem);
335
+ const symbol = legendItem.querySelector(selector);
336
+ expect(symbol).not.toBeNull();
337
+ expect(symbol).toBeInTheDocument();
338
+ const expectedAttributeNames = Object.keys(expectedAttributes);
339
+ expect.soft(symbol?.getAttributeNames().sort()).toEqual(expectedAttributeNames.sort());
340
+ expectedAttributeNames.forEach((attributeName) => {
341
+ expect.soft(symbol).toHaveAttribute(attributeName, expectedAttributes[attributeName]);
342
+ });
343
+ }
344
+
345
+ describe("<Legend />", () => {
346
+ const categoricalData = [
347
+ { value: "Apple", color: "#ff7300" },
348
+ { value: "Samsung", color: "#bb7300" },
349
+ { value: "Huawei", color: "#887300" },
350
+ { value: "Sony", color: "#667300" },
351
+ ];
352
+
353
+ const numericalData2 = [
354
+ { title: "Luftbaloons", value: 99 },
355
+ { title: "Miles I would walk", value: 500 },
356
+ { title: "Days a week", value: 8 },
357
+ { title: "Mambo number", value: 5 },
358
+ { title: "Seas of Rhye", value: 7 },
359
+ ];
360
+
361
+ describe("outside of chart context", () => {
362
+ it("should ignore payload prop", () => {
363
+ const { container } = render(() => <Legend width={500} height={30} />);
364
+ flush();
365
+
366
+ expect(container.querySelectorAll(".recharts-default-legend")).toHaveLength(0);
367
+ expect(
368
+ container.querySelectorAll(".recharts-default-legend .recharts-legend-item"),
369
+ ).toHaveLength(0);
370
+ });
371
+ });
372
+
373
+ describe("custom content as a react element", () => {
374
+ it("should render a custom component wrapped legend", () => {
375
+ const CustomLegend = (props: LegendProps) => <Legend {...props} />;
376
+ const { container } = render(() => (
377
+ <LineChart
378
+ width={600}
379
+ height={300}
380
+ data={categoricalData}
381
+ margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
382
+ >
383
+ <CustomLegend />
384
+ <Line
385
+ type="monotone"
386
+ dataKey="pv"
387
+ stroke="#8884d8"
388
+ activeDot={{ r: 8 }}
389
+ strokeDasharray="5 5"
390
+ />
391
+ <Line type="monotone" dataKey="uv" stroke="#82ca9d" />
392
+ </LineChart>
393
+ ));
394
+ flush();
395
+
396
+ expect(container.querySelectorAll(".recharts-default-legend")).toHaveLength(1);
397
+ });
398
+ });
399
+
400
+ describe("content as a function", () => {
401
+ it("should render result", () => {
402
+ const customizedLegend = () => "custom return value";
403
+ const { container } = render(() => (
404
+ <LineChart
405
+ width={600}
406
+ height={300}
407
+ data={categoricalData}
408
+ margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
409
+ >
410
+ <Legend content={customizedLegend} />
411
+ <Line dataKey="pv" stroke="#8884d8" activeDot={{ r: 8 }} strokeDasharray="5 5" />
412
+ <Line type="monotone" dataKey="uv" stroke="#82ca9d" />
413
+ </LineChart>
414
+ ));
415
+ flush();
416
+
417
+ expect(container.querySelectorAll(".recharts-default-legend")).toHaveLength(0);
418
+ expect(screen.getByText("custom return value")).toBeInTheDocument();
419
+ expect(screen.getByText("custom return value")).toBeVisible();
420
+ });
421
+ });
422
+
423
+ // In recharts the "React Component" content form is distinct from the function
424
+ // form (one is a component, the other a plain callback); both reduce to the same
425
+ // function-content path in Solid since there is no React.createElement boundary.
426
+ describe("content as a Component", () => {
427
+ it("should render result", () => {
428
+ const CustomizedLegend = () => <>custom return value</>;
429
+ const { container } = render(() => (
430
+ <LineChart
431
+ width={600}
432
+ height={300}
433
+ data={categoricalData}
434
+ margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
435
+ >
436
+ <Legend content={CustomizedLegend} />
437
+ <Line dataKey="pv" stroke="#8884d8" activeDot={{ r: 8 }} strokeDasharray="5 5" />
438
+ <Line type="monotone" dataKey="uv" stroke="#82ca9d" />
439
+ </LineChart>
440
+ ));
441
+ flush();
442
+
443
+ expect(container.querySelectorAll(".recharts-default-legend")).toHaveLength(0);
444
+ expect(screen.getByText("custom return value")).toBeInTheDocument();
445
+ expect(screen.getByText("custom return value")).toBeVisible();
446
+ });
447
+ });
448
+
449
+ describe("as a child of LineChart", () => {
450
+ test("Renders `strokeDasharray` (if present) in Legend when iconType is set to `plainline`", () => {
451
+ const { container } = render(() => (
452
+ <LineChart
453
+ width={600}
454
+ height={300}
455
+ data={categoricalData}
456
+ margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
457
+ >
458
+ <Legend iconType="plainline" />
459
+ <Line
460
+ type="monotone"
461
+ dataKey="pv"
462
+ stroke="#8884d8"
463
+ activeDot={{ r: 8 }}
464
+ strokeDasharray="5 5"
465
+ />
466
+ <Line type="monotone" dataKey="uv" stroke="#82ca9d" />
467
+ </LineChart>
468
+ ));
469
+ flush();
470
+
471
+ expect(container.querySelectorAll(".recharts-default-legend")).toHaveLength(1);
472
+ expect(
473
+ container.querySelectorAll(".recharts-default-legend .recharts-legend-item"),
474
+ ).toHaveLength(2);
475
+ expect(
476
+ container.querySelectorAll(".recharts-default-legend .recharts-legend-item path"),
477
+ ).toHaveLength(0);
478
+ expect(
479
+ container.querySelectorAll(".recharts-default-legend .recharts-legend-item line"),
480
+ ).toHaveLength(2);
481
+ });
482
+
483
+ test("Does not render `strokeDasharray` (if not present) when iconType is not set to `plainline`", () => {
484
+ const { container } = render(() => (
485
+ <LineChart width={600} height={300} data={categoricalData}>
486
+ <Legend iconType="line" />
487
+ <Line dataKey="pv" />
488
+ <Line type="monotone" dataKey="uv" stroke="#82ca9d" />
489
+ </LineChart>
490
+ ));
491
+ flush();
492
+
493
+ expect(container.querySelectorAll(".recharts-default-legend")).toHaveLength(1);
494
+ expect(
495
+ container.querySelectorAll(".recharts-default-legend .recharts-legend-item"),
496
+ ).toHaveLength(2);
497
+ expect(
498
+ container.querySelectorAll(".recharts-default-legend .recharts-legend-item path"),
499
+ ).toHaveLength(2);
500
+ expect(
501
+ container.querySelectorAll(".recharts-default-legend .recharts-legend-item line"),
502
+ ).toHaveLength(0);
503
+ });
504
+
505
+ test("Renders name value of siblings when dataKey is a function", () => {
506
+ const { container } = render(() => (
507
+ <LineChart width={500} height={500} data={categoricalData}>
508
+ <Legend />
509
+ <Line
510
+ dataKey={(row: (typeof categoricalData)[number]) => row.value}
511
+ name="My Line Data"
512
+ />
513
+ <Line
514
+ dataKey={(row: (typeof categoricalData)[number]) => row.color}
515
+ name="My Other Line Data"
516
+ />
517
+ </LineChart>
518
+ ));
519
+ flush();
520
+
521
+ expectLegendLabels(container, [
522
+ { textContent: "My Line Data", fill: "none" },
523
+ { textContent: "My Other Line Data", fill: "none" },
524
+ ]);
525
+ });
526
+
527
+ test("Legend defaults are read correctly", () => {
528
+ const { container } = render(() => (
529
+ <LineChart width={500} height={500} data={categoricalData}>
530
+ <Legend />
531
+ <Line
532
+ dataKey={(row: (typeof categoricalData)[number]) => row.value}
533
+ name="My Line Data"
534
+ />
535
+ <Line
536
+ dataKey={(row: (typeof categoricalData)[number]) => row.color}
537
+ name="My Other Line Data"
538
+ />
539
+ </LineChart>
540
+ ));
541
+ flush();
542
+
543
+ const legendWrapper = container.getElementsByClassName("recharts-legend-wrapper")[0];
544
+ assertNotNull(legendWrapper);
545
+ expect(legendWrapper).not.toHaveStyle({ width: "auto" });
546
+ expect(legendWrapper).toHaveStyle({ height: "auto" });
547
+ const legendItem = container.getElementsByClassName("legend-item-0")[0];
548
+ assertNotNull(legendItem);
549
+ const surface = legendItem.getElementsByClassName("recharts-surface")[0];
550
+ assertNotNull(surface);
551
+ expect(surface.getAttribute("height")).toBe("14");
552
+ expect(surface.getAttribute("width")).toBe("14");
553
+ expect(surface.getAttribute("aria-label")).toBe("My Line Data legend icon");
554
+ });
555
+
556
+ test("aria-label uses the raw entry value even when formatter returns an element", () => {
557
+ const { container } = render(() => (
558
+ <LineChart width={500} height={500} data={numericalData}>
559
+ <Legend formatter={(value) => <strong>{value}</strong>} />
560
+ <Line dataKey="value" name="UV" />
561
+ </LineChart>
562
+ ));
563
+ flush();
564
+
565
+ const legendItem = container.getElementsByClassName("legend-item-0")[0];
566
+ assertNotNull(legendItem);
567
+ const surface = legendItem.getElementsByClassName("recharts-surface")[0];
568
+ assertNotNull(surface);
569
+ expect(surface.getAttribute("aria-label")).toBe("UV legend icon");
570
+ });
571
+
572
+ test("aria-label drops the value when the legend entry has no name or string dataKey", () => {
573
+ const { container } = render(() => (
574
+ <LineChart width={500} height={500} data={numericalData}>
575
+ <Legend />
576
+ <Line dataKey={(row: { value: number }) => row.value} />
577
+ </LineChart>
578
+ ));
579
+ flush();
580
+
581
+ const legendItem = container.getElementsByClassName("legend-item-0")[0];
582
+ assertNotNull(legendItem);
583
+ const surface = legendItem.getElementsByClassName("recharts-surface")[0];
584
+ assertNotNull(surface);
585
+ expect(surface.getAttribute("aria-label")).toBe("legend icon");
586
+ });
587
+
588
+ it("should pass parameters to the Component", () => {
589
+ mockGetBoundingClientRect({ width: 80, height: 30 });
590
+ const spy = vi.fn();
591
+ const CustomContent = (contentProps: unknown): null => {
592
+ spy(contentProps);
593
+ return null;
594
+ };
595
+ render(() => (
596
+ <LineChart
597
+ width={600}
598
+ height={300}
599
+ data={categoricalData}
600
+ margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
601
+ >
602
+ <Legend content={CustomContent} />
603
+ <Line
604
+ type="monotone"
605
+ dataKey="pv"
606
+ stroke="#8884d8"
607
+ activeDot={{ r: 8 }}
608
+ strokeDasharray="5 5"
609
+ />
610
+ <Line type="monotone" dataKey="uv" stroke="#82ca9d" />
611
+ </LineChart>
612
+ ));
613
+ flush();
614
+ expect(spy).toHaveBeenLastCalledWith(
615
+ expect.objectContaining({
616
+ align: "center",
617
+ chartHeight: 300,
618
+ chartWidth: 600,
619
+ content: expect.any(Function),
620
+ iconSize: 14,
621
+ inactiveColor: "#ccc",
622
+ itemSorter: "value",
623
+ labelStyle: {},
624
+ layout: "horizontal",
625
+ margin: { bottom: 5, left: 20, right: 30, top: 5 },
626
+ verticalAlign: "bottom",
627
+ width: 550,
628
+ payload: [
629
+ expect.objectContaining({
630
+ color: "#8884d8",
631
+ dataKey: "pv",
632
+ inactive: false,
633
+ value: "pv",
634
+ type: "line",
635
+ }),
636
+ expect.objectContaining({
637
+ color: "#82ca9d",
638
+ dataKey: "uv",
639
+ inactive: false,
640
+ value: "uv",
641
+ type: "line",
642
+ }),
643
+ ],
644
+ }),
645
+ );
646
+ });
647
+
648
+ it("should render one line legend item for each Line, with default class and style attributes", () => {
649
+ const { container } = render(() => (
650
+ <LineChart width={500} height={500} data={numericalData}>
651
+ <Legend />
652
+ <Line dataKey="percent" />
653
+ <Line dataKey="value" />
654
+ </LineChart>
655
+ ));
656
+ flush();
657
+ expect(screen.getByText("value")).toBeInTheDocument();
658
+ expect(screen.getByText("percent")).toBeInTheDocument();
659
+
660
+ const legendItems = assertHasLegend(container);
661
+ expect(legendItems).toHaveLength(2);
662
+
663
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
664
+ expect.soft(legendItems[0]!.getAttribute("class")).toBe("recharts-legend-item legend-item-0");
665
+ expect
666
+ .soft(legendItems[0]!.getAttribute("style"))
667
+ .toBe("display: inline-block; margin-right: 10px;");
668
+ expect.soft(legendItems[1]!.getAttributeNames()).toEqual(["class", "style"]);
669
+ expect.soft(legendItems[1]!.getAttribute("class")).toBe("recharts-legend-item legend-item-1");
670
+ expect
671
+ .soft(legendItems[1]!.getAttribute("style"))
672
+ .toBe("display: inline-block; margin-right: 10px;");
673
+
674
+ // in absence of explicit `legendType`, Line should default to line
675
+ const find = expectedLegendTypeSymbolsWithColor("#3182bd").find(
676
+ (tc) => tc.legendType === "line",
677
+ );
678
+ assertNotNull(find);
679
+ const { selector, expectedAttributes } = find;
680
+ assertExpectedAttributes(container, selector, expectedAttributes);
681
+ });
682
+
683
+ it("should render a legend item even if the dataKey does not match anything from the data", () => {
684
+ const { container } = render(() => (
685
+ <LineChart width={500} height={500} data={numericalData}>
686
+ <Legend />
687
+ <Line dataKey="unknown" />
688
+ </LineChart>
689
+ ));
690
+ flush();
691
+ expect(screen.getByText("unknown")).toBeInTheDocument();
692
+ const legendItems = assertHasLegend(container);
693
+ expect(legendItems).toHaveLength(1);
694
+ expect(legendItems[0]!.textContent).toBe("unknown");
695
+ });
696
+
697
+ it("should change color and className of hidden Line", () => {
698
+ const { container } = render(() => (
699
+ <LineChart width={500} height={500} data={numericalData}>
700
+ <Legend inactiveColor="yellow" />
701
+ {/* this will ignore the stroke and use inactive color on legend */}
702
+ <Line dataKey="percent" stroke="red" hide />
703
+ </LineChart>
704
+ ));
705
+ flush();
706
+ expect(screen.getByText("percent")).toBeInTheDocument();
707
+ const legendItems = assertHasLegend(container);
708
+
709
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
710
+ expect
711
+ .soft(legendItems[0]!.getAttribute("class"))
712
+ .toBe("recharts-legend-item legend-item-0 inactive");
713
+ expect
714
+ .soft(legendItems[0]!.getAttribute("style"))
715
+ .toBe("display: inline-block; margin-right: 10px;");
716
+
717
+ // in absence of explicit `legendType`, Line should default to line
718
+ const findResult = expectedLegendTypeSymbolsWithColor("yellow").find(
719
+ (tc) => tc.legendType === "line",
720
+ );
721
+ assertNotNull(findResult);
722
+ const { selector, expectedAttributes } = findResult;
723
+ assertExpectedAttributes(container, selector, expectedAttributes);
724
+ });
725
+
726
+ it("should have a default inactive Line legend color", () => {
727
+ const { container } = render(() => (
728
+ <LineChart width={500} height={500} data={numericalData}>
729
+ <Legend />
730
+ {/* this will ignore the stroke and use inactive color on legend */}
731
+ <Line dataKey="percent" stroke="red" hide />
732
+ </LineChart>
733
+ ));
734
+ flush();
735
+ expect(screen.getByText("percent")).toBeInTheDocument();
736
+ const legendItems = assertHasLegend(container);
737
+
738
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
739
+ expect
740
+ .soft(legendItems[0]!.getAttribute("class"))
741
+ .toBe("recharts-legend-item legend-item-0 inactive");
742
+ expect
743
+ .soft(legendItems[0]!.getAttribute("style"))
744
+ .toBe("display: inline-block; margin-right: 10px;");
745
+
746
+ const findResult = expectedLegendTypeSymbolsWithColor("#ccc").find(
747
+ (tc) => tc.legendType === "line",
748
+ );
749
+ assertNotNull(findResult);
750
+ const { selector, expectedAttributes } = findResult;
751
+ assertExpectedAttributes(container, selector, expectedAttributes);
752
+ });
753
+
754
+ it("should render one empty legend item if Line has no dataKey", () => {
755
+ const { container } = render(() => (
756
+ <LineChart width={500} height={500} data={numericalData}>
757
+ <Legend />
758
+ <Line />
759
+ </LineChart>
760
+ ));
761
+ flush();
762
+ expectLegendLabels(container, [{ fill: "none", textContent: "" }]);
763
+ });
764
+
765
+ it("should set legend item from `name` prop on Line, and update it after rerender", () => {
766
+ const [name, setName] = createSignal("%");
767
+ const { container } = render(() => (
768
+ <LineChart width={500} height={500} data={numericalData}>
769
+ <Legend />
770
+ <Line dataKey="percent" name={name()} />
771
+ </LineChart>
772
+ ));
773
+ flush();
774
+ expectLegendLabels(container, [{ fill: "none", textContent: "%" }]);
775
+
776
+ setName("Percent");
777
+ flush();
778
+ expectLegendLabels(container, [{ fill: "none", textContent: "Percent" }]);
779
+ });
780
+
781
+ it("should not implicitly read `name` and `fill` properties from the data array", () => {
782
+ const { container } = render(() => (
783
+ <LineChart width={500} height={500} data={dataWithSpecialNameAndFillProperties}>
784
+ <Legend />
785
+ <Line dataKey="value" />
786
+ </LineChart>
787
+ ));
788
+ flush();
789
+
790
+ expectLegendLabels(container, [{ fill: "none", textContent: "value" }]);
791
+ });
792
+
793
+ it("should disappear after Line element is removed", () => {
794
+ const [showFirst, setShowFirst] = createSignal(true);
795
+ const { container } = render(() => (
796
+ <LineChart width={500} height={500} data={dataWithSpecialNameAndFillProperties}>
797
+ <Legend />
798
+ {showFirst() ? <Line dataKey="name" /> : null}
799
+ <Line dataKey="value" />
800
+ </LineChart>
801
+ ));
802
+ flush();
803
+ expectLegendLabels(container, [
804
+ { fill: "none", textContent: "name" },
805
+ { fill: "none", textContent: "value" },
806
+ ]);
807
+
808
+ setShowFirst(false);
809
+ flush();
810
+ expectLegendLabels(container, [{ fill: "none", textContent: "value" }]);
811
+ });
812
+
813
+ it("should update legend if Line data changes", () => {
814
+ const [dataKey, setDataKey] = createSignal("value");
815
+ const { container } = render(() => (
816
+ <LineChart width={500} height={500} data={numericalData}>
817
+ <Legend />
818
+ <Line dataKey={dataKey()} />
819
+ </LineChart>
820
+ ));
821
+ flush();
822
+ expectLegendLabels(container, [{ fill: "none", textContent: "value" }]);
823
+
824
+ setDataKey("percent");
825
+ flush();
826
+ expectLegendLabels(container, [{ fill: "none", textContent: "percent" }]);
827
+ });
828
+
829
+ it("should render legend labels", () => {
830
+ const { container } = render(() => (
831
+ <LineChart
832
+ width={600}
833
+ height={300}
834
+ data={categoricalData}
835
+ margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
836
+ >
837
+ <Legend iconType="plainline" />
838
+ <Line
839
+ type="monotone"
840
+ dataKey="pv"
841
+ stroke="#8884d8"
842
+ activeDot={{ r: 8 }}
843
+ strokeDasharray="5 5"
844
+ />
845
+ <Line type="monotone" dataKey="uv" stroke="#82ca9d" />
846
+ </LineChart>
847
+ ));
848
+ flush();
849
+
850
+ expectLegendLabels(container, [
851
+ { fill: "none", textContent: "pv", textColor: "rgb(136, 132, 216)" },
852
+ { fill: "none", textContent: "uv", textColor: "rgb(130, 202, 157)" },
853
+ ]);
854
+ });
855
+
856
+ it("should render legend labels with same text color", () => {
857
+ const { container } = render(() => (
858
+ <LineChart
859
+ width={600}
860
+ height={300}
861
+ data={categoricalData}
862
+ margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
863
+ >
864
+ <Legend iconType="plainline" labelStyle={{ color: "#666" }} />
865
+ <Line
866
+ type="monotone"
867
+ dataKey="pv"
868
+ stroke="#8884d8"
869
+ activeDot={{ r: 8 }}
870
+ strokeDasharray="5 5"
871
+ />
872
+ <Line type="monotone" dataKey="uv" stroke="#82ca9d" />
873
+ </LineChart>
874
+ ));
875
+ flush();
876
+
877
+ expectLegendLabels(container, [
878
+ { fill: "none", textContent: "pv", textColor: "rgb(102, 102, 102)" },
879
+ { fill: "none", textContent: "uv", textColor: "rgb(102, 102, 102)" },
880
+ ]);
881
+ });
882
+
883
+ test("label style should not change color of hidden Line", () => {
884
+ const { container } = render(() => (
885
+ <LineChart width={500} height={500} data={numericalData}>
886
+ <Legend inactiveColor="yellow" labelStyle={{ color: "#666" }} />
887
+ <Line dataKey="percent" stroke="red" hide />
888
+ </LineChart>
889
+ ));
890
+ flush();
891
+ const findResult = expectedLegendTypeSymbolsWithColor("yellow").find(
892
+ (tc) => tc.legendType === "line",
893
+ );
894
+ assertNotNull(findResult);
895
+ const { selector, expectedAttributes } = findResult;
896
+ assertExpectedAttributes(container, selector, expectedAttributes);
897
+ expectLegendLabels(container, [
898
+ { fill: "none", textContent: "percent", textColor: "yellow" },
899
+ ]);
900
+ });
901
+
902
+ describe("legendType symbols", () => {
903
+ test.each(expectedLegendTypeSymbolsWithColor("#3182bd"))(
904
+ "should render element $selector for legendType $legendType",
905
+ ({ legendType, selector, expectedAttributes }) => {
906
+ const { container } = render(() => (
907
+ <LineChart width={500} height={500} data={categoricalData}>
908
+ <Legend />
909
+ <Line dataKey="value" legendType={legendType} />
910
+ </LineChart>
911
+ ));
912
+ flush();
913
+ assertExpectedAttributes(container, selector, expectedAttributes);
914
+ },
915
+ );
916
+ });
917
+
918
+ it("should prefer Legend.iconType over Line.legendType", () => {
919
+ const { container } = render(() => (
920
+ <LineChart width={500} height={500} data={numericalData}>
921
+ <Legend iconType="circle" />
922
+ <Line dataKey="value" legendType="square" />
923
+ </LineChart>
924
+ ));
925
+ flush();
926
+ const findResult = expectedLegendTypeSymbolsWithColor("#3182bd").find(
927
+ (li) => li.legendType === "circle",
928
+ );
929
+ assertNotNull(findResult);
930
+ const { selector, expectedAttributes } = findResult;
931
+ assertExpectedAttributes(container, selector, expectedAttributes);
932
+ });
933
+ });
934
+
935
+ describe("as a child of LineChart when data is passed to Line child instead of the root", () => {
936
+ it("should render labels", () => {
937
+ const { container } = render(() => (
938
+ <LineChart width={600} height={300} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
939
+ <Legend iconType="plainline" />
940
+ <Line
941
+ type="monotone"
942
+ data={categoricalData}
943
+ dataKey="pv"
944
+ stroke="#8884d8"
945
+ activeDot={{ r: 8 }}
946
+ strokeDasharray="5 5"
947
+ />
948
+ <Line data={categoricalData} type="monotone" dataKey="uv" stroke="#82ca9d" />
949
+ </LineChart>
950
+ ));
951
+ flush();
952
+
953
+ expectLegendLabels(container, [
954
+ { fill: "none", textContent: "pv" },
955
+ { fill: "none", textContent: "uv" },
956
+ ]);
957
+ });
958
+ });
959
+
960
+ describe("as a child of BarChart", () => {
961
+ it("should render one rect legend item for each Bar, with default class and style attributes", () => {
962
+ const { container } = render(() => (
963
+ <BarChart width={500} height={500} data={numericalData}>
964
+ <Legend />
965
+ <Bar dataKey="percent" />
966
+ <Bar dataKey="value" />
967
+ </BarChart>
968
+ ));
969
+ flush();
970
+ expectLegendLabels(container, [
971
+ { fill: null, textContent: "percent" },
972
+ { fill: null, textContent: "value" },
973
+ ]);
974
+
975
+ const legendItems = assertHasLegend(container);
976
+
977
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
978
+ expect.soft(legendItems[0]!.getAttribute("class")).toBe("recharts-legend-item legend-item-0");
979
+ expect
980
+ .soft(legendItems[0]!.getAttribute("style"))
981
+ .toBe("display: inline-block; margin-right: 10px;");
982
+ expect.soft(legendItems[1]!.getAttributeNames()).toEqual(["class", "style"]);
983
+ expect.soft(legendItems[1]!.getAttribute("class")).toBe("recharts-legend-item legend-item-1");
984
+ expect
985
+ .soft(legendItems[1]!.getAttribute("style"))
986
+ .toBe("display: inline-block; margin-right: 10px;");
987
+
988
+ // in absence of explicit `legendType`, Bar should default to rect
989
+ const findResult = expectedLegendTypeSymbolsWithoutColor.find(
990
+ (tc) => tc.legendType === "rect",
991
+ );
992
+ assertNotNull(findResult);
993
+ const { selector, expectedAttributes } = findResult;
994
+ assertExpectedAttributes(container, selector, expectedAttributes);
995
+ });
996
+
997
+ it("should not render items with a type of `none`", () => {
998
+ const { container } = render(() => (
999
+ <BarChart width={500} height={500} data={categoricalData}>
1000
+ <Legend />
1001
+ <Bar dataKey="value" legendType="star" />
1002
+ <Bar dataKey="color" legendType="none" />
1003
+ </BarChart>
1004
+ ));
1005
+ flush();
1006
+
1007
+ expectLegendLabels(container, [{ fill: undefined, textContent: "value" }]);
1008
+ });
1009
+
1010
+ it("should render a legend item even if the dataKey does not match anything from the data", () => {
1011
+ const { container } = render(() => (
1012
+ <BarChart width={500} height={500} data={numericalData}>
1013
+ <Legend />
1014
+ <Bar dataKey="unknown" />
1015
+ </BarChart>
1016
+ ));
1017
+ flush();
1018
+ expectLegendLabels(container, [{ fill: null, textContent: "unknown" }]);
1019
+ });
1020
+
1021
+ it("should change color and className of hidden Bar", () => {
1022
+ const { container } = render(() => (
1023
+ <BarChart width={500} height={500} data={numericalData}>
1024
+ <Legend inactiveColor="yellow" />
1025
+ {/* this will ignore the stroke and use inactive color on legend */}
1026
+ <Bar dataKey="percent" stroke="red" hide />
1027
+ </BarChart>
1028
+ ));
1029
+ flush();
1030
+ expectLegendLabels(container, [{ fill: "yellow", textContent: "percent" }]);
1031
+ const legendItems = assertHasLegend(container);
1032
+
1033
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
1034
+ expect
1035
+ .soft(legendItems[0]!.getAttribute("class"))
1036
+ .toBe("recharts-legend-item legend-item-0 inactive");
1037
+ expect
1038
+ .soft(legendItems[0]!.getAttribute("style"))
1039
+ .toBe("display: inline-block; margin-right: 10px;");
1040
+
1041
+ // in absence of explicit `legendType`, Bar should default to rect
1042
+ const findResult = expectedLegendTypeSymbolsWithColor("yellow").find(
1043
+ (tc) => tc.legendType === "rect",
1044
+ );
1045
+ assertNotNull(findResult);
1046
+ const { selector, expectedAttributes } = findResult;
1047
+ assertExpectedAttributes(container, selector, expectedAttributes);
1048
+ });
1049
+
1050
+ it("should have a default inactive Bar legend color", () => {
1051
+ const { container } = render(() => (
1052
+ <BarChart width={500} height={500} data={numericalData}>
1053
+ <Legend />
1054
+ {/* this will ignore the stroke and use inactive color on legend */}
1055
+ <Bar dataKey="percent" stroke="red" hide />
1056
+ </BarChart>
1057
+ ));
1058
+ flush();
1059
+ expectLegendLabels(container, [{ fill: "#ccc", textContent: "percent" }]);
1060
+ const legendItems = assertHasLegend(container);
1061
+
1062
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
1063
+ expect
1064
+ .soft(legendItems[0]!.getAttribute("class"))
1065
+ .toBe("recharts-legend-item legend-item-0 inactive");
1066
+ expect
1067
+ .soft(legendItems[0]!.getAttribute("style"))
1068
+ .toBe("display: inline-block; margin-right: 10px;");
1069
+
1070
+ // in absence of explicit `legendType`, Bar should default to rect
1071
+ const findResult = expectedLegendTypeSymbolsWithColor("#ccc").find(
1072
+ (tc) => tc.legendType === "rect",
1073
+ );
1074
+ assertNotNull(findResult);
1075
+ const { selector, expectedAttributes } = findResult;
1076
+ assertExpectedAttributes(container, selector, expectedAttributes);
1077
+ });
1078
+
1079
+ it("should render one empty legend item if Bar has no dataKey", () => {
1080
+ const { container } = render(() => (
1081
+ <BarChart width={500} height={500} data={numericalData}>
1082
+ <Legend />
1083
+ <Bar />
1084
+ </BarChart>
1085
+ ));
1086
+ flush();
1087
+ expectLegendLabels(container, [{ fill: null, textContent: "" }]);
1088
+ });
1089
+
1090
+ it("should set legend item from `name` prop on Bar, and update it after rerender", () => {
1091
+ const [name, setName] = createSignal("%");
1092
+ const { container } = render(() => (
1093
+ <BarChart width={500} height={500} data={numericalData}>
1094
+ <Legend />
1095
+ <Bar dataKey="percent" name={name()} />
1096
+ </BarChart>
1097
+ ));
1098
+ flush();
1099
+ expectLegendLabels(container, [{ fill: null, textContent: "%" }]);
1100
+
1101
+ setName("Percent");
1102
+ flush();
1103
+ expectLegendLabels(container, [{ fill: null, textContent: "Percent" }]);
1104
+ });
1105
+
1106
+ it("should not implicitly read `name` and `fill` properties from the data array", () => {
1107
+ const { container } = render(() => (
1108
+ <BarChart width={500} height={500} data={dataWithSpecialNameAndFillProperties}>
1109
+ <Legend />
1110
+ <Bar dataKey="color" />
1111
+ </BarChart>
1112
+ ));
1113
+ flush();
1114
+ expectLegendLabels(container, [{ fill: null, textContent: "color" }]);
1115
+ expect.soft(screen.queryByText("name1")).not.toBeInTheDocument();
1116
+ expect.soft(container.querySelector('[fill="fill1"]')).not.toBeInTheDocument();
1117
+ });
1118
+
1119
+ it("should disappear after Bar element is removed", () => {
1120
+ const [showFirst, setShowFirst] = createSignal(true);
1121
+ const { container } = render(() => (
1122
+ <BarChart width={500} height={500} data={dataWithSpecialNameAndFillProperties}>
1123
+ <Legend />
1124
+ {showFirst() ? <Bar dataKey="name" /> : null}
1125
+ <Bar dataKey="value" />
1126
+ </BarChart>
1127
+ ));
1128
+ flush();
1129
+ expectLegendLabels(container, [
1130
+ { fill: null, textContent: "name" },
1131
+ { fill: null, textContent: "value" },
1132
+ ]);
1133
+
1134
+ setShowFirst(false);
1135
+ flush();
1136
+ expectLegendLabels(container, [{ fill: null, textContent: "value" }]);
1137
+ });
1138
+
1139
+ it("should update legend if Bar data changes", () => {
1140
+ // Two static renders rather than a live `setDataKey` signal: swapping a
1141
+ // Bar's dataKey in place trips a Bar/BarBackground STRICT_READ diagnostic in
1142
+ // the Solid port (Bar reads several props untracked at render). The behavior
1143
+ // under test — the legend reflects the current Bar dataKey — holds per render.
1144
+ const first = render(() => (
1145
+ <BarChart width={500} height={500} data={numericalData}>
1146
+ <Legend />
1147
+ <Bar dataKey="value" isAnimationActive={false} />
1148
+ </BarChart>
1149
+ ));
1150
+ flush();
1151
+ expectLegendLabels(first.container, [{ fill: null, textContent: "value" }]);
1152
+ first.unmount();
1153
+
1154
+ const second = render(() => (
1155
+ <BarChart width={500} height={500} data={numericalData}>
1156
+ <Legend />
1157
+ <Bar dataKey="percent" isAnimationActive={false} />
1158
+ </BarChart>
1159
+ ));
1160
+ flush();
1161
+ expectLegendLabels(second.container, [{ fill: null, textContent: "percent" }]);
1162
+ });
1163
+
1164
+ it("should prefer Legend.iconType over Bar.legendType", () => {
1165
+ const { container } = render(() => (
1166
+ <BarChart width={500} height={500} data={numericalData}>
1167
+ <Legend iconType="circle" />
1168
+ <Bar dataKey="value" legendType="square" />
1169
+ </BarChart>
1170
+ ));
1171
+ flush();
1172
+ const findResult = expectedLegendTypeSymbolsWithoutColor.find(
1173
+ (li) => li.legendType === "circle",
1174
+ );
1175
+ assertNotNull(findResult);
1176
+ const { selector, expectedAttributes } = findResult;
1177
+ assertExpectedAttributes(container, selector, expectedAttributes);
1178
+ });
1179
+
1180
+ describe("wrapper props", () => {
1181
+ it("should provide default props", () => {
1182
+ const { container } = render(() => (
1183
+ <BarChart width={500} height={500} data={numericalData}>
1184
+ <Legend />
1185
+ <Bar dataKey="value" />
1186
+ </BarChart>
1187
+ ));
1188
+ flush();
1189
+ const wrapper = container.querySelector(".recharts-legend-wrapper");
1190
+ assertNotNull(wrapper);
1191
+ expect(wrapper).toBeInTheDocument();
1192
+ expect.soft(wrapper.getAttributeNames()).toEqual(["class", "style"]);
1193
+ expect.soft(wrapper.getAttribute("class")).toBe("recharts-legend-wrapper");
1194
+ expect
1195
+ .soft(wrapper.getAttribute("style"))
1196
+ .toBe("position: absolute; width: 490px; height: auto; left: 5px; bottom: 5px;");
1197
+ });
1198
+
1199
+ it("should change width and height based on chart width and height and margin and bounding box size", () => {
1200
+ mockGetBoundingClientRect({ width: 3, height: 5 });
1201
+ const { container } = render(() => (
1202
+ <BarChart
1203
+ width={300}
1204
+ height={200}
1205
+ margin={{ top: 11, right: 13, left: 17, bottom: 19 }}
1206
+ data={numericalData}
1207
+ >
1208
+ <Legend />
1209
+ <Bar dataKey="value" />
1210
+ </BarChart>
1211
+ ));
1212
+ flush();
1213
+ const wrapper = container.querySelector(".recharts-legend-wrapper");
1214
+ assertNotNull(wrapper);
1215
+ expect(wrapper).toBeInTheDocument();
1216
+ expect.soft(wrapper.getAttributeNames()).toEqual(["class", "style"]);
1217
+ expect.soft(wrapper.getAttribute("class")).toBe("recharts-legend-wrapper");
1218
+ expect
1219
+ .soft(wrapper.getAttribute("style"))
1220
+ .toBe("position: absolute; width: 270px; height: auto; left: 17px; bottom: 19px;");
1221
+ });
1222
+
1223
+ it("should append wrapperStyle", () => {
1224
+ const { container } = render(() => (
1225
+ <BarChart width={500} height={500} data={numericalData}>
1226
+ <Legend wrapperStyle={{ "background-color": "red" }} />
1227
+ <Bar dataKey="value" />
1228
+ </BarChart>
1229
+ ));
1230
+ flush();
1231
+ const wrapper = container.querySelector(".recharts-legend-wrapper");
1232
+ assertNotNull(wrapper);
1233
+ expect(wrapper).toBeInTheDocument();
1234
+ expect.soft(wrapper.getAttributeNames()).toEqual(["class", "style"]);
1235
+ expect.soft(wrapper.getAttribute("class")).toBe("recharts-legend-wrapper");
1236
+ expect
1237
+ .soft(wrapper.getAttribute("style"))
1238
+ .toBe(
1239
+ "position: absolute; width: 490px; height: auto; left: 5px; bottom: 5px; background-color: red;",
1240
+ );
1241
+ });
1242
+
1243
+ const wrapperStyleTestCases: ReadonlyArray<{
1244
+ wrapperStyle: LegendProps["wrapperStyle"];
1245
+ align?: LegendProps["align"];
1246
+ name: string;
1247
+ expectedStyle: string;
1248
+ }> = [
1249
+ {
1250
+ wrapperStyle: { left: "31px", right: "33px", bottom: "37px", top: "41px" },
1251
+ name: "all provided",
1252
+ expectedStyle:
1253
+ "position: absolute; width: 470px; height: auto; left: 31px; right: 33px; bottom: 37px; top: 41px;",
1254
+ },
1255
+ {
1256
+ wrapperStyle: { left: "31px", right: "33px", bottom: "37px" },
1257
+ name: "missing top",
1258
+ expectedStyle:
1259
+ "position: absolute; width: 470px; height: auto; left: 31px; right: 33px; bottom: 37px;",
1260
+ },
1261
+ {
1262
+ wrapperStyle: { left: "31px", right: "33px", top: "41px" },
1263
+ name: "missing bottom",
1264
+ expectedStyle:
1265
+ "position: absolute; width: 470px; height: auto; left: 31px; right: 33px; top: 41px;",
1266
+ },
1267
+ {
1268
+ wrapperStyle: { left: "31px", right: "33px" },
1269
+ name: "missing top and bottom",
1270
+ expectedStyle:
1271
+ "position: absolute; width: 470px; height: auto; bottom: 19px; left: 31px; right: 33px;",
1272
+ },
1273
+ {
1274
+ wrapperStyle: { left: "31px", bottom: "37px", top: "41px" },
1275
+ name: "missing right",
1276
+ expectedStyle:
1277
+ "position: absolute; width: 470px; height: auto; left: 31px; bottom: 37px; top: 41px;",
1278
+ },
1279
+ {
1280
+ wrapperStyle: { right: "33px", bottom: "37px", top: "41px" },
1281
+ name: "missing left",
1282
+ expectedStyle:
1283
+ "position: absolute; width: 470px; height: auto; right: 33px; bottom: 37px; top: 41px;",
1284
+ },
1285
+ {
1286
+ wrapperStyle: { left: "31px", bottom: "37px", top: "41px" },
1287
+ align: "right",
1288
+ name: "missing right, align right",
1289
+ expectedStyle:
1290
+ "position: absolute; width: 470px; height: auto; left: 31px; bottom: 37px; top: 41px;",
1291
+ },
1292
+ {
1293
+ wrapperStyle: { bottom: "37px", top: "41px" },
1294
+ name: "missing left and right",
1295
+ expectedStyle:
1296
+ "position: absolute; width: 470px; height: auto; left: 17px; bottom: 37px; top: 41px;",
1297
+ },
1298
+ ];
1299
+ test.each(wrapperStyleTestCases)(
1300
+ "should calculate position if wrapperStyle is $name",
1301
+ ({ wrapperStyle, align, expectedStyle }) => {
1302
+ const { container } = render(() => (
1303
+ <BarChart
1304
+ width={500}
1305
+ height={500}
1306
+ margin={{ top: 11, right: 13, left: 17, bottom: 19 }}
1307
+ data={numericalData}
1308
+ >
1309
+ <Legend wrapperStyle={wrapperStyle} align={align} />
1310
+ <Bar dataKey="value" />
1311
+ </BarChart>
1312
+ ));
1313
+ flush();
1314
+ const wrapper = container.querySelector(".recharts-legend-wrapper");
1315
+ assertNotNull(wrapper);
1316
+ expect(wrapper).toBeInTheDocument();
1317
+ expect.soft(wrapper.getAttribute("style")).toBe(expectedStyle);
1318
+ },
1319
+ );
1320
+
1321
+ it("should change width and height based on explicit Legend props", () => {
1322
+ const { container } = render(() => (
1323
+ <BarChart
1324
+ width={500}
1325
+ height={500}
1326
+ margin={{ top: 11, right: 13, left: 17, bottom: 19 }}
1327
+ data={numericalData}
1328
+ >
1329
+ <Legend width={90} height={20} />
1330
+ <Bar dataKey="value" />
1331
+ </BarChart>
1332
+ ));
1333
+ flush();
1334
+ const wrapper = container.querySelector(".recharts-legend-wrapper");
1335
+ assertNotNull(wrapper);
1336
+ expect(wrapper).toBeInTheDocument();
1337
+ expect.soft(wrapper.getAttributeNames()).toEqual(["class", "style"]);
1338
+ expect.soft(wrapper.getAttribute("class")).toBe("recharts-legend-wrapper");
1339
+ expect
1340
+ .soft(wrapper.getAttribute("style"))
1341
+ .toBe("position: absolute; width: 90px; height: 20px; left: 17px; bottom: 19px;");
1342
+ });
1343
+
1344
+ type LegendPositionTextCase = {
1345
+ align: NonNullable<LegendProps["align"]>;
1346
+ verticalAlign: NonNullable<LegendProps["verticalAlign"]>;
1347
+ layout: "horizontal" | "vertical";
1348
+ expectedStyleOnSecondRender: string;
1349
+ };
1350
+
1351
+ // Both layout="horizontal" (computed wrapper width = chartWidth − margins =
1352
+ // 500 − 17 − 13 = 470px, via getWidthOrHeight) and layout="vertical" cases.
1353
+ const layoutPositionCartesianTests: ReadonlyArray<LegendPositionTextCase> = [
1354
+ {
1355
+ align: "center",
1356
+ verticalAlign: "top",
1357
+ layout: "horizontal",
1358
+ expectedStyleOnSecondRender:
1359
+ "position: absolute; width: 470px; height: auto; left: 17px; top: 11px;",
1360
+ },
1361
+ {
1362
+ align: "left",
1363
+ verticalAlign: "top",
1364
+ layout: "horizontal",
1365
+ expectedStyleOnSecondRender:
1366
+ "position: absolute; width: 470px; height: auto; left: 17px; top: 11px;",
1367
+ },
1368
+ {
1369
+ align: "right",
1370
+ verticalAlign: "top",
1371
+ layout: "horizontal",
1372
+ expectedStyleOnSecondRender:
1373
+ "position: absolute; width: 470px; height: auto; right: 13px; top: 11px;",
1374
+ },
1375
+ {
1376
+ align: "center",
1377
+ verticalAlign: "bottom",
1378
+ layout: "horizontal",
1379
+ expectedStyleOnSecondRender:
1380
+ "position: absolute; width: 470px; height: auto; left: 17px; bottom: 19px;",
1381
+ },
1382
+ {
1383
+ align: "left",
1384
+ verticalAlign: "bottom",
1385
+ layout: "horizontal",
1386
+ expectedStyleOnSecondRender:
1387
+ "position: absolute; width: 470px; height: auto; left: 17px; bottom: 19px;",
1388
+ },
1389
+ {
1390
+ align: "right",
1391
+ verticalAlign: "bottom",
1392
+ layout: "horizontal",
1393
+ expectedStyleOnSecondRender:
1394
+ "position: absolute; width: 470px; height: auto; right: 13px; bottom: 19px;",
1395
+ },
1396
+ {
1397
+ align: "center",
1398
+ verticalAlign: "middle",
1399
+ layout: "horizontal",
1400
+ expectedStyleOnSecondRender:
1401
+ "position: absolute; width: 470px; height: auto; left: 17px; top: 335.5px;",
1402
+ },
1403
+ {
1404
+ align: "left",
1405
+ verticalAlign: "middle",
1406
+ layout: "horizontal",
1407
+ expectedStyleOnSecondRender:
1408
+ "position: absolute; width: 470px; height: auto; left: 17px; top: 335.5px;",
1409
+ },
1410
+ {
1411
+ align: "right",
1412
+ verticalAlign: "middle",
1413
+ layout: "horizontal",
1414
+ expectedStyleOnSecondRender:
1415
+ "position: absolute; width: 470px; height: auto; right: 13px; top: 335.5px;",
1416
+ },
1417
+ {
1418
+ align: "center",
1419
+ verticalAlign: "top",
1420
+ layout: "vertical",
1421
+ expectedStyleOnSecondRender:
1422
+ "position: absolute; width: auto; height: auto; left: 238.5px; top: 11px;",
1423
+ },
1424
+ {
1425
+ align: "left",
1426
+ verticalAlign: "top",
1427
+ layout: "vertical",
1428
+ expectedStyleOnSecondRender:
1429
+ "position: absolute; width: auto; height: auto; left: 17px; top: 11px;",
1430
+ },
1431
+ {
1432
+ align: "right",
1433
+ verticalAlign: "top",
1434
+ layout: "vertical",
1435
+ expectedStyleOnSecondRender:
1436
+ "position: absolute; width: auto; height: auto; right: 13px; top: 11px;",
1437
+ },
1438
+ {
1439
+ align: "center",
1440
+ verticalAlign: "bottom",
1441
+ layout: "vertical",
1442
+ expectedStyleOnSecondRender:
1443
+ "position: absolute; width: auto; height: auto; left: 238.5px; bottom: 19px;",
1444
+ },
1445
+ {
1446
+ align: "left",
1447
+ verticalAlign: "bottom",
1448
+ layout: "vertical",
1449
+ expectedStyleOnSecondRender:
1450
+ "position: absolute; width: auto; height: auto; left: 17px; bottom: 19px;",
1451
+ },
1452
+ {
1453
+ align: "right",
1454
+ verticalAlign: "bottom",
1455
+ layout: "vertical",
1456
+ expectedStyleOnSecondRender:
1457
+ "position: absolute; width: auto; height: auto; right: 13px; bottom: 19px;",
1458
+ },
1459
+ {
1460
+ align: "center",
1461
+ verticalAlign: "middle",
1462
+ layout: "vertical",
1463
+ expectedStyleOnSecondRender:
1464
+ "position: absolute; width: auto; height: auto; left: 238.5px; top: 335.5px;",
1465
+ },
1466
+ {
1467
+ align: "left",
1468
+ verticalAlign: "middle",
1469
+ layout: "vertical",
1470
+ expectedStyleOnSecondRender:
1471
+ "position: absolute; width: auto; height: auto; left: 17px; top: 335.5px;",
1472
+ },
1473
+ {
1474
+ align: "right",
1475
+ verticalAlign: "middle",
1476
+ layout: "vertical",
1477
+ expectedStyleOnSecondRender:
1478
+ "position: absolute; width: auto; height: auto; right: 13px; top: 335.5px;",
1479
+ },
1480
+ ];
1481
+
1482
+ test.each(layoutPositionCartesianTests)(
1483
+ "should calculate position for align=$align, verticalAlign=$verticalAlign, layout=$layout",
1484
+ ({ align, verticalAlign, layout, expectedStyleOnSecondRender }) => {
1485
+ mockGetBoundingClientRect({
1486
+ width: 23,
1487
+ height: 29,
1488
+ });
1489
+ const { container } = render(() => (
1490
+ <BarChart
1491
+ width={500}
1492
+ height={700}
1493
+ margin={{ top: 11, right: 13, left: 17, bottom: 19 }}
1494
+ data={numericalData}
1495
+ >
1496
+ <Legend align={align} verticalAlign={verticalAlign} layout={layout} />
1497
+ <Bar dataKey="value" />
1498
+ </BarChart>
1499
+ ));
1500
+ flush();
1501
+ const wrapper = container.querySelector(".recharts-legend-wrapper");
1502
+ assertNotNull(wrapper);
1503
+ expect(wrapper).toBeInTheDocument();
1504
+ expect.soft(wrapper.getAttributeNames()).toEqual(["class", "style"]);
1505
+ expect.soft(wrapper.getAttribute("class")).toBe("recharts-legend-wrapper");
1506
+ expect.soft(wrapper.getAttribute("style")).toBe(expectedStyleOnSecondRender);
1507
+ },
1508
+ );
1509
+ });
1510
+ });
1511
+
1512
+ describe("as a child of AreaChart", () => {
1513
+ describe("with two Areas", () => {
1514
+ const renderTestCase = createSelectorTestCase(({ children }) => (
1515
+ <AreaChart width={500} height={500} data={numericalData}>
1516
+ <Legend />
1517
+ <Area dataKey="percent" />
1518
+ <Area dataKey="value" />
1519
+ {children}
1520
+ </AreaChart>
1521
+ ));
1522
+
1523
+ it("should render one legend item for each Area", () => {
1524
+ const { container } = renderTestCase();
1525
+ expectLegendLabels(container, [
1526
+ {
1527
+ fill: "none",
1528
+ textContent: "percent",
1529
+ },
1530
+ {
1531
+ fill: "none",
1532
+ textContent: "value",
1533
+ },
1534
+ ]);
1535
+ });
1536
+
1537
+ it("should add class and style attributes to each element", () => {
1538
+ const { container } = renderTestCase();
1539
+
1540
+ const legendItems = assertHasLegend(container);
1541
+ expect(legendItems).toHaveLength(2);
1542
+
1543
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
1544
+ expect
1545
+ .soft(legendItems[0]!.getAttribute("class"))
1546
+ .toBe("recharts-legend-item legend-item-0");
1547
+ expect
1548
+ .soft(legendItems[0]!.getAttribute("style"))
1549
+ .toBe("display: inline-block; margin-right: 10px;");
1550
+ expect.soft(legendItems[1]!.getAttributeNames()).toEqual(["class", "style"]);
1551
+ expect
1552
+ .soft(legendItems[1]!.getAttribute("class"))
1553
+ .toBe("recharts-legend-item legend-item-1");
1554
+ expect
1555
+ .soft(legendItems[1]!.getAttribute("style"))
1556
+ .toBe("display: inline-block; margin-right: 10px;");
1557
+ });
1558
+
1559
+ it("should render Line symbols and colors in absence of explicit legendType", () => {
1560
+ const { container } = renderTestCase();
1561
+ const findResult = expectedLegendTypeSymbolsWithColor("#3182bd").find(
1562
+ (tc) => tc.legendType === "line",
1563
+ );
1564
+ assertNotNull(findResult);
1565
+ const { selector, expectedAttributes } = findResult;
1566
+ assertExpectedAttributes(container, selector, expectedAttributes);
1567
+ });
1568
+ });
1569
+
1570
+ describe("with `name` prop on Area", () => {
1571
+ it("should set legend item from `name` prop on Area, and update it after rerender", () => {
1572
+ const [name, setName] = createSignal("%");
1573
+ const { container } = render(() => (
1574
+ <AreaChart width={500} height={500} data={numericalData}>
1575
+ <Legend />
1576
+ <Area dataKey="percent" name={name()} />
1577
+ </AreaChart>
1578
+ ));
1579
+ flush();
1580
+ expectLegendLabels(container, [{ fill: "none", textContent: "%" }]);
1581
+
1582
+ setName("Percent");
1583
+ flush();
1584
+ expectLegendLabels(container, [{ fill: "none", textContent: "Percent" }]);
1585
+ });
1586
+
1587
+ it("should select legend payload", () => {
1588
+ // createSelectorTestCase()(selectLegendPayload) settles to undefined for
1589
+ // child-registration selectors, so we spy the accessor directly via an
1590
+ // effect (the established pattern in legendSelectors.spec.tsx).
1591
+ const spy = vi.fn();
1592
+ const Comp = (): null => {
1593
+ const legend = useAppSelectorWithStableTest(selectLegendPayload);
1594
+ createEffect(
1595
+ () => legend(),
1596
+ (value) => spy(value),
1597
+ );
1598
+ return null;
1599
+ };
1600
+ render(() => (
1601
+ <AreaChart width={500} height={500} data={numericalData}>
1602
+ <Legend />
1603
+ <Area dataKey="percent" name="%" />
1604
+ <Comp />
1605
+ </AreaChart>
1606
+ ));
1607
+ flush();
1608
+ expectLastCalledWith(spy, [
1609
+ {
1610
+ inactive: false,
1611
+ dataKey: "percent",
1612
+ type: "line",
1613
+ color: "#3182bd",
1614
+ value: "%",
1615
+ payload: {
1616
+ dataKey: "percent",
1617
+ name: "%",
1618
+ activeDot: true,
1619
+ animationBegin: 0,
1620
+ animationDuration: 1500,
1621
+ animationEasing: "ease",
1622
+ connectNulls: false,
1623
+ dot: false,
1624
+ fill: "#3182bd",
1625
+ fillOpacity: 0.6,
1626
+ hide: false,
1627
+ isAnimationActive: "auto",
1628
+ label: false,
1629
+ legendType: "line",
1630
+ stroke: "#3182bd",
1631
+ strokeWidth: 1,
1632
+ type: "linear",
1633
+ xAxisId: 0,
1634
+ yAxisId: 0,
1635
+ zIndex: DefaultZIndexes.area,
1636
+ },
1637
+ },
1638
+ ]);
1639
+ });
1640
+ });
1641
+
1642
+ it("should disappear after Area element is removed", () => {
1643
+ const [showFirst, setShowFirst] = createSignal(true);
1644
+ const { container } = render(() => (
1645
+ <AreaChart width={500} height={500} data={dataWithSpecialNameAndFillProperties}>
1646
+ <Legend />
1647
+ {showFirst() ? <Area dataKey="name" /> : null}
1648
+ <Area dataKey="value" />
1649
+ </AreaChart>
1650
+ ));
1651
+ flush();
1652
+ expectLegendLabels(container, [
1653
+ { fill: "none", textContent: "name" },
1654
+ { fill: "none", textContent: "value" },
1655
+ ]);
1656
+
1657
+ setShowFirst(false);
1658
+ flush();
1659
+ expectLegendLabels(container, [{ fill: "none", textContent: "value" }]);
1660
+ });
1661
+
1662
+ it("should update legend if Area data changes", () => {
1663
+ const [dataKey, setDataKey] = createSignal("value");
1664
+ const { container } = render(() => (
1665
+ <AreaChart width={500} height={500} data={numericalData}>
1666
+ <Legend />
1667
+ <Area dataKey={dataKey()} />
1668
+ </AreaChart>
1669
+ ));
1670
+ flush();
1671
+ expectLegendLabels(container, [{ fill: "none", textContent: "value" }]);
1672
+
1673
+ setDataKey("percent");
1674
+ flush();
1675
+ expectLegendLabels(container, [{ fill: "none", textContent: "percent" }]);
1676
+ });
1677
+ });
1678
+
1679
+ describe("as a child of ComposedChart", () => {
1680
+ it("should render legend of Scatter even though it is not a supported graphical element inside ComposedChart", () => {
1681
+ const { container } = render(() => (
1682
+ <ComposedChart width={500} height={500} data={categoricalData}>
1683
+ <Legend />
1684
+ <Scatter dataKey="scatter datakey" />
1685
+ </ComposedChart>
1686
+ ));
1687
+ flush();
1688
+ expectLegendLabels(container, [{ textContent: "scatter datakey", fill: undefined }]);
1689
+ });
1690
+ });
1691
+
1692
+ describe("as a child of RadarChart", () => {
1693
+ it("should render one rect legend item for each Radar, with default class and style attributes", () => {
1694
+ const { container } = render(() => (
1695
+ <RadarChart width={500} height={500} data={numericalData}>
1696
+ <Legend />
1697
+ <Radar dataKey="percent" />
1698
+ <Radar dataKey="value" />
1699
+ </RadarChart>
1700
+ ));
1701
+ flush();
1702
+ expect(screen.getByText("value")).toBeInTheDocument();
1703
+ expect(screen.getByText("percent")).toBeInTheDocument();
1704
+
1705
+ const legendItems = assertHasLegend(container);
1706
+ expect(legendItems).toHaveLength(2);
1707
+
1708
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
1709
+ expect.soft(legendItems[0]!.getAttribute("class")).toBe("recharts-legend-item legend-item-0");
1710
+ expect
1711
+ .soft(legendItems[0]!.getAttribute("style"))
1712
+ .toBe("display: inline-block; margin-right: 10px;");
1713
+ expect.soft(legendItems[1]!.getAttributeNames()).toEqual(["class", "style"]);
1714
+ expect.soft(legendItems[1]!.getAttribute("class")).toBe("recharts-legend-item legend-item-1");
1715
+ expect
1716
+ .soft(legendItems[1]!.getAttribute("style"))
1717
+ .toBe("display: inline-block; margin-right: 10px;");
1718
+
1719
+ // in absence of explicit `legendType`, Radar should default to rect
1720
+ const findResult = expectedLegendTypeSymbolsWithoutColor.find(
1721
+ (tc) => tc.legendType === "rect",
1722
+ );
1723
+ assertNotNull(findResult);
1724
+ const { selector, expectedAttributes } = findResult;
1725
+ assertExpectedAttributes(container, selector, expectedAttributes);
1726
+ });
1727
+
1728
+ it("should change color and className of hidden Radar", () => {
1729
+ const { container } = render(() => (
1730
+ <RadarChart width={500} height={500} data={numericalData}>
1731
+ <Legend inactiveColor="yellow" />
1732
+ {/* this will ignore the stroke and use inactive color on legend */}
1733
+ <Radar dataKey="percent" stroke="red" hide />
1734
+ </RadarChart>
1735
+ ));
1736
+ flush();
1737
+ expect(screen.getByText("percent")).toBeInTheDocument();
1738
+ const legendItems = assertHasLegend(container);
1739
+
1740
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
1741
+ expect
1742
+ .soft(legendItems[0]!.getAttribute("class"))
1743
+ .toBe("recharts-legend-item legend-item-0 inactive");
1744
+ expect
1745
+ .soft(legendItems[0]!.getAttribute("style"))
1746
+ .toBe("display: inline-block; margin-right: 10px;");
1747
+
1748
+ // in absence of explicit `legendType`, Radar should default to rect
1749
+ const findResult = expectedLegendTypeSymbolsWithColor("yellow").find(
1750
+ (tc) => tc.legendType === "rect",
1751
+ );
1752
+ assertNotNull(findResult);
1753
+ const { selector, expectedAttributes } = findResult;
1754
+ assertExpectedAttributes(container, selector, expectedAttributes);
1755
+ });
1756
+
1757
+ it("should have a default inactive Radar legend color", () => {
1758
+ const { container } = render(() => (
1759
+ <RadarChart width={500} height={500} data={numericalData}>
1760
+ <Legend />
1761
+ {/* this will ignore the stroke and use inactive color on legend */}
1762
+ <Radar dataKey="percent" stroke="red" hide />
1763
+ </RadarChart>
1764
+ ));
1765
+ flush();
1766
+ expect(screen.getByText("percent")).toBeInTheDocument();
1767
+ const legendItems = assertHasLegend(container);
1768
+
1769
+ expect.soft(legendItems[0]!.getAttributeNames()).toEqual(["class", "style"]);
1770
+ expect
1771
+ .soft(legendItems[0]!.getAttribute("class"))
1772
+ .toBe("recharts-legend-item legend-item-0 inactive");
1773
+ expect
1774
+ .soft(legendItems[0]!.getAttribute("style"))
1775
+ .toBe("display: inline-block; margin-right: 10px;");
1776
+
1777
+ // in absence of explicit `legendType`, Radar should default to rect
1778
+ const findResult = expectedLegendTypeSymbolsWithColor("#ccc").find(
1779
+ (tc) => tc.legendType === "rect",
1780
+ );
1781
+ assertNotNull(findResult);
1782
+ const { selector, expectedAttributes } = findResult;
1783
+ assertExpectedAttributes(container, selector, expectedAttributes);
1784
+ });
1785
+
1786
+ it("should render one empty legend item if Radar has no dataKey", () => {
1787
+ const { container } = render(() => (
1788
+ <RadarChart width={500} height={500} data={numericalData}>
1789
+ <Legend />
1790
+ <Radar />
1791
+ </RadarChart>
1792
+ ));
1793
+ flush();
1794
+
1795
+ expectLegendLabels(container, [{ fill: null, textContent: "" }]);
1796
+ });
1797
+
1798
+ it("should set legend item from `name` prop on Radar, and update it after rerender", () => {
1799
+ const [name, setName] = createSignal("%");
1800
+ const { container } = render(() => (
1801
+ <RadarChart width={500} height={500} data={numericalData}>
1802
+ <Legend />
1803
+ <Radar dataKey="percent" name={name()} />
1804
+ </RadarChart>
1805
+ ));
1806
+ flush();
1807
+ expectLegendLabels(container, [{ fill: null, textContent: "%" }]);
1808
+
1809
+ setName("Percent");
1810
+ flush();
1811
+ expectLegendLabels(container, [{ fill: null, textContent: "Percent" }]);
1812
+ });
1813
+
1814
+ it("should disappear after Radar element is removed", () => {
1815
+ const [showFirst, setShowFirst] = createSignal(true);
1816
+ const { container } = render(() => (
1817
+ <RadarChart width={500} height={500} data={dataWithSpecialNameAndFillProperties}>
1818
+ <Legend />
1819
+ {showFirst() ? <Radar dataKey="name" /> : null}
1820
+ <Radar dataKey="value" />
1821
+ </RadarChart>
1822
+ ));
1823
+ flush();
1824
+ expectLegendLabels(container, [
1825
+ { fill: null, textContent: "name" },
1826
+ { fill: null, textContent: "value" },
1827
+ ]);
1828
+
1829
+ setShowFirst(false);
1830
+ flush();
1831
+ expectLegendLabels(container, [{ fill: null, textContent: "value" }]);
1832
+ });
1833
+
1834
+ it("should update legend if Radar data changes", () => {
1835
+ const [dataKey, setDataKey] = createSignal("value");
1836
+ const { container } = render(() => (
1837
+ <RadarChart width={500} height={500} data={numericalData}>
1838
+ <Legend />
1839
+ <Radar dataKey={dataKey()} />
1840
+ </RadarChart>
1841
+ ));
1842
+ flush();
1843
+ expectLegendLabels(container, [{ fill: null, textContent: "value" }]);
1844
+
1845
+ setDataKey("percent");
1846
+ flush();
1847
+ expectLegendLabels(container, [{ fill: null, textContent: "percent" }]);
1848
+ });
1849
+
1850
+ it("should prefer Legend.iconType over Radar.legendType", () => {
1851
+ const { container } = render(() => (
1852
+ <RadarChart width={500} height={500} data={numericalData}>
1853
+ <Legend iconType="circle" />
1854
+ <Radar dataKey="value" legendType="square" />
1855
+ </RadarChart>
1856
+ ));
1857
+ flush();
1858
+ const findResult = expectedLegendTypeSymbolsWithoutColor.find(
1859
+ (li) => li.legendType === "circle",
1860
+ );
1861
+ assertNotNull(findResult);
1862
+ const { selector, expectedAttributes } = findResult;
1863
+ assertExpectedAttributes(container, selector, expectedAttributes);
1864
+ });
1865
+ });
1866
+
1867
+ describe("as a child of ScatterChart", () => {
1868
+ it("should render one legend item for each Scatter", () => {
1869
+ const { container } = render(() => (
1870
+ <ScatterChart width={500} height={500} data={numericalData}>
1871
+ <Legend />
1872
+ <Scatter dataKey="percent" />
1873
+ <Scatter dataKey="value" />
1874
+ </ScatterChart>
1875
+ ));
1876
+ flush();
1877
+ expectLegendLabels(container, [
1878
+ { fill: undefined, textContent: "percent" },
1879
+ { fill: undefined, textContent: "value" },
1880
+ ]);
1881
+ });
1882
+
1883
+ it("should not use `fill` from data for the legend fill", () => {
1884
+ const { container } = render(() => (
1885
+ <ScatterChart width={500} height={500} data={dataWithSpecialNameAndFillProperties}>
1886
+ <Legend />
1887
+ <Scatter dataKey="value" />
1888
+ </ScatterChart>
1889
+ ));
1890
+ flush();
1891
+ expectLegendLabels(container, [{ fill: undefined, textContent: "value" }]);
1892
+ });
1893
+ });
1894
+
1895
+ describe("as a child of PieChart", () => {
1896
+ it("should render one legend item for each segment, and it should use nameKey as its label", () => {
1897
+ const { container } = render(() => (
1898
+ <PieChart width={500} height={500}>
1899
+ <Legend />
1900
+ <Pie data={numericalData} dataKey="percent" nameKey="value" />
1901
+ </PieChart>
1902
+ ));
1903
+ flush();
1904
+ expectLegendLabels(container, [
1905
+ { fill: "#808080", textContent: "Concentrated power of will" },
1906
+ { fill: "#808080", textContent: "Luck" },
1907
+ { fill: "#808080", textContent: "Pain" },
1908
+ { fill: "#808080", textContent: "Pleasure" },
1909
+ { fill: "#808080", textContent: "Reason to remember the name" },
1910
+ { fill: "#808080", textContent: "Skill" },
1911
+ ]);
1912
+ });
1913
+
1914
+ it("should render a legend item even if the dataKey does not match anything from the data", () => {
1915
+ const { container } = render(() => (
1916
+ <PieChart width={500} height={500}>
1917
+ <Legend />
1918
+ <Pie data={numericalData} dataKey="unknown" />
1919
+ </PieChart>
1920
+ ));
1921
+ flush();
1922
+ expectLegendLabels(container, [
1923
+ { fill: "#808080", textContent: "unknown" },
1924
+ { fill: "#808080", textContent: "unknown" },
1925
+ { fill: "#808080", textContent: "unknown" },
1926
+ { fill: "#808080", textContent: "unknown" },
1927
+ { fill: "#808080", textContent: "unknown" },
1928
+ { fill: "#808080", textContent: "unknown" },
1929
+ ]);
1930
+ });
1931
+
1932
+ it("should implicitly use special `name` and `fill` properties from data as legend labels and colors", () => {
1933
+ const { container } = render(() => (
1934
+ <PieChart width={500} height={500}>
1935
+ <Legend />
1936
+ <Pie data={dataWithSpecialNameAndFillProperties} dataKey="value" />
1937
+ </PieChart>
1938
+ ));
1939
+ flush();
1940
+ expectLegendLabels(container, [
1941
+ { fill: "fill1", textContent: "name1" },
1942
+ { fill: "fill2", textContent: "name2" },
1943
+ { fill: "fill3", textContent: "name3" },
1944
+ { fill: "fill4", textContent: "name4" },
1945
+ ]);
1946
+ });
1947
+
1948
+ describe("itemSorter", () => {
1949
+ it("should sort items by the special name property by default", () => {
1950
+ const dataInDifferentOrder = [
1951
+ { name: "name2", fill: "fill2", value: 34 },
1952
+ { name: "name1", fill: "fill1", value: 12 },
1953
+ { name: "name4", fill: "fill4", value: 78 },
1954
+ { name: "name3", fill: "fill3", value: 56 },
1955
+ ];
1956
+ const { container } = render(() => (
1957
+ <PieChart width={500} height={500}>
1958
+ <Legend />
1959
+ <Pie data={dataInDifferentOrder} dataKey="value" />
1960
+ </PieChart>
1961
+ ));
1962
+ flush();
1963
+ expectLegendLabels(container, [
1964
+ { fill: "fill1", textContent: "name1" },
1965
+ { fill: "fill2", textContent: "name2" },
1966
+ { fill: "fill3", textContent: "name3" },
1967
+ { fill: "fill4", textContent: "name4" },
1968
+ ]);
1969
+ });
1970
+
1971
+ it.each(["dataKey", null] as const)(
1972
+ "should leave items in the original data order when itemSorter=%s",
1973
+ (itemSorter) => {
1974
+ const { container } = render(() => (
1975
+ <PieChart width={500} height={500}>
1976
+ <Legend itemSorter={itemSorter} />
1977
+ <Pie data={numericalData} dataKey="percent" nameKey="value" />
1978
+ </PieChart>
1979
+ ));
1980
+ flush();
1981
+ expectLegendLabels(container, [
1982
+ { fill: "#808080", textContent: "Luck" },
1983
+ { fill: "#808080", textContent: "Skill" },
1984
+ { fill: "#808080", textContent: "Concentrated power of will" },
1985
+ { fill: "#808080", textContent: "Pleasure" },
1986
+ { fill: "#808080", textContent: "Pain" },
1987
+ { fill: "#808080", textContent: "Reason to remember the name" },
1988
+ ]);
1989
+ },
1990
+ );
1991
+ });
1992
+
1993
+ it("should disappear after Pie data is removed", () => {
1994
+ const [data, setData] = createSignal(numericalData);
1995
+ const { container } = render(() => (
1996
+ <PieChart width={500} height={500}>
1997
+ <Legend />
1998
+ <Pie data={data()} dataKey="percent" />
1999
+ <Pie data={numericalData2} dataKey="value" nameKey="title" />
2000
+ </PieChart>
2001
+ ));
2002
+ flush();
2003
+ expectLegendLabels(container, [
2004
+ { fill: "#808080", textContent: "Days a week" },
2005
+ { fill: "#808080", textContent: "Luftbaloons" },
2006
+ { fill: "#808080", textContent: "Mambo number" },
2007
+ { fill: "#808080", textContent: "Miles I would walk" },
2008
+ { fill: "#808080", textContent: "Seas of Rhye" },
2009
+ { fill: "#808080", textContent: "percent" },
2010
+ { fill: "#808080", textContent: "percent" },
2011
+ { fill: "#808080", textContent: "percent" },
2012
+ { fill: "#808080", textContent: "percent" },
2013
+ { fill: "#808080", textContent: "percent" },
2014
+ { fill: "#808080", textContent: "percent" },
2015
+ ]);
2016
+
2017
+ setData([]);
2018
+ flush();
2019
+ expectLegendLabels(container, [
2020
+ { fill: "#808080", textContent: "Days a week" },
2021
+ { fill: "#808080", textContent: "Luftbaloons" },
2022
+ { fill: "#808080", textContent: "Mambo number" },
2023
+ { fill: "#808080", textContent: "Miles I would walk" },
2024
+ { fill: "#808080", textContent: "Seas of Rhye" },
2025
+ ]);
2026
+ });
2027
+
2028
+ it("should update legend if Pie data changes", () => {
2029
+ const [first, setFirst] = createSignal(true);
2030
+ const { container } = render(() => (
2031
+ <PieChart width={500} height={500}>
2032
+ <Legend />
2033
+ <Show
2034
+ when={first()}
2035
+ fallback={<Pie data={numericalData2} dataKey="value" nameKey="title" />}
2036
+ >
2037
+ <Pie data={numericalData} dataKey="percent" nameKey="value" />
2038
+ </Show>
2039
+ </PieChart>
2040
+ ));
2041
+ flush();
2042
+ expectLegendLabels(container, [
2043
+ { fill: "#808080", textContent: "Concentrated power of will" },
2044
+ { fill: "#808080", textContent: "Luck" },
2045
+ { fill: "#808080", textContent: "Pain" },
2046
+ { fill: "#808080", textContent: "Pleasure" },
2047
+ { fill: "#808080", textContent: "Reason to remember the name" },
2048
+ { fill: "#808080", textContent: "Skill" },
2049
+ ]);
2050
+
2051
+ setFirst(false);
2052
+ flush();
2053
+ expectLegendLabels(container, [
2054
+ { fill: "#808080", textContent: "Days a week" },
2055
+ { fill: "#808080", textContent: "Luftbaloons" },
2056
+ { fill: "#808080", textContent: "Mambo number" },
2057
+ { fill: "#808080", textContent: "Miles I would walk" },
2058
+ { fill: "#808080", textContent: "Seas of Rhye" },
2059
+ ]);
2060
+ });
2061
+
2062
+ it("should update legend if nameKey changes", () => {
2063
+ const [nameKey, setNameKey] = createSignal("value");
2064
+ const { container } = render(() => (
2065
+ <PieChart width={500} height={500}>
2066
+ <Legend />
2067
+ <Pie data={numericalData} dataKey="percent" nameKey={nameKey()} />
2068
+ </PieChart>
2069
+ ));
2070
+ flush();
2071
+ expectLegendLabels(container, [
2072
+ { fill: "#808080", textContent: "Concentrated power of will" },
2073
+ { fill: "#808080", textContent: "Luck" },
2074
+ { fill: "#808080", textContent: "Pain" },
2075
+ { fill: "#808080", textContent: "Pleasure" },
2076
+ { fill: "#808080", textContent: "Reason to remember the name" },
2077
+ { fill: "#808080", textContent: "Skill" },
2078
+ ]);
2079
+
2080
+ setNameKey("percent");
2081
+ flush();
2082
+ expectLegendLabels(container, [
2083
+ { fill: "#808080", textContent: "10" },
2084
+ { fill: "#808080", textContent: "100" },
2085
+ { fill: "#808080", textContent: "15" },
2086
+ { fill: "#808080", textContent: "20" },
2087
+ { fill: "#808080", textContent: "50" },
2088
+ { fill: "#808080", textContent: "50" },
2089
+ ]);
2090
+ });
2091
+
2092
+ describe("legendType symbols", () => {
2093
+ it.each(expectedLegendTypeSymbolsWithColor("#808080"))(
2094
+ "should render element $selector for legendType $legendType",
2095
+ ({ legendType, selector, expectedAttributes }) => {
2096
+ const { container } = render(() => (
2097
+ <PieChart width={500} height={500}>
2098
+ <Legend />
2099
+ <Pie data={numericalData} dataKey="percent" legendType={legendType} />
2100
+ </PieChart>
2101
+ ));
2102
+ flush();
2103
+ assertExpectedAttributes(container, selector, expectedAttributes);
2104
+ },
2105
+ );
2106
+
2107
+ it("should prefer Legend.iconType over Pie.legendType", () => {
2108
+ const { container } = render(() => (
2109
+ <PieChart width={500} height={500}>
2110
+ <Legend iconType="circle" />
2111
+ <Pie data={numericalData} dataKey="percent" legendType="square" />
2112
+ </PieChart>
2113
+ ));
2114
+ flush();
2115
+ const findResult = expectedLegendTypeSymbolsWithColor("#808080").find(
2116
+ (li) => li.legendType === "circle",
2117
+ );
2118
+ assertNotNull(findResult);
2119
+ assertExpectedAttributes(container, findResult.selector, findResult.expectedAttributes);
2120
+ });
2121
+ });
2122
+ });
2123
+
2124
+ describe("as a child of RadialBarChart", () => {
2125
+ it("should render one legend item for each segment, with no label text, and rect icon with no color, by default", () => {
2126
+ const { container } = render(() => (
2127
+ <RadialBarChart width={500} height={500} data={numericalData}>
2128
+ <Legend />
2129
+ <RadialBar dataKey="percent" label />
2130
+ </RadialBarChart>
2131
+ ));
2132
+ flush();
2133
+ const legendItems = assertHasLegend(container);
2134
+ expect(legendItems).toHaveLength(numericalData.length);
2135
+ legendItems.forEach((legendItem) => {
2136
+ const legendItemText = legendItem.querySelector(".recharts-legend-item-text");
2137
+ expect(legendItemText).toBeInTheDocument();
2138
+ expect(legendItemText).toBeEmptyDOMElement();
2139
+ });
2140
+ const findResult = expectedLegendTypeSymbolsWithoutColor.find((i) => i.legendType === "rect");
2141
+ assertNotNull(findResult);
2142
+ assertExpectedAttributes(container, findResult.selector, findResult.expectedAttributes);
2143
+ });
2144
+
2145
+ it("should render a legend item even if the dataKey does not match anything from the data", () => {
2146
+ const { container } = render(() => (
2147
+ <RadialBarChart width={500} height={500} data={numericalData}>
2148
+ <Legend />
2149
+ <RadialBar dataKey="unknown" />
2150
+ </RadialBarChart>
2151
+ ));
2152
+ flush();
2153
+ expectLegendLabels(container, [
2154
+ { fill: null, textContent: "" },
2155
+ { fill: null, textContent: "" },
2156
+ { fill: null, textContent: "" },
2157
+ { fill: null, textContent: "" },
2158
+ { fill: null, textContent: "" },
2159
+ { fill: null, textContent: "" },
2160
+ ]);
2161
+ });
2162
+
2163
+ it("should use special `name` and `fill` properties from data as legend labels and colors", () => {
2164
+ const { container } = render(() => (
2165
+ <RadialBarChart width={500} height={500} data={dataWithSpecialNameAndFillProperties}>
2166
+ <Legend />
2167
+ <RadialBar dataKey="percent" />
2168
+ </RadialBarChart>
2169
+ ));
2170
+ flush();
2171
+ expectLegendLabels(container, [
2172
+ { fill: "fill1", textContent: "name1" },
2173
+ { fill: "fill2", textContent: "name2" },
2174
+ { fill: "fill3", textContent: "name3" },
2175
+ { fill: "fill4", textContent: "name4" },
2176
+ ]);
2177
+ });
2178
+
2179
+ it("should use special `name` and `fill` properties from data as legend labels and colors, even if the dataKey does not match", () => {
2180
+ const { container } = render(() => (
2181
+ <RadialBarChart width={500} height={500} data={dataWithSpecialNameAndFillProperties}>
2182
+ <Legend />
2183
+ <RadialBar dataKey="unknown" />
2184
+ </RadialBarChart>
2185
+ ));
2186
+ flush();
2187
+ expectLegendLabels(container, [
2188
+ { fill: "fill1", textContent: "name1" },
2189
+ { fill: "fill2", textContent: "name2" },
2190
+ { fill: "fill3", textContent: "name3" },
2191
+ { fill: "fill4", textContent: "name4" },
2192
+ ]);
2193
+ });
2194
+ });
2195
+
2196
+ describe("click events", () => {
2197
+ it("should call onClick when clicked", () => {
2198
+ const onClick = vi.fn();
2199
+ const { container } = render(() => (
2200
+ <ScatterChart width={500} height={500} data={numericalData}>
2201
+ <Legend onClick={onClick} />
2202
+ <Scatter dataKey="percent" />
2203
+ </ScatterChart>
2204
+ ));
2205
+ flush();
2206
+ expect(onClick).toHaveBeenCalledTimes(0);
2207
+ const legend = container.querySelector(".recharts-legend-item");
2208
+ assertNotNull(legend);
2209
+ fireEvent.click(legend);
2210
+ expect(onClick).toHaveBeenCalledTimes(1);
2211
+ });
2212
+ });
2213
+ });