@buildcanada/charts 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 (404) hide show
  1. package/LICENSE.md +8 -0
  2. package/README.md +113 -0
  3. package/package.json +137 -0
  4. package/src/components/BodyPortal/BodyPortal.tsx +40 -0
  5. package/src/components/Button/Button.scss +110 -0
  6. package/src/components/Button/Button.tsx +101 -0
  7. package/src/components/Checkbox.scss +93 -0
  8. package/src/components/Checkbox.tsx +47 -0
  9. package/src/components/ExpandableToggle/ExpandableToggle.scss +123 -0
  10. package/src/components/ExpandableToggle/ExpandableToggle.tsx +60 -0
  11. package/src/components/GrapherTabIcon.tsx +156 -0
  12. package/src/components/GrapherTrendArrow.scss +16 -0
  13. package/src/components/GrapherTrendArrow.tsx +30 -0
  14. package/src/components/Halo/Halo.tsx +44 -0
  15. package/src/components/LabeledSwitch/LabeledSwitch.scss +109 -0
  16. package/src/components/LabeledSwitch/LabeledSwitch.tsx +62 -0
  17. package/src/components/MarkdownTextWrap/MarkdownTextWrap.tsx +1173 -0
  18. package/src/components/OverlayHeader.scss +18 -0
  19. package/src/components/OverlayHeader.tsx +29 -0
  20. package/src/components/RadioButton.scss +69 -0
  21. package/src/components/RadioButton.tsx +42 -0
  22. package/src/components/SimpleMarkdownText.tsx +89 -0
  23. package/src/components/TextInput.scss +17 -0
  24. package/src/components/TextInput.tsx +19 -0
  25. package/src/components/TextWrap/TextWrap.tsx +361 -0
  26. package/src/components/TextWrap/TextWrapUtils.ts +32 -0
  27. package/src/components/closeButton/CloseButton.scss +40 -0
  28. package/src/components/closeButton/CloseButton.tsx +27 -0
  29. package/src/components/index.ts +70 -0
  30. package/src/components/loadingIndicator/LoadingIndicator.scss +40 -0
  31. package/src/components/loadingIndicator/LoadingIndicator.tsx +28 -0
  32. package/src/components/markdown/remarkPlainLinks.ts +36 -0
  33. package/src/components/reactUtil.ts +20 -0
  34. package/src/components/stubs/CodeSnippet.tsx +19 -0
  35. package/src/components/stubs/DataCitation.tsx +16 -0
  36. package/src/components/stubs/IndicatorKeyData.tsx +45 -0
  37. package/src/components/stubs/IndicatorProcessing.tsx +15 -0
  38. package/src/components/stubs/IndicatorSources.tsx +15 -0
  39. package/src/components/styles/colors.scss +113 -0
  40. package/src/components/styles/mixins.scss +630 -0
  41. package/src/components/styles/typography.scss +579 -0
  42. package/src/components/styles/util.scss +89 -0
  43. package/src/components/styles/variables.scss +208 -0
  44. package/src/config/ChartsConfig.ts +163 -0
  45. package/src/config/ChartsProvider.tsx +157 -0
  46. package/src/config/index.ts +20 -0
  47. package/src/core-table/CoreTable.ts +1355 -0
  48. package/src/core-table/CoreTableColumns.ts +973 -0
  49. package/src/core-table/CoreTableUtils.ts +793 -0
  50. package/src/core-table/ErrorValues.ts +73 -0
  51. package/src/core-table/OwidTable.ts +1175 -0
  52. package/src/core-table/OwidTableSynthesizers.ts +272 -0
  53. package/src/core-table/OwidTableUtil.ts +76 -0
  54. package/src/core-table/Transforms.ts +484 -0
  55. package/src/core-table/index.ts +82 -0
  56. package/src/explorer/ColumnGrammar.ts +217 -0
  57. package/src/explorer/Explorer.sample.ts +212 -0
  58. package/src/explorer/Explorer.scss +148 -0
  59. package/src/explorer/Explorer.tsx +1283 -0
  60. package/src/explorer/ExplorerConstants.ts +85 -0
  61. package/src/explorer/ExplorerControls.scss +156 -0
  62. package/src/explorer/ExplorerControls.tsx +210 -0
  63. package/src/explorer/ExplorerDecisionMatrix.ts +471 -0
  64. package/src/explorer/ExplorerGrammar.ts +161 -0
  65. package/src/explorer/ExplorerProgram.ts +568 -0
  66. package/src/explorer/ExplorerUtils.ts +59 -0
  67. package/src/explorer/GrapherGrammar.ts +387 -0
  68. package/src/explorer/gridLang/GrammarUtils.ts +121 -0
  69. package/src/explorer/gridLang/GridCell.ts +298 -0
  70. package/src/explorer/gridLang/GridLangConstants.ts +255 -0
  71. package/src/explorer/gridLang/GridProgram.ts +311 -0
  72. package/src/explorer/gridLang/readme.md +17 -0
  73. package/src/explorer/index.ts +69 -0
  74. package/src/explorer/readme.md +19 -0
  75. package/src/explorer/urlMigrations/CO2UrlMigration.ts +46 -0
  76. package/src/explorer/urlMigrations/CovidUrlMigration.ts +37 -0
  77. package/src/explorer/urlMigrations/EnergyUrlMigration.ts +41 -0
  78. package/src/explorer/urlMigrations/ExplorerPageUrlMigrationSpec.ts +12 -0
  79. package/src/explorer/urlMigrations/ExplorerUrlMigrationUtils.ts +45 -0
  80. package/src/explorer/urlMigrations/ExplorerUrlMigrations.ts +33 -0
  81. package/src/explorer/urlMigrations/LegacyCovidUrlMigration.ts +144 -0
  82. package/src/explorer/urlMigrations/readme.md +39 -0
  83. package/src/grapher/axis/Axis.ts +973 -0
  84. package/src/grapher/axis/AxisConfig.ts +179 -0
  85. package/src/grapher/axis/AxisViews.tsx +597 -0
  86. package/src/grapher/barCharts/DiscreteBarChart.tsx +728 -0
  87. package/src/grapher/barCharts/DiscreteBarChartConstants.ts +60 -0
  88. package/src/grapher/barCharts/DiscreteBarChartHelpers.ts +338 -0
  89. package/src/grapher/barCharts/DiscreteBarChartState.ts +354 -0
  90. package/src/grapher/barCharts/DiscreteBarChartThumbnail.tsx +34 -0
  91. package/src/grapher/captionedChart/CaptionedChart.scss +61 -0
  92. package/src/grapher/captionedChart/CaptionedChart.tsx +523 -0
  93. package/src/grapher/captionedChart/Logos.tsx +141 -0
  94. package/src/grapher/captionedChart/LogosSVG.tsx +16 -0
  95. package/src/grapher/captionedChart/StaticChartRasterizer.tsx +178 -0
  96. package/src/grapher/captionedChart/assets/buildcanada-logo-square.svg +15 -0
  97. package/src/grapher/captionedChart/assets/buildcanada-logo.svg +15 -0
  98. package/src/grapher/captionedChart/assets/canadaspends.svg +7 -0
  99. package/src/grapher/captionedChart/readme.md +14 -0
  100. package/src/grapher/chart/Chart.tsx +62 -0
  101. package/src/grapher/chart/ChartAreaContent.tsx +172 -0
  102. package/src/grapher/chart/ChartDimension.ts +121 -0
  103. package/src/grapher/chart/ChartInterface.ts +83 -0
  104. package/src/grapher/chart/ChartManager.ts +113 -0
  105. package/src/grapher/chart/ChartTabs.ts +178 -0
  106. package/src/grapher/chart/ChartTypeMap.tsx +158 -0
  107. package/src/grapher/chart/ChartTypeSwitcher.tsx +26 -0
  108. package/src/grapher/chart/ChartUtils.tsx +364 -0
  109. package/src/grapher/chart/DimensionSlot.ts +45 -0
  110. package/src/grapher/chart/StaticChartWrapper.tsx +94 -0
  111. package/src/grapher/chart/guidedChartUtils.ts +82 -0
  112. package/src/grapher/color/BinningStrategies.ts +484 -0
  113. package/src/grapher/color/BinningStrategyEqualSizeBins.ts +132 -0
  114. package/src/grapher/color/BinningStrategyLogarithmic.ts +121 -0
  115. package/src/grapher/color/CategoricalColorAssigner.ts +97 -0
  116. package/src/grapher/color/ColorBrewerSchemes.ts +80 -0
  117. package/src/grapher/color/ColorConstants.ts +20 -0
  118. package/src/grapher/color/ColorScale.ts +339 -0
  119. package/src/grapher/color/ColorScaleBin.ts +147 -0
  120. package/src/grapher/color/ColorScaleConfig.ts +204 -0
  121. package/src/grapher/color/ColorScheme.ts +137 -0
  122. package/src/grapher/color/ColorSchemes.ts +149 -0
  123. package/src/grapher/color/ColorUtils.ts +86 -0
  124. package/src/grapher/color/CustomSchemes.ts +1772 -0
  125. package/src/grapher/color/readme.md +84 -0
  126. package/src/grapher/comparisonLine/ComparisonLine.tsx +31 -0
  127. package/src/grapher/comparisonLine/ComparisonLineConstants.ts +11 -0
  128. package/src/grapher/comparisonLine/ComparisonLineGenerator.ts +60 -0
  129. package/src/grapher/comparisonLine/ComparisonLineHelpers.ts +10 -0
  130. package/src/grapher/comparisonLine/CustomComparisonLine.tsx +159 -0
  131. package/src/grapher/comparisonLine/VerticalComparisonLine.tsx +208 -0
  132. package/src/grapher/controls/ActionButtons.scss +97 -0
  133. package/src/grapher/controls/ActionButtons.tsx +453 -0
  134. package/src/grapher/controls/CommandPalette.scss +50 -0
  135. package/src/grapher/controls/CommandPalette.tsx +74 -0
  136. package/src/grapher/controls/ContentSwitchers.scss +93 -0
  137. package/src/grapher/controls/ContentSwitchers.tsx +238 -0
  138. package/src/grapher/controls/Controls.scss +158 -0
  139. package/src/grapher/controls/DataTableFilterDropdown.scss +7 -0
  140. package/src/grapher/controls/DataTableFilterDropdown.tsx +168 -0
  141. package/src/grapher/controls/DataTableSearchField.scss +3 -0
  142. package/src/grapher/controls/DataTableSearchField.tsx +76 -0
  143. package/src/grapher/controls/Dropdown.scss +252 -0
  144. package/src/grapher/controls/Dropdown.tsx +235 -0
  145. package/src/grapher/controls/EntitySelectionToggle.tsx +135 -0
  146. package/src/grapher/controls/MapRegionDropdown.scss +3 -0
  147. package/src/grapher/controls/MapRegionDropdown.tsx +104 -0
  148. package/src/grapher/controls/MapResetButton.tsx +115 -0
  149. package/src/grapher/controls/MapZoomDropdown.scss +9 -0
  150. package/src/grapher/controls/MapZoomDropdown.tsx +270 -0
  151. package/src/grapher/controls/MapZoomToSelectionButton.tsx +87 -0
  152. package/src/grapher/controls/SearchField.scss +78 -0
  153. package/src/grapher/controls/SearchField.tsx +63 -0
  154. package/src/grapher/controls/SettingsMenu.scss +191 -0
  155. package/src/grapher/controls/SettingsMenu.tsx +399 -0
  156. package/src/grapher/controls/ShareMenu.scss +58 -0
  157. package/src/grapher/controls/ShareMenu.tsx +304 -0
  158. package/src/grapher/controls/SortIcon.tsx +39 -0
  159. package/src/grapher/controls/VerticalScrollContainer.tsx +263 -0
  160. package/src/grapher/controls/controlsRow/ControlsRow.tsx +168 -0
  161. package/src/grapher/controls/dropdown-icons.scss +4 -0
  162. package/src/grapher/controls/entityPicker/EntityPicker.scss +255 -0
  163. package/src/grapher/controls/entityPicker/EntityPicker.tsx +816 -0
  164. package/src/grapher/controls/entityPicker/EntityPickerConstants.ts +23 -0
  165. package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.scss +129 -0
  166. package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.tsx +463 -0
  167. package/src/grapher/controls/globalEntitySelector/GlobalEntitySelectorConstants.ts +3 -0
  168. package/src/grapher/controls/globalEntitySelector/readme.md +17 -0
  169. package/src/grapher/controls/settings/AbsRelToggle.tsx +64 -0
  170. package/src/grapher/controls/settings/AxisScaleToggle.tsx +53 -0
  171. package/src/grapher/controls/settings/FacetStrategySelector.tsx +110 -0
  172. package/src/grapher/controls/settings/FacetYDomainToggle.tsx +51 -0
  173. package/src/grapher/controls/settings/NoDataAreaToggle.tsx +38 -0
  174. package/src/grapher/controls/settings/ZoomToggle.tsx +36 -0
  175. package/src/grapher/core/EntitiesByRegionType.ts +174 -0
  176. package/src/grapher/core/EntityCodes.ts +19 -0
  177. package/src/grapher/core/EntityUrlBuilder.ts +200 -0
  178. package/src/grapher/core/FetchingGrapher.tsx +156 -0
  179. package/src/grapher/core/Grapher.tsx +760 -0
  180. package/src/grapher/core/GrapherAnalytics.ts +229 -0
  181. package/src/grapher/core/GrapherConstants.ts +173 -0
  182. package/src/grapher/core/GrapherState.tsx +3659 -0
  183. package/src/grapher/core/GrapherUrl.ts +184 -0
  184. package/src/grapher/core/GrapherUrlMigrations.ts +29 -0
  185. package/src/grapher/core/GrapherUseHelpers.tsx +147 -0
  186. package/src/grapher/core/LegacyToOwidTable.ts +841 -0
  187. package/src/grapher/core/grapher.entry.ts +5 -0
  188. package/src/grapher/core/grapher.scss +257 -0
  189. package/src/grapher/core/loadGrapherTableHelpers.ts +116 -0
  190. package/src/grapher/core/loadVariable.ts +104 -0
  191. package/src/grapher/core/relatedQuestion.ts +12 -0
  192. package/src/grapher/core/typography.scss +206 -0
  193. package/src/grapher/dataTable/DataTable.sample.ts +206 -0
  194. package/src/grapher/dataTable/DataTable.scss +249 -0
  195. package/src/grapher/dataTable/DataTable.tsx +1332 -0
  196. package/src/grapher/dataTable/DataTableConstants.ts +186 -0
  197. package/src/grapher/entitySelector/EntitySelector.scss +255 -0
  198. package/src/grapher/entitySelector/EntitySelector.tsx +1838 -0
  199. package/src/grapher/facet/FacetChart.tsx +943 -0
  200. package/src/grapher/facet/FacetChartConstants.ts +24 -0
  201. package/src/grapher/facet/FacetChartUtils.ts +51 -0
  202. package/src/grapher/facet/FacetMap.tsx +604 -0
  203. package/src/grapher/facet/FacetMapConstants.ts +23 -0
  204. package/src/grapher/facet/readme.md +13 -0
  205. package/src/grapher/focus/FocusArray.ts +79 -0
  206. package/src/grapher/footer/Footer.scss +63 -0
  207. package/src/grapher/footer/Footer.tsx +809 -0
  208. package/src/grapher/footer/FooterManager.ts +44 -0
  209. package/src/grapher/fullScreen/FullScreen.scss +11 -0
  210. package/src/grapher/fullScreen/FullScreen.tsx +61 -0
  211. package/src/grapher/header/Header.scss +35 -0
  212. package/src/grapher/header/Header.tsx +372 -0
  213. package/src/grapher/header/HeaderManager.ts +28 -0
  214. package/src/grapher/index.ts +157 -0
  215. package/src/grapher/interaction/InteractionState.ts +60 -0
  216. package/src/grapher/legend/HorizontalColorLegends.tsx +923 -0
  217. package/src/grapher/legend/LegendInteractionState.ts +40 -0
  218. package/src/grapher/legend/VerticalColorLegend.tsx +295 -0
  219. package/src/grapher/lineCharts/LineChart.tsx +968 -0
  220. package/src/grapher/lineCharts/LineChartConstants.ts +89 -0
  221. package/src/grapher/lineCharts/LineChartHelpers.ts +184 -0
  222. package/src/grapher/lineCharts/LineChartState.ts +394 -0
  223. package/src/grapher/lineCharts/LineChartThumbnail.tsx +437 -0
  224. package/src/grapher/lineCharts/Lines.tsx +258 -0
  225. package/src/grapher/lineLegend/LineLegend.tsx +723 -0
  226. package/src/grapher/lineLegend/LineLegendConstants.ts +9 -0
  227. package/src/grapher/lineLegend/LineLegendFilterAlgorithms.ts +143 -0
  228. package/src/grapher/lineLegend/LineLegendHelpers.ts +253 -0
  229. package/src/grapher/lineLegend/LineLegendTypes.ts +32 -0
  230. package/src/grapher/mapCharts/CanadaTopology.ts +17922 -0
  231. package/src/grapher/mapCharts/ChoroplethGlobe.tsx +949 -0
  232. package/src/grapher/mapCharts/ChoroplethMap.tsx +662 -0
  233. package/src/grapher/mapCharts/GeoFeatures.ts +184 -0
  234. package/src/grapher/mapCharts/GlobeController.ts +496 -0
  235. package/src/grapher/mapCharts/MapAnnotationPlacements.json +1040 -0
  236. package/src/grapher/mapCharts/MapAnnotationPlacements.ts +31 -0
  237. package/src/grapher/mapCharts/MapAnnotations.ts +723 -0
  238. package/src/grapher/mapCharts/MapChart.sample.ts +59 -0
  239. package/src/grapher/mapCharts/MapChart.scss +5 -0
  240. package/src/grapher/mapCharts/MapChart.tsx +720 -0
  241. package/src/grapher/mapCharts/MapChartConstants.ts +260 -0
  242. package/src/grapher/mapCharts/MapChartState.ts +416 -0
  243. package/src/grapher/mapCharts/MapChartThumbnail.tsx +25 -0
  244. package/src/grapher/mapCharts/MapComponents.tsx +338 -0
  245. package/src/grapher/mapCharts/MapConfig.ts +156 -0
  246. package/src/grapher/mapCharts/MapHelpers.ts +181 -0
  247. package/src/grapher/mapCharts/MapProjections.ts +49 -0
  248. package/src/grapher/mapCharts/MapSparkline.tsx +257 -0
  249. package/src/grapher/mapCharts/MapTooltip.scss +49 -0
  250. package/src/grapher/mapCharts/MapTooltip.tsx +409 -0
  251. package/src/grapher/mapCharts/MapTopology.ts +1766 -0
  252. package/src/grapher/mapCharts/d3-bboxCollide.js +204 -0
  253. package/src/grapher/mapCharts/d3-geo-projection.ts +198 -0
  254. package/src/grapher/modal/DownloadIcons.tsx +39 -0
  255. package/src/grapher/modal/DownloadModal.scss +300 -0
  256. package/src/grapher/modal/DownloadModal.tsx +1226 -0
  257. package/src/grapher/modal/EmbedModal.scss +40 -0
  258. package/src/grapher/modal/EmbedModal.tsx +160 -0
  259. package/src/grapher/modal/EntitySelectorModal.tsx +59 -0
  260. package/src/grapher/modal/Modal.scss +31 -0
  261. package/src/grapher/modal/Modal.tsx +90 -0
  262. package/src/grapher/modal/ModalHeader.scss +12 -0
  263. package/src/grapher/modal/ModalHeader.tsx +16 -0
  264. package/src/grapher/modal/SourcesDescriptions.scss +87 -0
  265. package/src/grapher/modal/SourcesDescriptions.tsx +89 -0
  266. package/src/grapher/modal/SourcesKeyDataTable.scss +49 -0
  267. package/src/grapher/modal/SourcesKeyDataTable.tsx +87 -0
  268. package/src/grapher/modal/SourcesModal.scss +301 -0
  269. package/src/grapher/modal/SourcesModal.tsx +568 -0
  270. package/src/grapher/noDataModal/NoDataModal.tsx +125 -0
  271. package/src/grapher/scatterCharts/ConnectedScatterLegend.tsx +143 -0
  272. package/src/grapher/scatterCharts/MultiColorPolyline.tsx +129 -0
  273. package/src/grapher/scatterCharts/NoDataSection.scss +14 -0
  274. package/src/grapher/scatterCharts/NoDataSection.tsx +56 -0
  275. package/src/grapher/scatterCharts/ScatterPlotChart.tsx +792 -0
  276. package/src/grapher/scatterCharts/ScatterPlotChartConstants.ts +157 -0
  277. package/src/grapher/scatterCharts/ScatterPlotChartState.ts +678 -0
  278. package/src/grapher/scatterCharts/ScatterPlotChartThumbnail.tsx +155 -0
  279. package/src/grapher/scatterCharts/ScatterPlotTooltip.tsx +560 -0
  280. package/src/grapher/scatterCharts/ScatterPoints.tsx +153 -0
  281. package/src/grapher/scatterCharts/ScatterPointsWithLabels.tsx +708 -0
  282. package/src/grapher/scatterCharts/ScatterSizeLegend.tsx +327 -0
  283. package/src/grapher/scatterCharts/ScatterUtils.ts +265 -0
  284. package/src/grapher/scatterCharts/Triangle.tsx +41 -0
  285. package/src/grapher/schema/README.md +33 -0
  286. package/src/grapher/schema/defaultGrapherConfig.ts +100 -0
  287. package/src/grapher/schema/grapher-schema.009.yaml +781 -0
  288. package/src/grapher/schema/migrations/helpers.ts +58 -0
  289. package/src/grapher/schema/migrations/migrate.ts +75 -0
  290. package/src/grapher/schema/migrations/migrations.ts +158 -0
  291. package/src/grapher/selection/MapSelectionArray.ts +99 -0
  292. package/src/grapher/selection/SelectionArray.ts +71 -0
  293. package/src/grapher/selection/readme.md +16 -0
  294. package/src/grapher/sidePanel/SidePanel.scss +10 -0
  295. package/src/grapher/sidePanel/SidePanel.tsx +23 -0
  296. package/src/grapher/slideInDrawer/SlideInDrawer.scss +57 -0
  297. package/src/grapher/slideInDrawer/SlideInDrawer.tsx +125 -0
  298. package/src/grapher/slideshowController/SlideShowController.tsx +43 -0
  299. package/src/grapher/slideshowController/readme.md +7 -0
  300. package/src/grapher/slopeCharts/MarkX.tsx +45 -0
  301. package/src/grapher/slopeCharts/Slope.tsx +102 -0
  302. package/src/grapher/slopeCharts/SlopeChart.tsx +1152 -0
  303. package/src/grapher/slopeCharts/SlopeChartConstants.ts +33 -0
  304. package/src/grapher/slopeCharts/SlopeChartHelpers.ts +73 -0
  305. package/src/grapher/slopeCharts/SlopeChartState.ts +392 -0
  306. package/src/grapher/slopeCharts/SlopeChartThumbnail.tsx +368 -0
  307. package/src/grapher/stackedCharts/AbstractStackedChartState.ts +370 -0
  308. package/src/grapher/stackedCharts/MarimekkoBars.tsx +190 -0
  309. package/src/grapher/stackedCharts/MarimekkoBarsForOneEntity.tsx +168 -0
  310. package/src/grapher/stackedCharts/MarimekkoChart.tsx +1144 -0
  311. package/src/grapher/stackedCharts/MarimekkoChartConstants.ts +112 -0
  312. package/src/grapher/stackedCharts/MarimekkoChartHelpers.ts +21 -0
  313. package/src/grapher/stackedCharts/MarimekkoChartState.ts +465 -0
  314. package/src/grapher/stackedCharts/MarimekkoChartThumbnail.tsx +168 -0
  315. package/src/grapher/stackedCharts/MarimekkoInternalLabels.tsx +124 -0
  316. package/src/grapher/stackedCharts/StackedAreaChart.tsx +678 -0
  317. package/src/grapher/stackedCharts/StackedAreaChartState.ts +34 -0
  318. package/src/grapher/stackedCharts/StackedAreaChartThumbnail.tsx +215 -0
  319. package/src/grapher/stackedCharts/StackedAreas.tsx +223 -0
  320. package/src/grapher/stackedCharts/StackedBarChart.tsx +619 -0
  321. package/src/grapher/stackedCharts/StackedBarChartState.ts +80 -0
  322. package/src/grapher/stackedCharts/StackedBarChartThumbnail.tsx +220 -0
  323. package/src/grapher/stackedCharts/StackedBarSegment.tsx +87 -0
  324. package/src/grapher/stackedCharts/StackedBars.tsx +102 -0
  325. package/src/grapher/stackedCharts/StackedConstants.ts +109 -0
  326. package/src/grapher/stackedCharts/StackedDiscreteBarChart.tsx +270 -0
  327. package/src/grapher/stackedCharts/StackedDiscreteBarChartState.ts +296 -0
  328. package/src/grapher/stackedCharts/StackedDiscreteBarChartThumbnail.tsx +27 -0
  329. package/src/grapher/stackedCharts/StackedDiscreteBars.tsx +648 -0
  330. package/src/grapher/stackedCharts/StackedUtils.ts +142 -0
  331. package/src/grapher/tabs/Tabs.scss +169 -0
  332. package/src/grapher/tabs/Tabs.tsx +54 -0
  333. package/src/grapher/tabs/TabsWithDropdown.scss +62 -0
  334. package/src/grapher/tabs/TabsWithDropdown.tsx +114 -0
  335. package/src/grapher/testData/OwidTestData.sample.ts +273 -0
  336. package/src/grapher/testData/OwidTestData.ts +64 -0
  337. package/src/grapher/timeline/TimelineComponent.scss +139 -0
  338. package/src/grapher/timeline/TimelineComponent.tsx +658 -0
  339. package/src/grapher/timeline/TimelineController.ts +368 -0
  340. package/src/grapher/timeline/readme.md +7 -0
  341. package/src/grapher/tooltip/Tooltip.scss +510 -0
  342. package/src/grapher/tooltip/Tooltip.tsx +294 -0
  343. package/src/grapher/tooltip/TooltipContents.tsx +383 -0
  344. package/src/grapher/tooltip/TooltipProps.ts +123 -0
  345. package/src/grapher/tooltip/TooltipState.ts +81 -0
  346. package/src/grapher/verticalLabels/VerticalLabels.tsx +31 -0
  347. package/src/grapher/verticalLabels/VerticalLabelsState.ts +154 -0
  348. package/src/index.ts +226 -0
  349. package/src/styles/charts.scss +15 -0
  350. package/src/types/NominalType.ts +30 -0
  351. package/src/types/OwidOrigin.ts +18 -0
  352. package/src/types/OwidSource.ts +9 -0
  353. package/src/types/OwidVariable.ts +133 -0
  354. package/src/types/OwidVariableDisplayConfigInterface.ts +49 -0
  355. package/src/types/analyticsTypes.ts +54 -0
  356. package/src/types/dbTypes/Tags.ts +11 -0
  357. package/src/types/domainTypes/Archive.ts +139 -0
  358. package/src/types/domainTypes/Author.ts +28 -0
  359. package/src/types/domainTypes/ContentGraph.ts +76 -0
  360. package/src/types/domainTypes/CoreTableTypes.ts +305 -0
  361. package/src/types/domainTypes/DeployStatus.ts +23 -0
  362. package/src/types/domainTypes/Layout.ts +34 -0
  363. package/src/types/domainTypes/Posts.ts +34 -0
  364. package/src/types/domainTypes/Search.ts +299 -0
  365. package/src/types/domainTypes/Site.ts +8 -0
  366. package/src/types/domainTypes/StaticViz.ts +64 -0
  367. package/src/types/domainTypes/Toc.ts +11 -0
  368. package/src/types/domainTypes/Tombstone.ts +19 -0
  369. package/src/types/domainTypes/Various.ts +79 -0
  370. package/src/types/gdocTypes/Gdoc.ts +280 -0
  371. package/src/types/grapherTypes/BinningStrategyTypes.ts +46 -0
  372. package/src/types/grapherTypes/GrapherConstants.ts +53 -0
  373. package/src/types/grapherTypes/GrapherTypes.ts +743 -0
  374. package/src/types/index.ts +316 -0
  375. package/src/types/wordpressTypes/WordpressTypes.ts +9 -0
  376. package/src/utils/Bounds.ts +439 -0
  377. package/src/utils/BrowserUtils.ts +12 -0
  378. package/src/utils/FuzzySearch.ts +74 -0
  379. package/src/utils/MultiDimDataPageConfig.ts +31 -0
  380. package/src/utils/OwidVariable.ts +82 -0
  381. package/src/utils/PointVector.ts +97 -0
  382. package/src/utils/PromiseCache.ts +36 -0
  383. package/src/utils/PromiseSwitcher.ts +52 -0
  384. package/src/utils/TimeBounds.ts +130 -0
  385. package/src/utils/Tippy.tsx +57 -0
  386. package/src/utils/Util.ts +2369 -0
  387. package/src/utils/archival/archivalDate.ts +48 -0
  388. package/src/utils/dayjs.ts +32 -0
  389. package/src/utils/formatValue.ts +242 -0
  390. package/src/utils/grapherConfigUtils.ts +81 -0
  391. package/src/utils/image.ts +225 -0
  392. package/src/utils/index.ts +318 -0
  393. package/src/utils/isPresent.ts +5 -0
  394. package/src/utils/metadataHelpers.ts +329 -0
  395. package/src/utils/persistable/Persistable.ts +82 -0
  396. package/src/utils/persistable/readme.md +50 -0
  397. package/src/utils/regions.json +5635 -0
  398. package/src/utils/regions.ts +463 -0
  399. package/src/utils/serializers.ts +16 -0
  400. package/src/utils/string.ts +42 -0
  401. package/src/utils/urls/Url.ts +195 -0
  402. package/src/utils/urls/UrlMigration.ts +10 -0
  403. package/src/utils/urls/UrlUtils.ts +54 -0
  404. package/src/utils/urls/readme.md +90 -0
