diff --git a/web/src/features/charts/BreakdownChart.tsx b/web/src/features/charts/BreakdownChart.tsx
index 3972394b5a..1c13155867 100644
--- a/web/src/features/charts/BreakdownChart.tsx
+++ b/web/src/features/charts/BreakdownChart.tsx
@@ -7,7 +7,7 @@ import { Factory, Zap } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { ElectricityModeType } from 'types';
import trackEvent from 'utils/analytics';
-import { TimeAverages, TrackEvent } from 'utils/constants';
+import { Charts, TimeAverages, TrackEvent } from 'utils/constants';
import { formatCo2 } from 'utils/formatting';
import {
dataSourcesCollapsedBreakdownAtom,
@@ -87,6 +87,11 @@ function BreakdownChart({
translationKey={`country-history.${titleDisplayMode}${titleMixMode}`}
badge={badge}
unit={valueAxisLabel}
+ id={
+ displayByEmissions
+ ? Charts.CARBON_EMISSION_ORIGIN_CHART
+ : Charts.ELECTRICITY_ORIGIN_CHART
+ }
/>
-
{t(`${translationKey}.${timeAverage}`)}
+
+ {t(`${translationKey}.${timeAverage}`)}
+
{badge}
{unit && {unit}
}
diff --git a/web/src/features/charts/EmissionChart.tsx b/web/src/features/charts/EmissionChart.tsx
index 48d13741e4..e24c33148d 100644
--- a/web/src/features/charts/EmissionChart.tsx
+++ b/web/src/features/charts/EmissionChart.tsx
@@ -5,9 +5,12 @@ import { useAtom } from 'jotai';
import { Factory, Zap } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import trackEvent from 'utils/analytics';
-import { TimeAverages, TrackEvent } from 'utils/constants';
+import { Charts, TimeAverages, TrackEvent } from 'utils/constants';
import { formatCo2 } from 'utils/formatting';
-import { dataSourcesCollapsedEmissionAtom } from 'utils/state/atoms';
+import {
+ dataSourcesCollapsedEmissionAtom,
+ displayByEmissionsAtom,
+} from 'utils/state/atoms';
import { ChartTitle } from './ChartTitle';
import { DataSources } from './DataSources';
@@ -33,6 +36,7 @@ function EmissionChart({ timeAverage, datetimes }: EmissionChartProps) {
powerGenerationSources,
emissionFactorSourcesToProductionSources,
} = useZoneDataSources();
+ const [displayByEmissions] = useAtom(displayByEmissionsAtom);
const { t } = useTranslation();
if (isLoading || isError || !data) {
return null;
@@ -52,6 +56,11 @@ function EmissionChart({ timeAverage, datetimes }: EmissionChartProps) {
translationKey="country-history.emissions"
badge={badge}
unit={'CO₂eq'}
+ id={
+ displayByEmissions
+ ? Charts.CARBON_EMISSION_CHART
+ : Charts.CARBON_INTENSITY_CHART
+ }
/>
-
+
{isPriceDisabled && (
diff --git a/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx b/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx
index 42e92508c5..1905beb5e1 100644
--- a/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx
+++ b/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
import { ElectricityModeType, ZoneKey } from 'types';
import useResizeObserver from 'use-resize-observer';
import trackEvent from 'utils/analytics';
-import { TrackEvent } from 'utils/constants';
+import { Charts, TrackEvent } from 'utils/constants';
import {
dataSourcesCollapsedBarBreakdownAtom,
displayByEmissionsAtom,
@@ -137,6 +137,11 @@ function BarBreakdownChart({
estimatedPercentage={currentZoneDetail.estimatedPercentage}
unit={graphUnit}
estimationMethod={currentZoneDetail.estimationMethod}
+ id={
+ displayByEmissions
+ ? Charts.CARBON_EMISSIONS_BAR_BREAKDOWN
+ : Charts.ELECTRICITY_CONSUMPTION_BAR_BREAKDOWN
+ }
/>
{!displayByEmissions && (
diff --git a/web/src/features/charts/bar-breakdown/elements/BySource.tsx b/web/src/features/charts/bar-breakdown/elements/BySource.tsx
index b6a945de75..f62da84e1b 100644
--- a/web/src/features/charts/bar-breakdown/elements/BySource.tsx
+++ b/web/src/features/charts/bar-breakdown/elements/BySource.tsx
@@ -1,8 +1,10 @@
import EstimationBadge from 'components/EstimationBadge';
import { useGetEstimationTranslation } from 'hooks/getEstimationTranslation';
+import { useScrollAnchorIntoView } from 'hooks/useScrollAnchorIntoView';
import { TFunction } from 'i18next';
import { useAtom } from 'jotai';
import { CircleDashed, TrendingUpDown } from 'lucide-react';
+import { useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { EstimationMethods, TimeAverages } from 'utils/constants';
import {
@@ -38,12 +40,14 @@ export default function BySource({
estimatedPercentage,
unit,
estimationMethod,
+ id,
}: {
className?: string;
hasEstimationPill?: boolean;
estimatedPercentage?: number;
unit?: string | number;
estimationMethod?: EstimationMethods;
+ id?: string;
}) {
const { t } = useTranslation();
const [timeAverage] = useAtom(timeAverageAtom);
@@ -58,13 +62,18 @@ export default function BySource({
estimatedPercentage
);
+ const reference = useRef(null);
+ useScrollAnchorIntoView(reference);
+
return (
-
{text}
+
+ {text}
+
{hasEstimationPill && (
{
+ const location = useLocation();
+ const [displayByEmissions, setDisplayByEmissions] = useAtom(displayByEmissionsAtom);
+
+ useEffect(() => {
+ const hash = location.hash.slice(1);
+
+ if (!hash) {
+ return;
+ }
+
+ const panel = ChartsToPanel[hash as Charts];
+
+ if (!panel) {
+ return;
+ }
+
+ if (
+ (displayByEmissions && panel === LeftPanelToggleOptions.EMISSIONS) ||
+ (!displayByEmissions && panel === LeftPanelToggleOptions.ELECTRICITY)
+ ) {
+ return;
+ }
+ setDisplayByEmissions(panel === LeftPanelToggleOptions.EMISSIONS);
+ }, [location.hash, isLoading, displayByEmissions, setDisplayByEmissions]);
+};
+
export default function ZoneDetails(): JSX.Element {
const { zoneId } = useParams();
const timeAverage = useAtomValue(timeAverageAtom);
@@ -40,6 +73,8 @@ export default function ZoneDetails(): JSX.Element {
const hasSubZones = getHasSubZones(zoneId);
const isSubZone = zoneId ? zoneId.includes('-') : true;
+ useScrollHashIntoView(isLoading);
+
useEffect(() => {
if (hasSubZones === null) {
return;
diff --git a/web/src/hooks/useClearFragment.ts b/web/src/hooks/useClearFragment.ts
new file mode 100644
index 0000000000..a942dfcbd9
--- /dev/null
+++ b/web/src/hooks/useClearFragment.ts
@@ -0,0 +1,11 @@
+import { useCallback } from 'react';
+import { useLocation, useNavigate } from 'react-router-dom';
+
+export function useClearFragment() {
+ const navigate = useNavigate();
+ const location = useLocation();
+
+ return useCallback(() => {
+ navigate(`${location.pathname}${location.search}`);
+ }, [navigate, location.pathname, location.search]);
+}
diff --git a/web/src/hooks/useScrollAnchorIntoView.ts b/web/src/hooks/useScrollAnchorIntoView.ts
new file mode 100644
index 0000000000..c93088ba59
--- /dev/null
+++ b/web/src/hooks/useScrollAnchorIntoView.ts
@@ -0,0 +1,17 @@
+import { RefObject, useEffect } from 'react';
+import { useLocation } from 'react-router-dom';
+
+import { useClearFragment } from './useClearFragment';
+
+export function useScrollAnchorIntoView(reference: RefObject) {
+ const { hash } = useLocation();
+ const clearFragment = useClearFragment();
+ useEffect(() => {
+ if (reference.current?.id === hash.slice(1)) {
+ reference.current.scrollIntoView({ behavior: 'smooth' });
+ clearFragment();
+ }
+ }, [hash, clearFragment, reference]);
+
+ return;
+}
diff --git a/web/src/utils/constants.ts b/web/src/utils/constants.ts
index ff5ba9466b..f86ba2c469 100644
--- a/web/src/utils/constants.ts
+++ b/web/src/utils/constants.ts
@@ -38,6 +38,32 @@ export enum LeftPanelToggleOptions {
EMISSIONS = 'emissions',
}
+export enum Charts {
+ CARBON_EMISSIONS_BAR_BREAKDOWN = 'carbon_emissions_bar_breakdown',
+ ELECTRICITY_CONSUMPTION_BAR_BREAKDOWN = 'electricity_consumption_bar_breakdown',
+ ELECTRICITY_NET_EXCHANGE_CHART = 'electricity_net_exchange_chart',
+ EMISSIONS_NET_EXCHANGE_CHART = 'emissions_net_exchange_chart',
+ CARBON_EMISSION_CHART = 'carbon_emission_chart',
+ CARBON_INTENSITY_CHART = 'carbon_intensity_chart',
+ ELECTRICITY_ORIGIN_CHART = 'electricity_origin_chart',
+ CARBON_EMISSION_ORIGIN_CHART = 'carbon_emission_origin_chart',
+ ELECTRICITY_PRICES_CHART = 'electricity_prices_chart',
+ EMISSIONS_ELECTRICITY_PRICES_CHART = 'emissions_electricity_prices_chart',
+}
+
+export const ChartsToPanel = {
+ [Charts.CARBON_EMISSIONS_BAR_BREAKDOWN]: LeftPanelToggleOptions.EMISSIONS,
+ [Charts.ELECTRICITY_CONSUMPTION_BAR_BREAKDOWN]: LeftPanelToggleOptions.ELECTRICITY,
+ [Charts.ELECTRICITY_NET_EXCHANGE_CHART]: LeftPanelToggleOptions.ELECTRICITY,
+ [Charts.EMISSIONS_NET_EXCHANGE_CHART]: LeftPanelToggleOptions.EMISSIONS,
+ [Charts.CARBON_EMISSION_CHART]: LeftPanelToggleOptions.EMISSIONS,
+ [Charts.CARBON_INTENSITY_CHART]: LeftPanelToggleOptions.ELECTRICITY,
+ [Charts.ELECTRICITY_ORIGIN_CHART]: LeftPanelToggleOptions.ELECTRICITY,
+ [Charts.CARBON_EMISSION_ORIGIN_CHART]: LeftPanelToggleOptions.EMISSIONS,
+ [Charts.ELECTRICITY_PRICES_CHART]: LeftPanelToggleOptions.ELECTRICITY,
+ [Charts.EMISSIONS_ELECTRICITY_PRICES_CHART]: LeftPanelToggleOptions.EMISSIONS,
+};
+
export enum TrackEvent {
DATA_SOURCES_CLICKED = 'Data Sources Clicked',
APP_BANNER_CTA_CLICKED = 'App Banner CTA Clicked',