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',