@@ -0,0 +1,53 @@
1
+ import * as React from "react"
2
+ import { action, makeObservable } from "mobx"
3
+ import { observer } from "mobx-react"
4
+ import { ScaleType } from "../../../types/index.js"
5
+ import { AxisConfig } from "../../axis/AxisConfig"
6
+ import classnames from "classnames"
7
+
8
+ interface AxisScaleToggleProps {
9
+ axis: AxisConfig
10
+ subtitle?: string
11
+ prefix?: string
12
+ }
13
+
14
+ @observer
15
+ export class AxisScaleToggle extends React.Component<AxisScaleToggleProps> {
16
+ constructor(props: AxisScaleToggleProps) {
17
+ super(props)
18
+ makeObservable(this)
19
+ }
20
+
21
+ @action.bound private setAxisScale(scale: ScaleType): void {
22
+ this.props.axis.scaleType = scale
23
+ }
24
+
25
+ override render(): React.ReactElement {
26
+ const { linear, log } = ScaleType,
27
+ { axis, prefix, subtitle } = this.props,
28
+ isLinear = axis.scaleType === linear,
29
+ label = prefix ? `${prefix}: ` : undefined
30
+
31
+ return (
32
+ <>
33
+ <div className="config-toggle">
34
+ {subtitle && <label>{subtitle}</label>}
35
+ <button
36
+ className={classnames({ active: isLinear })}
37
+ onClick={(): void => this.setAxisScale(linear)}
38
+ data-track-note="chart_toggle_scale"
39
+ >
40
+ {label}Linear
41
+ </button>
42
+ <button
43
+ className={classnames({ active: !isLinear })}
44
+ onClick={(): void => this.setAxisScale(log)}
45
+ data-track-note="chart_toggle_scale"
46
+ >
47
+ {label}Logarithmic
48
+ </button>
49
+ </div>
50
+ </>
51
+ )
52
+ }
53
+ }
@@ -0,0 +1,110 @@
1
+ import * as _ from "lodash-es"
2
+ import * as React from "react"
3
+ import { computed, makeObservable } from "mobx"
4
+ import { observer } from "mobx-react"
5
+ import { DEFAULT_GRAPHER_ENTITY_TYPE } from "../../core/GrapherConstants"
6
+ import { FacetStrategy } from "../../../utils/index.js"
7
+ import classnames from "classnames"
8
+
9
+ export interface FacetStrategySelectionManager {
10
+ availableFacetStrategies: FacetStrategy[]
11
+ facetStrategy?: FacetStrategy
12
+ entityType?: string
13
+ facettingLabelByYVariables?: string
14
+ }
15
+
16
+ @observer
17
+ export class FacetStrategySelector extends React.Component<{
18
+ manager: FacetStrategySelectionManager
19
+ }> {
20
+ constructor(props: { manager: FacetStrategySelectionManager }) {
21
+ super(props)
22
+ makeObservable(this)
23
+ }
24
+
25
+ @computed get facetStrategyLabels(): { [key in FacetStrategy]: string } {
26
+ return {
27
+ [FacetStrategy.none]: "All together",
28
+ [FacetStrategy.entity]: `Split by ${this.entityName}`,
29
+ [FacetStrategy.metric]: `Split by ${this.metricName}`,
30
+ }
31
+ }
32
+
33
+ @computed get entityName(): string {
34
+ return this.props.manager.entityType ?? DEFAULT_GRAPHER_ENTITY_TYPE
35
+ }
36
+
37
+ @computed get metricName(): string {
38
+ return this.props.manager.facettingLabelByYVariables ?? "metric"
39
+ }
40
+
41
+ @computed get strategies(): FacetStrategy[] {
42
+ return (
43
+ this.props.manager.availableFacetStrategies || [
44
+ FacetStrategy.none,
45
+ FacetStrategy.entity,
46
+ FacetStrategy.metric,
47
+ ]
48
+ )
49
+ }
50
+
51
+ @computed get subtitle(): string {
52
+ const entityChoice = this.entityName.replace(/ or /, "/"),
53
+ byEntity = this.strategies.includes(FacetStrategy.entity),
54
+ byMetric = this.strategies.includes(FacetStrategy.metric)
55
+
56
+ if (byEntity || byMetric) {
57
+ const facet =
58
+ byEntity && byMetric
59
+ ? `${this.metricName} or ${entityChoice}`
60
+ : byEntity
61
+ ? this.entityName
62
+ : this.metricName
63
+ return (
64
+ "Visualize the data all together in one chart or split it by " +
65
+ facet
66
+ )
67
+ } else {
68
+ return ""
69
+ }
70
+ }
71
+
72
+ override render(): React.ReactElement {
73
+ return (
74
+ <>
75
+ <div className="config-subtitle">{this.subtitle}</div>
76
+ <div className="config-list">
77
+ {this.strategies.map((value: FacetStrategy) => {
78
+ const label = this.facetStrategyLabels[value],
79
+ active = value === this.facetStrategy,
80
+ option = value.toString()
81
+
82
+ return (
83
+ <button
84
+ key={option}
85
+ className={classnames(option, { active })}
86
+ onClick={(): void => {
87
+ this.props.manager.facetStrategy = value
88
+ }}
89
+ data-track-note={`chart_facet_${option}`}
90
+ >
91
+ <div className="faceting-icon">
92
+ {_.range(value === "none" ? 1 : 6).map(
93
+ (i) => (
94
+ <span key={i}></span>
95
+ )
96
+ )}
97
+ </div>
98
+ {label}
99
+ </button>
100
+ )
101
+ })}
102
+ </div>
103
+ </>
104
+ )
105
+ }
106
+
107
+ @computed get facetStrategy(): FacetStrategy {
108
+ return this.props.manager.facetStrategy || FacetStrategy.none
109
+ }
110
+ }
@@ -0,0 +1,51 @@
1
+ import * as React from "react"
2
+ import { computed, action, makeObservable } from "mobx"
3
+ import { observer } from "mobx-react"
4
+ import { FacetAxisDomain, FacetStrategy } from "../../../types/index.js"
5
+ import { AxisConfig } from "../../axis/AxisConfig"
6
+ import { LabeledSwitch } from "../../../components/index.js"
7
+
8
+ export interface FacetYDomainToggleManager {
9
+ facetStrategy?: FacetStrategy
10
+ yAxis?: AxisConfig
11
+ }
12
+
13
+ @observer
14
+ export class FacetYDomainToggle extends React.Component<{
15
+ manager: FacetYDomainToggleManager
16
+ }> {
17
+ constructor(props: { manager: FacetYDomainToggleManager }) {
18
+ super(props)
19
+ makeObservable(this)
20
+ }
21
+
22
+ @action.bound onToggle(): void {
23
+ const { yAxis } = this.props.manager
24
+ if (yAxis) {
25
+ yAxis.facetDomain = this.isYDomainShared
26
+ ? FacetAxisDomain.independent
27
+ : FacetAxisDomain.shared
28
+ }
29
+ }
30
+
31
+ @computed get isYDomainShared(): boolean {
32
+ const { yAxis } = this.props.manager
33
+ const facetDomain = yAxis?.facetDomain || FacetAxisDomain.shared
34
+ return facetDomain === FacetAxisDomain.shared
35
+ }
36
+
37
+ override render(): React.ReactElement | null {
38
+ const { yAxis, facetStrategy } = this.props.manager
39
+ if (!yAxis || facetStrategy === "none") return null
40
+
41
+ return (
42
+ <LabeledSwitch
43
+ label="Align axis scales"
44
+ tooltip="Use the same minimum and maximum values on all charts or scale axes to fit the data in each chart"
45
+ value={this.isYDomainShared}
46
+ onToggle={this.onToggle}
47
+ tracking="chart_facet_ydomain_toggle"
48
+ />
49
+ )
50
+ }
51
+ }
@@ -0,0 +1,38 @@
1
+ import * as React from "react"
2
+ import { computed, action, makeObservable } from "mobx"
3
+ import { observer } from "mobx-react"
4
+ import { LabeledSwitch } from "../../../components/index.js"
5
+
6
+ export interface NoDataAreaToggleManager {
7
+ showNoDataArea?: boolean
8
+ }
9
+
10
+ @observer
11
+ export class NoDataAreaToggle extends React.Component<{
12
+ manager: NoDataAreaToggleManager
13
+ }> {
14
+ constructor(props: { manager: NoDataAreaToggleManager }) {
15
+ super(props)
16
+ makeObservable(this)
17
+ }
18
+
19
+ @action.bound onToggle(): void {
20
+ this.manager.showNoDataArea = !this.manager.showNoDataArea
21
+ }
22
+
23
+ @computed get manager(): NoDataAreaToggleManager {
24
+ return this.props.manager
25
+ }
26
+
27
+ override render(): React.ReactElement {
28
+ return (
29
+ <LabeledSwitch
30
+ label={"Show \u2018no data\u2019 area"}
31
+ value={this.manager.showNoDataArea}
32
+ tooltip="Include entities for which ‘no data’ is available in the chart."
33
+ onToggle={this.onToggle}
34
+ tracking="chart_no_data_area_toggle"
35
+ />
36
+ )
37
+ }
38
+ }
@@ -0,0 +1,36 @@
1
+ import * as React from "react"
2
+ import { action, makeObservable } from "mobx"
3
+ import { observer } from "mobx-react"
4
+ import { LabeledSwitch } from "../../../components/index.js"
5
+
6
+ export interface ZoomToggleManager {
7
+ zoomToSelection?: boolean
8
+ }
9
+
10
+ @observer
11
+ export class ZoomToggle extends React.Component<{
12
+ manager: ZoomToggleManager
13
+ }> {
14
+ constructor(props: { manager: ZoomToggleManager }) {
15
+ super(props)
16
+ makeObservable(this)
17
+ }
18
+
19
+ @action.bound onToggle(): void {
20
+ this.props.manager.zoomToSelection = this.props.manager.zoomToSelection
21
+ ? undefined
22
+ : true
23
+ }
24
+
25
+ override render(): React.ReactElement {
26
+ return (
27
+ <LabeledSwitch
28
+ label="Zoom to selection"
29
+ tooltip="Scale axes to focus on the currently highlighted data points."
30
+ value={this.props.manager.zoomToSelection}
31
+ onToggle={this.onToggle}
32
+ tracking="chart_zoom_to_selection"
33
+ />
34
+ )
35
+ }
36
+ }
@@ -0,0 +1,174 @@
1
+ import * as _ from "lodash-es"
2
+ import { EntityName } from "../../types/index.js"
3
+ import {
4
+ AggregateSource,
5
+ aggregateSources,
6
+ Country,
7
+ excludeUndefined,
8
+ getRegionByName,
9
+ } from "../../utils/index.js"
10
+ import { CUSTOM_REGION_SOURCE_IDS, isWorldEntityName } from "./GrapherConstants"
11
+ import * as R from "remeda"
12
+
13
+ const customAggregateSources = CUSTOM_REGION_SOURCE_IDS
14
+ type CustomAggregateSource = (typeof customAggregateSources)[number]
15
+
16
+ const entityRegionTypes = [
17
+ "countries",
18
+ "continents", // owid continents
19
+ "incomeGroups",
20
+ "historicalCountries", // e.g. USSR, Austria-Hungary
21
+ ...aggregateSources,
22
+ ...customAggregateSources,
23
+ ] as const
24
+ export type EntityRegionType = (typeof entityRegionTypes)[number]
25
+
26
+ export interface EntityRegionTypeGroup {
27
+ regionType: EntityRegionType
28
+ entityNames: EntityName[]
29
+ }
30
+
31
+ export type EntityNamesByRegionType = Map<EntityRegionType, EntityName[]>
32
+
33
+ export const entityRegionTypeLabels: Record<EntityRegionType, string> = {
34
+ countries: "Countries",
35
+ continents: "Continents", // OWID-defined continents
36
+ incomeGroups: "Income groups",
37
+ historicalCountries: "Historical countries and regions", // e.g. USSR, Austria-Hungary
38
+
39
+ // Regions defined by an institution, and where we have region definition about what constitutes these regions in regions.json
40
+ who: "World Health Organization regions",
41
+ wb: "World Bank regions",
42
+ un: "United Nations regions",
43
+ unsdg: "UN Sustainable Development Goals regions",
44
+ unm49: "United Nations M49 regions",
45
+ pew: "Pew Research Center regions",
46
+
47
+ // Regions defined by an institution, but we don't have region definitions in regions.json for these (we recognize them by their suffix)
48
+ unsd: "UN Statistics Division regions",
49
+ fao: "FAO regions", // UN's Food and Agriculture Organization
50
+ ei: "Education International regions",
51
+ pip: "PIP regions", // World Bank’s Poverty and Inequality Platform
52
+ ember: "Ember regions",
53
+ gcp: "Global Carbon Project regions",
54
+ niaid: "NIAID regions", // National Institute of Allergy and Infectious Diseases
55
+ unicef: "UNICEF regions",
56
+ unaids: "UNAIDS regions", // Joint United Nations Programme on HIV and AIDS
57
+ undp: "UN Development Programme regions",
58
+ wid: "World Inequality Database regions",
59
+ oecd: "OECD regions", // Organisation for Economic Co-operation and Development
60
+ }
61
+
62
+ export function groupEntityNamesByRegionType(
63
+ entityNames: EntityName[]
64
+ ): EntityRegionTypeGroup[] {
65
+ // the 'World' entity shouldn't show up in any of the groups
66
+ const availableEntityNames = entityNames.filter(
67
+ (entityName) => !isWorldEntityName(entityName)
68
+ )
69
+
70
+ // map entities to their regions
71
+ const availableRegions = excludeUndefined(
72
+ availableEntityNames.map((entityName) => getRegionByName(entityName))
73
+ )
74
+
75
+ // group regions by type
76
+ const regionsGroupedByType = _.groupBy(
77
+ availableRegions,
78
+ (r) => r.regionType
79
+ )
80
+
81
+ const entitiesByType: EntityRegionTypeGroup[] = []
82
+
83
+ // split countries into historical and non-historical
84
+ const [historicalCountries, nonHistoricalCountries] = R.partition(
85
+ regionsGroupedByType.country ?? [],
86
+ (country) => !!(country as Country).isHistorical
87
+ )
88
+
89
+ // add the 'countries' group
90
+ if (nonHistoricalCountries.length > 0) {
91
+ entitiesByType.push({
92
+ regionType: "countries",
93
+ entityNames: nonHistoricalCountries.map((region) => region.name),
94
+ })
95
+ }
96
+
97
+ // add the 'continents' group
98
+ if (regionsGroupedByType.continent) {
99
+ entitiesByType.push({
100
+ regionType: "continents",
101
+ entityNames: regionsGroupedByType.continent.map(
102
+ (region) => region.name
103
+ ),
104
+ })
105
+ }
106
+
107
+ // add the 'incomeGroups' group
108
+ if (regionsGroupedByType.income_group) {
109
+ // match by name instead of relying on the regions file because
110
+ // some charts have income groups that aren't listed in the regions
111
+ // file, e.g. 'Lower-middle-income countries'
112
+ const incomeGroups = availableEntityNames.filter(
113
+ (entityName) =>
114
+ entityName.includes("income countries") ||
115
+ // matches 'No income group available', for example
116
+ entityName.includes("income group")
117
+ )
118
+
119
+ entitiesByType.push({
120
+ regionType: "incomeGroups",
121
+ entityNames: incomeGroups,
122
+ })
123
+ }
124
+
125
+ const entitiesBySource = new Map<
126
+ AggregateSource | CustomAggregateSource,
127
+ EntityName[]
128
+ >()
129
+ for (const entityName of availableEntityNames) {
130
+ // The regions file includes a definedBy field for aggregates,
131
+ // which could be used here. However, non-OWID regions aren't
132
+ // standardized, meaning we might miss some entities.
133
+ // Instead, we rely on the convention that non-OWID regions
134
+ // are suffixed with (source) and check the entity name.
135
+ const match = entityName.match(/\(([^)]+)\)$/)
136
+ const sourceCandidate = match?.[1].toLowerCase().replaceAll(" ", "")
137
+ if (sourceCandidate && isAggregateSource(sourceCandidate)) {
138
+ if (!entitiesBySource.get(sourceCandidate))
139
+ entitiesBySource.set(sourceCandidate, [])
140
+ entitiesBySource.get(sourceCandidate)!.push(entityName)
141
+ }
142
+ }
143
+
144
+ for (const [source, entityNames] of entitiesBySource) {
145
+ entitiesByType.push({ regionType: source, entityNames })
146
+ }
147
+
148
+ // add a group for historical countries
149
+ if (historicalCountries.length > 0) {
150
+ entitiesByType.push({
151
+ regionType: "historicalCountries",
152
+ entityNames: historicalCountries.map((region) => region.name),
153
+ })
154
+ }
155
+
156
+ return entitiesByType
157
+ }
158
+
159
+ const aggregateSourceSet = new Set([
160
+ ...aggregateSources,
161
+ ...customAggregateSources,
162
+ ])
163
+
164
+ export function isAggregateSource(
165
+ candidate: string
166
+ ): candidate is AggregateSource | CustomAggregateSource {
167
+ return aggregateSourceSet.has(candidate as any)
168
+ }
169
+
170
+ export function isEntityRegionType(
171
+ candidate: string
172
+ ): candidate is EntityRegionType {
173
+ return entityRegionTypes.includes(candidate as any)
174
+ }
@@ -0,0 +1,19 @@
1
+ import { lazy, regions } from "../../utils/index.js"
2
+ import { EntityName } from "../../types/index.js"
3
+ import * as R from "remeda"
4
+
5
+ const getEntityCodesToEntityNames: () => Record<string, string> = lazy(() =>
6
+ Object.fromEntries(regions.map(({ code, name }) => [code, name]))
7
+ )
8
+
9
+ const getEntityNamesToEntityCodes = lazy(() =>
10
+ R.invert(getEntityCodesToEntityNames())
11
+ )
12
+
13
+ export const codeToEntityName = (codeOrEntityName: string): EntityName => {
14
+ return getEntityCodesToEntityNames()[codeOrEntityName] ?? codeOrEntityName
15
+ }
16
+
17
+ export const entityNameToCode = (entityName: EntityName): string => {
18
+ return getEntityNamesToEntityCodes()[entityName] ?? entityName
19
+ }
@@ -0,0 +1,200 @@
1
+ import { EntityName, SeriesName } from "../../types/index.js"
2
+ import { Url, performUrlMigrations, UrlMigration } from "../../utils/index.js"
3
+ import { codeToEntityName, entityNameToCode } from "./EntityCodes"
4
+
5
+ /*
6
+ * Migration #1: Switch from + to ~ delimited entities.
7
+ *
8
+ * Implemented: May 2020
9
+ *
10
+ * See PR discussion on how we decided on ~ (tilde): https://github.com/owid/owid-grapher/pull/446
11
+ * And the initial issue (Facebook rewriting our URLs): https://github.com/owid/owid-grapher/issues/397
12
+ *
13
+ * In short:
14
+ *
15
+ * Facebook replaces `%20` in URLs with `+`. Before this migration we encoded
16
+ * ["North America", "South America"] → "North%20America+South%20America".
17
+ * Facebook would turn this into "North+America+South+America" making the delimiters and spaces
18
+ * ambiguous.
19
+ *
20
+ * We chose ~ (tilde) because no entities existed in the database that contain that symbol, so the
21
+ * existence of that symbol could be used to detect legacy URLs.
22
+ *
23
+ */
24
+
25
+ // Todo: ensure EntityName never contains the v2Delimiter
26
+
27
+ const V1_DELIMITER = "+"
28
+ export const ENTITY_V2_DELIMITER = "~"
29
+
30
+ const isV1Param = (encodedQueryParam: string): boolean => {
31
+ // No legacy entities have a v2Delimiter in their name,
32
+ // so if a v2Delimiter is present we know it's a v2 link.
33
+ return !decodeURIComponent(encodedQueryParam).includes(ENTITY_V2_DELIMITER)
34
+ }
35
+
36
+ const entityNamesFromV1EncodedParam = (
37
+ encodedQueryParam: string
38
+ ): EntityName[] => {
39
+ // need to use still-encoded URL params because we need to
40
+ // distinguish between `+` and `%20` in legacy URLs
41
+ return encodedQueryParam.split(V1_DELIMITER).map(decodeURIComponent)
42
+ }
43
+
44
+ const entityNamesToV2Param = (entityNames: EntityName[]): string => {
45
+ // Always include a v2Delimiter in a v2 link. When decoding we will drop any empty strings.
46
+ if (entityNames.length === 1) return ENTITY_V2_DELIMITER + entityNames[0]
47
+ return entityNames.join(ENTITY_V2_DELIMITER)
48
+ }
49
+
50
+ const entityNamesFromV2Param = (queryParam: string): EntityName[] => {
51
+ // Facebook turns %20 into +. v2 links will never contain a +, so we can safely replace all of them with %20.
52
+ return queryParam.split(ENTITY_V2_DELIMITER).filter((item) => item)
53
+ }
54
+
55
+ const migrateV1Delimited: UrlMigration = (url) => {
56
+ const { country } = url.encodedQueryParams
57
+
58
+ if (country !== undefined && isV1Param(country)) {
59
+ return url.updateQueryParams({
60
+ country: entityNamesToV2Param(
61
+ entityNamesFromV1EncodedParam(country)
62
+ ),
63
+ })
64
+ }
65
+ return url
66
+ }
67
+
68
+ /*
69
+ * Migration #2: Drop dimension keys from selected entities.
70
+ *
71
+ * Implemented: March 2021
72
+ *
73
+ * When plotting multiple variables on a chart, it used to be possible to pick which
74
+ * variable-entity pairs get plotted.
75
+ *
76
+ * For example, if you had a line chart with 3 variables: Energy consumption from Coal, Oil and Gas,
77
+ * then you (as a user) could select individual variable-entity pairs to plot:
78
+ *
79
+ * - France - Coal ("FRA-0")
80
+ * - France - Oil ("FRA-1")
81
+ * - France - Gas ("FRA-2")
82
+ * - ...
83
+ *
84
+ * The index of the dimension was appended to the entity (e.g. 0 for Coal).
85
+ *
86
+ * We dropped this feature in March 2021 in order to simplify the selection-handling logic, and it
87
+ * was also, in most cases, not desirable to present users with variable-entity options.
88
+ *
89
+ */
90
+
91
+ // Pattern for a entity name - number pair, where the entity name contains at least one non-digit character.
92
+ const LegacyDimensionRegex = /^(.*\D.*)-\d+$/
93
+
94
+ const injectEntityNamesInLegacyDimension = (
95
+ entityNames: EntityName[]
96
+ ): EntityName[] => {
97
+ // If an entity has the old name-dimension encoding, removing the dimension part and add it as
98
+ // a new selection. So USA-1 becomes USA.
99
+ const newNames: EntityName[] = []
100
+ entityNames.forEach((entityName) => {
101
+ newNames.push(entityName)
102
+ if (LegacyDimensionRegex.test(entityName)) {
103
+ const nonDimensionName = entityName.replace(
104
+ LegacyDimensionRegex,
105
+ "$1"
106
+ )
107
+ newNames.push(nonDimensionName)
108
+ }
109
+ })
110
+ return newNames
111
+ }
112
+
113
+ const migrateLegacyDimensionPairs: UrlMigration = (url) => {
114
+ const { country } = url.queryParams
115
+ if (country) {
116
+ return url.updateQueryParams({
117
+ country: entityNamesToV2Param(
118
+ injectEntityNamesInLegacyDimension(
119
+ entityNamesFromV2Param(country)
120
+ )
121
+ ),
122
+ })
123
+ }
124
+ return url
125
+ }
126
+
127
+ /*
128
+ * Combining all migrations
129
+ */
130
+
131
+ const urlMigrations: UrlMigration[] = [
132
+ migrateV1Delimited,
133
+ migrateLegacyDimensionPairs,
134
+ ]
135
+
136
+ export const migrateSelectedEntityNamesParam: UrlMigration = (
137
+ url: Url
138
+ ): Url => {
139
+ return performUrlMigrations(urlMigrations, url)
140
+ }
141
+
142
+ /*
143
+ * Accessors
144
+ */
145
+
146
+ export const getEntityNamesParam = (
147
+ param: string | undefined
148
+ ): EntityName[] | undefined => {
149
+ if (param === undefined) return undefined
150
+ return entityNamesFromV2Param(param).map(codeToEntityName)
151
+ }
152
+
153
+ export const getSelectedEntityNamesParam = (
154
+ url: Url
155
+ ): EntityName[] | undefined => {
156
+ // Expects an already-migrated URL as input
157
+ const { country } = url.queryParams
158
+ return getEntityNamesParam(country)
159
+ }
160
+
161
+ export const generateSelectedEntityNamesParam = (
162
+ entityNames: EntityName[]
163
+ ): string => entityNamesToV2Param(entityNames.map(entityNameToCode))
164
+
165
+ export const setSelectedEntityNamesParam = (
166
+ url: Url,
167
+ entityNames: EntityName[] | undefined
168
+ ): Url => {
169
+ // Expects an already-migrated URL as input
170
+ return url.updateQueryParams({
171
+ country: entityNames
172
+ ? generateSelectedEntityNamesParam(entityNames)
173
+ : undefined,
174
+ })
175
+ }
176
+
177
+ /*
178
+ * Focused series names
179
+ *
180
+ * A focused series name is one of:
181
+ * (i) an entity name (common case)
182
+ * (ii) an indicator name (less common, but not rare)
183
+ * (iii) a combination of both, typically represented as 'entityName – indicatorName' (rare)
184
+ *
185
+ * Parsing and serializing focused series names for the URL is done using utility
186
+ * functions that have originally been written for entity names, so that the same
187
+ * delimiter is used and entity names are mapped to their codes if possible. Note
188
+ * that stand-alone entity names are mapped to their codes (case i), while entity
189
+ * names that are a substring of a series name are not (case iii).
190
+ */
191
+
192
+ export const getFocusedSeriesNamesParam = (
193
+ queryParam: string | undefined
194
+ ): SeriesName[] | undefined => {
195
+ return getEntityNamesParam(queryParam)
196
+ }
197
+
198
+ export const generateFocusedSeriesNamesParam = (
199
+ seriesNames: SeriesName[]
200
+ ): string => entityNamesToV2Param(seriesNames.map(entityNameToCode))