@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.
- package/LICENSE.md +8 -0
- package/README.md +113 -0
- package/package.json +137 -0
- package/src/components/BodyPortal/BodyPortal.tsx +40 -0
- package/src/components/Button/Button.scss +110 -0
- package/src/components/Button/Button.tsx +101 -0
- package/src/components/Checkbox.scss +93 -0
- package/src/components/Checkbox.tsx +47 -0
- package/src/components/ExpandableToggle/ExpandableToggle.scss +123 -0
- package/src/components/ExpandableToggle/ExpandableToggle.tsx +60 -0
- package/src/components/GrapherTabIcon.tsx +156 -0
- package/src/components/GrapherTrendArrow.scss +16 -0
- package/src/components/GrapherTrendArrow.tsx +30 -0
- package/src/components/Halo/Halo.tsx +44 -0
- package/src/components/LabeledSwitch/LabeledSwitch.scss +109 -0
- package/src/components/LabeledSwitch/LabeledSwitch.tsx +62 -0
- package/src/components/MarkdownTextWrap/MarkdownTextWrap.tsx +1173 -0
- package/src/components/OverlayHeader.scss +18 -0
- package/src/components/OverlayHeader.tsx +29 -0
- package/src/components/RadioButton.scss +69 -0
- package/src/components/RadioButton.tsx +42 -0
- package/src/components/SimpleMarkdownText.tsx +89 -0
- package/src/components/TextInput.scss +17 -0
- package/src/components/TextInput.tsx +19 -0
- package/src/components/TextWrap/TextWrap.tsx +361 -0
- package/src/components/TextWrap/TextWrapUtils.ts +32 -0
- package/src/components/closeButton/CloseButton.scss +40 -0
- package/src/components/closeButton/CloseButton.tsx +27 -0
- package/src/components/index.ts +70 -0
- package/src/components/loadingIndicator/LoadingIndicator.scss +40 -0
- package/src/components/loadingIndicator/LoadingIndicator.tsx +28 -0
- package/src/components/markdown/remarkPlainLinks.ts +36 -0
- package/src/components/reactUtil.ts +20 -0
- package/src/components/stubs/CodeSnippet.tsx +19 -0
- package/src/components/stubs/DataCitation.tsx +16 -0
- package/src/components/stubs/IndicatorKeyData.tsx +45 -0
- package/src/components/stubs/IndicatorProcessing.tsx +15 -0
- package/src/components/stubs/IndicatorSources.tsx +15 -0
- package/src/components/styles/colors.scss +113 -0
- package/src/components/styles/mixins.scss +630 -0
- package/src/components/styles/typography.scss +579 -0
- package/src/components/styles/util.scss +89 -0
- package/src/components/styles/variables.scss +208 -0
- package/src/config/ChartsConfig.ts +163 -0
- package/src/config/ChartsProvider.tsx +157 -0
- package/src/config/index.ts +20 -0
- package/src/core-table/CoreTable.ts +1355 -0
- package/src/core-table/CoreTableColumns.ts +973 -0
- package/src/core-table/CoreTableUtils.ts +793 -0
- package/src/core-table/ErrorValues.ts +73 -0
- package/src/core-table/OwidTable.ts +1175 -0
- package/src/core-table/OwidTableSynthesizers.ts +272 -0
- package/src/core-table/OwidTableUtil.ts +76 -0
- package/src/core-table/Transforms.ts +484 -0
- package/src/core-table/index.ts +82 -0
- package/src/explorer/ColumnGrammar.ts +217 -0
- package/src/explorer/Explorer.sample.ts +212 -0
- package/src/explorer/Explorer.scss +148 -0
- package/src/explorer/Explorer.tsx +1283 -0
- package/src/explorer/ExplorerConstants.ts +85 -0
- package/src/explorer/ExplorerControls.scss +156 -0
- package/src/explorer/ExplorerControls.tsx +210 -0
- package/src/explorer/ExplorerDecisionMatrix.ts +471 -0
- package/src/explorer/ExplorerGrammar.ts +161 -0
- package/src/explorer/ExplorerProgram.ts +568 -0
- package/src/explorer/ExplorerUtils.ts +59 -0
- package/src/explorer/GrapherGrammar.ts +387 -0
- package/src/explorer/gridLang/GrammarUtils.ts +121 -0
- package/src/explorer/gridLang/GridCell.ts +298 -0
- package/src/explorer/gridLang/GridLangConstants.ts +255 -0
- package/src/explorer/gridLang/GridProgram.ts +311 -0
- package/src/explorer/gridLang/readme.md +17 -0
- package/src/explorer/index.ts +69 -0
- package/src/explorer/readme.md +19 -0
- package/src/explorer/urlMigrations/CO2UrlMigration.ts +46 -0
- package/src/explorer/urlMigrations/CovidUrlMigration.ts +37 -0
- package/src/explorer/urlMigrations/EnergyUrlMigration.ts +41 -0
- package/src/explorer/urlMigrations/ExplorerPageUrlMigrationSpec.ts +12 -0
- package/src/explorer/urlMigrations/ExplorerUrlMigrationUtils.ts +45 -0
- package/src/explorer/urlMigrations/ExplorerUrlMigrations.ts +33 -0
- package/src/explorer/urlMigrations/LegacyCovidUrlMigration.ts +144 -0
- package/src/explorer/urlMigrations/readme.md +39 -0
- package/src/grapher/axis/Axis.ts +973 -0
- package/src/grapher/axis/AxisConfig.ts +179 -0
- package/src/grapher/axis/AxisViews.tsx +597 -0
- package/src/grapher/barCharts/DiscreteBarChart.tsx +728 -0
- package/src/grapher/barCharts/DiscreteBarChartConstants.ts +60 -0
- package/src/grapher/barCharts/DiscreteBarChartHelpers.ts +338 -0
- package/src/grapher/barCharts/DiscreteBarChartState.ts +354 -0
- package/src/grapher/barCharts/DiscreteBarChartThumbnail.tsx +34 -0
- package/src/grapher/captionedChart/CaptionedChart.scss +61 -0
- package/src/grapher/captionedChart/CaptionedChart.tsx +523 -0
- package/src/grapher/captionedChart/Logos.tsx +141 -0
- package/src/grapher/captionedChart/LogosSVG.tsx +16 -0
- package/src/grapher/captionedChart/StaticChartRasterizer.tsx +178 -0
- package/src/grapher/captionedChart/assets/buildcanada-logo-square.svg +15 -0
- package/src/grapher/captionedChart/assets/buildcanada-logo.svg +15 -0
- package/src/grapher/captionedChart/assets/canadaspends.svg +7 -0
- package/src/grapher/captionedChart/readme.md +14 -0
- package/src/grapher/chart/Chart.tsx +62 -0
- package/src/grapher/chart/ChartAreaContent.tsx +172 -0
- package/src/grapher/chart/ChartDimension.ts +121 -0
- package/src/grapher/chart/ChartInterface.ts +83 -0
- package/src/grapher/chart/ChartManager.ts +113 -0
- package/src/grapher/chart/ChartTabs.ts +178 -0
- package/src/grapher/chart/ChartTypeMap.tsx +158 -0
- package/src/grapher/chart/ChartTypeSwitcher.tsx +26 -0
- package/src/grapher/chart/ChartUtils.tsx +364 -0
- package/src/grapher/chart/DimensionSlot.ts +45 -0
- package/src/grapher/chart/StaticChartWrapper.tsx +94 -0
- package/src/grapher/chart/guidedChartUtils.ts +82 -0
- package/src/grapher/color/BinningStrategies.ts +484 -0
- package/src/grapher/color/BinningStrategyEqualSizeBins.ts +132 -0
- package/src/grapher/color/BinningStrategyLogarithmic.ts +121 -0
- package/src/grapher/color/CategoricalColorAssigner.ts +97 -0
- package/src/grapher/color/ColorBrewerSchemes.ts +80 -0
- package/src/grapher/color/ColorConstants.ts +20 -0
- package/src/grapher/color/ColorScale.ts +339 -0
- package/src/grapher/color/ColorScaleBin.ts +147 -0
- package/src/grapher/color/ColorScaleConfig.ts +204 -0
- package/src/grapher/color/ColorScheme.ts +137 -0
- package/src/grapher/color/ColorSchemes.ts +149 -0
- package/src/grapher/color/ColorUtils.ts +86 -0
- package/src/grapher/color/CustomSchemes.ts +1772 -0
- package/src/grapher/color/readme.md +84 -0
- package/src/grapher/comparisonLine/ComparisonLine.tsx +31 -0
- package/src/grapher/comparisonLine/ComparisonLineConstants.ts +11 -0
- package/src/grapher/comparisonLine/ComparisonLineGenerator.ts +60 -0
- package/src/grapher/comparisonLine/ComparisonLineHelpers.ts +10 -0
- package/src/grapher/comparisonLine/CustomComparisonLine.tsx +159 -0
- package/src/grapher/comparisonLine/VerticalComparisonLine.tsx +208 -0
- package/src/grapher/controls/ActionButtons.scss +97 -0
- package/src/grapher/controls/ActionButtons.tsx +453 -0
- package/src/grapher/controls/CommandPalette.scss +50 -0
- package/src/grapher/controls/CommandPalette.tsx +74 -0
- package/src/grapher/controls/ContentSwitchers.scss +93 -0
- package/src/grapher/controls/ContentSwitchers.tsx +238 -0
- package/src/grapher/controls/Controls.scss +158 -0
- package/src/grapher/controls/DataTableFilterDropdown.scss +7 -0
- package/src/grapher/controls/DataTableFilterDropdown.tsx +168 -0
- package/src/grapher/controls/DataTableSearchField.scss +3 -0
- package/src/grapher/controls/DataTableSearchField.tsx +76 -0
- package/src/grapher/controls/Dropdown.scss +252 -0
- package/src/grapher/controls/Dropdown.tsx +235 -0
- package/src/grapher/controls/EntitySelectionToggle.tsx +135 -0
- package/src/grapher/controls/MapRegionDropdown.scss +3 -0
- package/src/grapher/controls/MapRegionDropdown.tsx +104 -0
- package/src/grapher/controls/MapResetButton.tsx +115 -0
- package/src/grapher/controls/MapZoomDropdown.scss +9 -0
- package/src/grapher/controls/MapZoomDropdown.tsx +270 -0
- package/src/grapher/controls/MapZoomToSelectionButton.tsx +87 -0
- package/src/grapher/controls/SearchField.scss +78 -0
- package/src/grapher/controls/SearchField.tsx +63 -0
- package/src/grapher/controls/SettingsMenu.scss +191 -0
- package/src/grapher/controls/SettingsMenu.tsx +399 -0
- package/src/grapher/controls/ShareMenu.scss +58 -0
- package/src/grapher/controls/ShareMenu.tsx +304 -0
- package/src/grapher/controls/SortIcon.tsx +39 -0
- package/src/grapher/controls/VerticalScrollContainer.tsx +263 -0
- package/src/grapher/controls/controlsRow/ControlsRow.tsx +168 -0
- package/src/grapher/controls/dropdown-icons.scss +4 -0
- package/src/grapher/controls/entityPicker/EntityPicker.scss +255 -0
- package/src/grapher/controls/entityPicker/EntityPicker.tsx +816 -0
- package/src/grapher/controls/entityPicker/EntityPickerConstants.ts +23 -0
- package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.scss +129 -0
- package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.tsx +463 -0
- package/src/grapher/controls/globalEntitySelector/GlobalEntitySelectorConstants.ts +3 -0
- package/src/grapher/controls/globalEntitySelector/readme.md +17 -0
- package/src/grapher/controls/settings/AbsRelToggle.tsx +64 -0
- package/src/grapher/controls/settings/AxisScaleToggle.tsx +53 -0
- package/src/grapher/controls/settings/FacetStrategySelector.tsx +110 -0
- package/src/grapher/controls/settings/FacetYDomainToggle.tsx +51 -0
- package/src/grapher/controls/settings/NoDataAreaToggle.tsx +38 -0
- package/src/grapher/controls/settings/ZoomToggle.tsx +36 -0
- package/src/grapher/core/EntitiesByRegionType.ts +174 -0
- package/src/grapher/core/EntityCodes.ts +19 -0
- package/src/grapher/core/EntityUrlBuilder.ts +200 -0
- package/src/grapher/core/FetchingGrapher.tsx +156 -0
- package/src/grapher/core/Grapher.tsx +760 -0
- package/src/grapher/core/GrapherAnalytics.ts +229 -0
- package/src/grapher/core/GrapherConstants.ts +173 -0
- package/src/grapher/core/GrapherState.tsx +3659 -0
- package/src/grapher/core/GrapherUrl.ts +184 -0
- package/src/grapher/core/GrapherUrlMigrations.ts +29 -0
- package/src/grapher/core/GrapherUseHelpers.tsx +147 -0
- package/src/grapher/core/LegacyToOwidTable.ts +841 -0
- package/src/grapher/core/grapher.entry.ts +5 -0
- package/src/grapher/core/grapher.scss +257 -0
- package/src/grapher/core/loadGrapherTableHelpers.ts +116 -0
- package/src/grapher/core/loadVariable.ts +104 -0
- package/src/grapher/core/relatedQuestion.ts +12 -0
- package/src/grapher/core/typography.scss +206 -0
- package/src/grapher/dataTable/DataTable.sample.ts +206 -0
- package/src/grapher/dataTable/DataTable.scss +249 -0
- package/src/grapher/dataTable/DataTable.tsx +1332 -0
- package/src/grapher/dataTable/DataTableConstants.ts +186 -0
- package/src/grapher/entitySelector/EntitySelector.scss +255 -0
- package/src/grapher/entitySelector/EntitySelector.tsx +1838 -0
- package/src/grapher/facet/FacetChart.tsx +943 -0
- package/src/grapher/facet/FacetChartConstants.ts +24 -0
- package/src/grapher/facet/FacetChartUtils.ts +51 -0
- package/src/grapher/facet/FacetMap.tsx +604 -0
- package/src/grapher/facet/FacetMapConstants.ts +23 -0
- package/src/grapher/facet/readme.md +13 -0
- package/src/grapher/focus/FocusArray.ts +79 -0
- package/src/grapher/footer/Footer.scss +63 -0
- package/src/grapher/footer/Footer.tsx +809 -0
- package/src/grapher/footer/FooterManager.ts +44 -0
- package/src/grapher/fullScreen/FullScreen.scss +11 -0
- package/src/grapher/fullScreen/FullScreen.tsx +61 -0
- package/src/grapher/header/Header.scss +35 -0
- package/src/grapher/header/Header.tsx +372 -0
- package/src/grapher/header/HeaderManager.ts +28 -0
- package/src/grapher/index.ts +157 -0
- package/src/grapher/interaction/InteractionState.ts +60 -0
- package/src/grapher/legend/HorizontalColorLegends.tsx +923 -0
- package/src/grapher/legend/LegendInteractionState.ts +40 -0
- package/src/grapher/legend/VerticalColorLegend.tsx +295 -0
- package/src/grapher/lineCharts/LineChart.tsx +968 -0
- package/src/grapher/lineCharts/LineChartConstants.ts +89 -0
- package/src/grapher/lineCharts/LineChartHelpers.ts +184 -0
- package/src/grapher/lineCharts/LineChartState.ts +394 -0
- package/src/grapher/lineCharts/LineChartThumbnail.tsx +437 -0
- package/src/grapher/lineCharts/Lines.tsx +258 -0
- package/src/grapher/lineLegend/LineLegend.tsx +723 -0
- package/src/grapher/lineLegend/LineLegendConstants.ts +9 -0
- package/src/grapher/lineLegend/LineLegendFilterAlgorithms.ts +143 -0
- package/src/grapher/lineLegend/LineLegendHelpers.ts +253 -0
- package/src/grapher/lineLegend/LineLegendTypes.ts +32 -0
- package/src/grapher/mapCharts/CanadaTopology.ts +17922 -0
- package/src/grapher/mapCharts/ChoroplethGlobe.tsx +949 -0
- package/src/grapher/mapCharts/ChoroplethMap.tsx +662 -0
- package/src/grapher/mapCharts/GeoFeatures.ts +184 -0
- package/src/grapher/mapCharts/GlobeController.ts +496 -0
- package/src/grapher/mapCharts/MapAnnotationPlacements.json +1040 -0
- package/src/grapher/mapCharts/MapAnnotationPlacements.ts +31 -0
- package/src/grapher/mapCharts/MapAnnotations.ts +723 -0
- package/src/grapher/mapCharts/MapChart.sample.ts +59 -0
- package/src/grapher/mapCharts/MapChart.scss +5 -0
- package/src/grapher/mapCharts/MapChart.tsx +720 -0
- package/src/grapher/mapCharts/MapChartConstants.ts +260 -0
- package/src/grapher/mapCharts/MapChartState.ts +416 -0
- package/src/grapher/mapCharts/MapChartThumbnail.tsx +25 -0
- package/src/grapher/mapCharts/MapComponents.tsx +338 -0
- package/src/grapher/mapCharts/MapConfig.ts +156 -0
- package/src/grapher/mapCharts/MapHelpers.ts +181 -0
- package/src/grapher/mapCharts/MapProjections.ts +49 -0
- package/src/grapher/mapCharts/MapSparkline.tsx +257 -0
- package/src/grapher/mapCharts/MapTooltip.scss +49 -0
- package/src/grapher/mapCharts/MapTooltip.tsx +409 -0
- package/src/grapher/mapCharts/MapTopology.ts +1766 -0
- package/src/grapher/mapCharts/d3-bboxCollide.js +204 -0
- package/src/grapher/mapCharts/d3-geo-projection.ts +198 -0
- package/src/grapher/modal/DownloadIcons.tsx +39 -0
- package/src/grapher/modal/DownloadModal.scss +300 -0
- package/src/grapher/modal/DownloadModal.tsx +1226 -0
- package/src/grapher/modal/EmbedModal.scss +40 -0
- package/src/grapher/modal/EmbedModal.tsx +160 -0
- package/src/grapher/modal/EntitySelectorModal.tsx +59 -0
- package/src/grapher/modal/Modal.scss +31 -0
- package/src/grapher/modal/Modal.tsx +90 -0
- package/src/grapher/modal/ModalHeader.scss +12 -0
- package/src/grapher/modal/ModalHeader.tsx +16 -0
- package/src/grapher/modal/SourcesDescriptions.scss +87 -0
- package/src/grapher/modal/SourcesDescriptions.tsx +89 -0
- package/src/grapher/modal/SourcesKeyDataTable.scss +49 -0
- package/src/grapher/modal/SourcesKeyDataTable.tsx +87 -0
- package/src/grapher/modal/SourcesModal.scss +301 -0
- package/src/grapher/modal/SourcesModal.tsx +568 -0
- package/src/grapher/noDataModal/NoDataModal.tsx +125 -0
- package/src/grapher/scatterCharts/ConnectedScatterLegend.tsx +143 -0
- package/src/grapher/scatterCharts/MultiColorPolyline.tsx +129 -0
- package/src/grapher/scatterCharts/NoDataSection.scss +14 -0
- package/src/grapher/scatterCharts/NoDataSection.tsx +56 -0
- package/src/grapher/scatterCharts/ScatterPlotChart.tsx +792 -0
- package/src/grapher/scatterCharts/ScatterPlotChartConstants.ts +157 -0
- package/src/grapher/scatterCharts/ScatterPlotChartState.ts +678 -0
- package/src/grapher/scatterCharts/ScatterPlotChartThumbnail.tsx +155 -0
- package/src/grapher/scatterCharts/ScatterPlotTooltip.tsx +560 -0
- package/src/grapher/scatterCharts/ScatterPoints.tsx +153 -0
- package/src/grapher/scatterCharts/ScatterPointsWithLabels.tsx +708 -0
- package/src/grapher/scatterCharts/ScatterSizeLegend.tsx +327 -0
- package/src/grapher/scatterCharts/ScatterUtils.ts +265 -0
- package/src/grapher/scatterCharts/Triangle.tsx +41 -0
- package/src/grapher/schema/README.md +33 -0
- package/src/grapher/schema/defaultGrapherConfig.ts +100 -0
- package/src/grapher/schema/grapher-schema.009.yaml +781 -0
- package/src/grapher/schema/migrations/helpers.ts +58 -0
- package/src/grapher/schema/migrations/migrate.ts +75 -0
- package/src/grapher/schema/migrations/migrations.ts +158 -0
- package/src/grapher/selection/MapSelectionArray.ts +99 -0
- package/src/grapher/selection/SelectionArray.ts +71 -0
- package/src/grapher/selection/readme.md +16 -0
- package/src/grapher/sidePanel/SidePanel.scss +10 -0
- package/src/grapher/sidePanel/SidePanel.tsx +23 -0
- package/src/grapher/slideInDrawer/SlideInDrawer.scss +57 -0
- package/src/grapher/slideInDrawer/SlideInDrawer.tsx +125 -0
- package/src/grapher/slideshowController/SlideShowController.tsx +43 -0
- package/src/grapher/slideshowController/readme.md +7 -0
- package/src/grapher/slopeCharts/MarkX.tsx +45 -0
- package/src/grapher/slopeCharts/Slope.tsx +102 -0
- package/src/grapher/slopeCharts/SlopeChart.tsx +1152 -0
- package/src/grapher/slopeCharts/SlopeChartConstants.ts +33 -0
- package/src/grapher/slopeCharts/SlopeChartHelpers.ts +73 -0
- package/src/grapher/slopeCharts/SlopeChartState.ts +392 -0
- package/src/grapher/slopeCharts/SlopeChartThumbnail.tsx +368 -0
- package/src/grapher/stackedCharts/AbstractStackedChartState.ts +370 -0
- package/src/grapher/stackedCharts/MarimekkoBars.tsx +190 -0
- package/src/grapher/stackedCharts/MarimekkoBarsForOneEntity.tsx +168 -0
- package/src/grapher/stackedCharts/MarimekkoChart.tsx +1144 -0
- package/src/grapher/stackedCharts/MarimekkoChartConstants.ts +112 -0
- package/src/grapher/stackedCharts/MarimekkoChartHelpers.ts +21 -0
- package/src/grapher/stackedCharts/MarimekkoChartState.ts +465 -0
- package/src/grapher/stackedCharts/MarimekkoChartThumbnail.tsx +168 -0
- package/src/grapher/stackedCharts/MarimekkoInternalLabels.tsx +124 -0
- package/src/grapher/stackedCharts/StackedAreaChart.tsx +678 -0
- package/src/grapher/stackedCharts/StackedAreaChartState.ts +34 -0
- package/src/grapher/stackedCharts/StackedAreaChartThumbnail.tsx +215 -0
- package/src/grapher/stackedCharts/StackedAreas.tsx +223 -0
- package/src/grapher/stackedCharts/StackedBarChart.tsx +619 -0
- package/src/grapher/stackedCharts/StackedBarChartState.ts +80 -0
- package/src/grapher/stackedCharts/StackedBarChartThumbnail.tsx +220 -0
- package/src/grapher/stackedCharts/StackedBarSegment.tsx +87 -0
- package/src/grapher/stackedCharts/StackedBars.tsx +102 -0
- package/src/grapher/stackedCharts/StackedConstants.ts +109 -0
- package/src/grapher/stackedCharts/StackedDiscreteBarChart.tsx +270 -0
- package/src/grapher/stackedCharts/StackedDiscreteBarChartState.ts +296 -0
- package/src/grapher/stackedCharts/StackedDiscreteBarChartThumbnail.tsx +27 -0
- package/src/grapher/stackedCharts/StackedDiscreteBars.tsx +648 -0
- package/src/grapher/stackedCharts/StackedUtils.ts +142 -0
- package/src/grapher/tabs/Tabs.scss +169 -0
- package/src/grapher/tabs/Tabs.tsx +54 -0
- package/src/grapher/tabs/TabsWithDropdown.scss +62 -0
- package/src/grapher/tabs/TabsWithDropdown.tsx +114 -0
- package/src/grapher/testData/OwidTestData.sample.ts +273 -0
- package/src/grapher/testData/OwidTestData.ts +64 -0
- package/src/grapher/timeline/TimelineComponent.scss +139 -0
- package/src/grapher/timeline/TimelineComponent.tsx +658 -0
- package/src/grapher/timeline/TimelineController.ts +368 -0
- package/src/grapher/timeline/readme.md +7 -0
- package/src/grapher/tooltip/Tooltip.scss +510 -0
- package/src/grapher/tooltip/Tooltip.tsx +294 -0
- package/src/grapher/tooltip/TooltipContents.tsx +383 -0
- package/src/grapher/tooltip/TooltipProps.ts +123 -0
- package/src/grapher/tooltip/TooltipState.ts +81 -0
- package/src/grapher/verticalLabels/VerticalLabels.tsx +31 -0
- package/src/grapher/verticalLabels/VerticalLabelsState.ts +154 -0
- package/src/index.ts +226 -0
- package/src/styles/charts.scss +15 -0
- package/src/types/NominalType.ts +30 -0
- package/src/types/OwidOrigin.ts +18 -0
- package/src/types/OwidSource.ts +9 -0
- package/src/types/OwidVariable.ts +133 -0
- package/src/types/OwidVariableDisplayConfigInterface.ts +49 -0
- package/src/types/analyticsTypes.ts +54 -0
- package/src/types/dbTypes/Tags.ts +11 -0
- package/src/types/domainTypes/Archive.ts +139 -0
- package/src/types/domainTypes/Author.ts +28 -0
- package/src/types/domainTypes/ContentGraph.ts +76 -0
- package/src/types/domainTypes/CoreTableTypes.ts +305 -0
- package/src/types/domainTypes/DeployStatus.ts +23 -0
- package/src/types/domainTypes/Layout.ts +34 -0
- package/src/types/domainTypes/Posts.ts +34 -0
- package/src/types/domainTypes/Search.ts +299 -0
- package/src/types/domainTypes/Site.ts +8 -0
- package/src/types/domainTypes/StaticViz.ts +64 -0
- package/src/types/domainTypes/Toc.ts +11 -0
- package/src/types/domainTypes/Tombstone.ts +19 -0
- package/src/types/domainTypes/Various.ts +79 -0
- package/src/types/gdocTypes/Gdoc.ts +280 -0
- package/src/types/grapherTypes/BinningStrategyTypes.ts +46 -0
- package/src/types/grapherTypes/GrapherConstants.ts +53 -0
- package/src/types/grapherTypes/GrapherTypes.ts +743 -0
- package/src/types/index.ts +316 -0
- package/src/types/wordpressTypes/WordpressTypes.ts +9 -0
- package/src/utils/Bounds.ts +439 -0
- package/src/utils/BrowserUtils.ts +12 -0
- package/src/utils/FuzzySearch.ts +74 -0
- package/src/utils/MultiDimDataPageConfig.ts +31 -0
- package/src/utils/OwidVariable.ts +82 -0
- package/src/utils/PointVector.ts +97 -0
- package/src/utils/PromiseCache.ts +36 -0
- package/src/utils/PromiseSwitcher.ts +52 -0
- package/src/utils/TimeBounds.ts +130 -0
- package/src/utils/Tippy.tsx +57 -0
- package/src/utils/Util.ts +2369 -0
- package/src/utils/archival/archivalDate.ts +48 -0
- package/src/utils/dayjs.ts +32 -0
- package/src/utils/formatValue.ts +242 -0
- package/src/utils/grapherConfigUtils.ts +81 -0
- package/src/utils/image.ts +225 -0
- package/src/utils/index.ts +318 -0
- package/src/utils/isPresent.ts +5 -0
- package/src/utils/metadataHelpers.ts +329 -0
- package/src/utils/persistable/Persistable.ts +82 -0
- package/src/utils/persistable/readme.md +50 -0
- package/src/utils/regions.json +5635 -0
- package/src/utils/regions.ts +463 -0
- package/src/utils/serializers.ts +16 -0
- package/src/utils/string.ts +42 -0
- package/src/utils/urls/Url.ts +195 -0
- package/src/utils/urls/UrlMigration.ts +10 -0
- package/src/utils/urls/UrlUtils.ts +54 -0
- 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))
|