From 22e16ae4e4737139c47d1374faa80e6c825690ea Mon Sep 17 00:00:00 2001 From: Ben Hammond Date: Fri, 11 Oct 2024 12:56:25 -0600 Subject: [PATCH] extract more components --- .../src/charts/rateBarChart/EndOfBarLabel.tsx | 3 +- .../charts/rateBarChart/GroupLabelsYAxis.tsx | 10 +- frontend/src/charts/rateBarChart/Index.tsx | 102 ++++-------------- .../rateBarChart/RoundedBarsWithLabels.tsx | 80 ++++++++++++++ frontend/src/charts/rateBarChart/YAxis.tsx | 54 ++++++++++ frontend/src/charts/rateBarChart/helpers.ts | 3 +- 6 files changed, 165 insertions(+), 87 deletions(-) create mode 100644 frontend/src/charts/rateBarChart/RoundedBarsWithLabels.tsx create mode 100644 frontend/src/charts/rateBarChart/YAxis.tsx diff --git a/frontend/src/charts/rateBarChart/EndOfBarLabel.tsx b/frontend/src/charts/rateBarChart/EndOfBarLabel.tsx index d8267dccc5..b01c5459ce 100644 --- a/frontend/src/charts/rateBarChart/EndOfBarLabel.tsx +++ b/frontend/src/charts/rateBarChart/EndOfBarLabel.tsx @@ -1,3 +1,4 @@ +import type { ScaleBand } from 'd3' import type { MetricConfig } from '../../data/config/MetricConfigTypes' import { formatValue } from './helpers' @@ -6,7 +7,7 @@ interface EndOfBarLabelProps { d: Record shouldLabelBeInside: boolean barWidth: number - yScale: any + yScale: ScaleBand barLabelColor: string isTinyAndUp: boolean } diff --git a/frontend/src/charts/rateBarChart/GroupLabelsYAxis.tsx b/frontend/src/charts/rateBarChart/GroupLabelsYAxis.tsx index 117b2cf3b6..31d03a9b74 100644 --- a/frontend/src/charts/rateBarChart/GroupLabelsYAxis.tsx +++ b/frontend/src/charts/rateBarChart/GroupLabelsYAxis.tsx @@ -1,12 +1,18 @@ +import type { ScaleBand } from 'd3' import type { Fips } from '../../data/utils/Fips' import { getComparisonAllSubGroupLines } from './helpers' +type WrappedLabel = { + original: string + lines: string[] +} + interface GroupLabelsYAxisProps { fips: Fips useIntersectionalComparisonAlls?: boolean comparisonAllSubGroup?: string - wrappedLabels: any - yScale: any + wrappedLabels: WrappedLabel[] + yScale: ScaleBand getYPosition: (index: number, label: string) => number } diff --git a/frontend/src/charts/rateBarChart/Index.tsx b/frontend/src/charts/rateBarChart/Index.tsx index 1d5619ada6..7aceb834fb 100644 --- a/frontend/src/charts/rateBarChart/Index.tsx +++ b/frontend/src/charts/rateBarChart/Index.tsx @@ -2,7 +2,6 @@ import { max, scaleBand, scaleLinear } from 'd3' import { useMemo } from 'react' import type { MetricConfig } from '../../data/config/MetricConfigTypes' import { - DEMOGRAPHIC_DISPLAY_TYPES_LOWER_CASE, hasSkinnyGroupLabels, type DemographicType, } from '../../data/query/Breakdowns' @@ -16,19 +15,17 @@ import { BAR_HEIGHT, BAR_PADDING, EXTRA_SPACE_AFTER_ALL, - LABEL_SWAP_CUTOFF_PERCENT, MARGIN, MAX_LABEL_WIDTH_BIG, MAX_LABEL_WIDTH_SMALL, NORMAL_MARGIN_HEIGHT, Y_AXIS_LABEL_HEIGHT, } from './constants' -import EndOfBarLabel from './EndOfBarLabel' -import GroupLabelsYAxis from './GroupLabelsYAxis' -import { buildRoundedBarString, wrapLabel } from './helpers' +import RoundedBarsWithLabels from './RoundedBarsWithLabels' import { useRateChartTooltip } from './useRateChartTooltip' import VerticalGridlines from './VerticalGridlines' import XAxis from './XAxis' +import YAxis from './YAxis' interface RateBarChartProps { data: HetRow[] @@ -67,13 +64,6 @@ export function RateBarChart(props: RateBarChartProps) { ? sortForVegaByIncome(props.data) : props.data - const wrappedLabels = useMemo(() => { - return processedData.map((d) => ({ - original: d[props.demographicType], - lines: wrapLabel(d[props.demographicType], maxLabelWidth), - })) - }, [processedData, props.demographicType]) - const allIndex = processedData.findIndex( (d) => d[props.demographicType] === 'All', ) @@ -104,12 +94,6 @@ export function RateBarChart(props: RateBarChartProps) { return position } - const barLabelBreakpoint = useMemo(() => { - const maxValue = - max(processedData, (d) => d[props.metricConfig.metricId]) || 0 - return maxValue * (LABEL_SWAP_CUTOFF_PERCENT / 100) - }, [processedData, props.metricConfig.metricId]) - return (
- {/* Y-axis DemographicType label */} - {isSmAndUp && ( - - - {DEMOGRAPHIC_DISPLAY_TYPES_LOWER_CASE[props.demographicType]} - - - )} - - + - {/* Bars */} - {processedData.map((d, index) => { - const barWidth = xScale(d[props.metricConfig.metricId]) || 0 - const shouldLabelBeInside = - d[props.metricConfig.metricId] > barLabelBreakpoint - const yPosition = getYPosition(index, d[props.demographicType]) - - const barLabelColor = - shouldLabelBeInside && d[props.demographicType] !== 'All' - ? 'fill-white' - : 'fill-current' - - const roundedBarString = buildRoundedBarString(barWidth, yScale) - - if (!roundedBarString) return <> - - const barAriaLabel = `${d[props.demographicType]}: ${d[props.metricConfig.metricId]} ${props.metricConfig.shortLabel}` - - return ( - handleTooltip(e, d, false)} - onMouseLeave={closeTooltip} - onTouchStart={(e) => { - handleTooltip(e, d, true) - }} - > - - - - ) - })} + yScale: ScaleBand + getYPosition: (index: number, label: string) => number + isTinyAndUp: boolean + handleTooltip: any + closeTooltip: any +} + +export default function RoundedBarsWithLabels( + props: RoundedBarsWithLabelsProps, +) { + const barLabelBreakpoint = useMemo(() => { + const maxValue = + max(props.processedData, (d) => d[props.metricConfig.metricId]) || 0 + return maxValue * (LABEL_SWAP_CUTOFF_PERCENT / 100) + }, [props.processedData, props.metricConfig.metricId]) + + return props.processedData.map((d, index) => { + const barWidth = props.xScale(d[props.metricConfig.metricId]) || 0 + const shouldLabelBeInside = + d[props.metricConfig.metricId] > barLabelBreakpoint + const yPosition = props.getYPosition(index, d[props.demographicType]) + + const barLabelColor = + shouldLabelBeInside && d[props.demographicType] !== 'All' + ? 'fill-white' + : 'fill-current' + + const roundedBarString = buildRoundedBarString(barWidth, props.yScale) + + if (!roundedBarString) return <> + + const barAriaLabel = `${d[props.demographicType]}: ${d[props.metricConfig.metricId]} ${props.metricConfig.shortLabel}` + + return ( + props.handleTooltip(e, d, false)} + onMouseLeave={props.closeTooltip} + onTouchStart={(e) => { + props.handleTooltip(e, d, true) + }} + > + + + + ) + }) +} diff --git a/frontend/src/charts/rateBarChart/YAxis.tsx b/frontend/src/charts/rateBarChart/YAxis.tsx new file mode 100644 index 0000000000..ddd3b6690c --- /dev/null +++ b/frontend/src/charts/rateBarChart/YAxis.tsx @@ -0,0 +1,54 @@ +import type { ScaleBand } from 'd3' +import { useMemo } from 'react' +import { + type DemographicType, + DEMOGRAPHIC_DISPLAY_TYPES_LOWER_CASE, +} from '../../data/query/Breakdowns' +import type { HetRow } from '../../data/utils/DatasetTypes' +import type { Fips } from '../../data/utils/Fips' +import { MARGIN, Y_AXIS_LABEL_HEIGHT } from './constants' +import GroupLabelsYAxis from './GroupLabelsYAxis' +import { wrapLabel } from './helpers' + +interface YAxisProps { + demographicType: DemographicType + isSmAndUp: boolean + processedData: HetRow[] + maxLabelWidth: number + yScale: ScaleBand + getYPosition: (index: number, label: string) => number + fips: Fips +} +export default function YAxis(props: YAxisProps) { + const wrappedLabels = useMemo(() => { + return props.processedData.map((d) => ({ + original: d[props.demographicType], + lines: wrapLabel(d[props.demographicType], props.maxLabelWidth), + })) + }, [props.processedData, props.demographicType]) + + return ( + + {props.isSmAndUp && ( + + + {DEMOGRAPHIC_DISPLAY_TYPES_LOWER_CASE[props.demographicType]} + + + )} + + + + ) +} diff --git a/frontend/src/charts/rateBarChart/helpers.ts b/frontend/src/charts/rateBarChart/helpers.ts index 4c830ecaa2..3451fa8a9c 100644 --- a/frontend/src/charts/rateBarChart/helpers.ts +++ b/frontend/src/charts/rateBarChart/helpers.ts @@ -1,3 +1,4 @@ +import type { ScaleBand } from 'd3' import type { MetricConfig } from '../../data/config/MetricConfigTypes' import { isPctType, isRateType } from '../../data/config/MetricConfigUtils' import type { DemographicType } from '../../data/query/Breakdowns' @@ -85,7 +86,7 @@ function getComparisonAllSubGroupLines( return lines } -function buildRoundedBarString(barWidth: number, yScale: any) { +function buildRoundedBarString(barWidth: number, yScale: ScaleBand) { const CORNER_RADIUS = 4 const safeBarWidth = Math.max(0, barWidth)