Skip to content

Commit

Permalink
extract more components
Browse files Browse the repository at this point in the history
  • Loading branch information
benhammondmusic committed Oct 11, 2024
1 parent 8ead7c4 commit 22e16ae
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 87 deletions.
3 changes: 2 additions & 1 deletion frontend/src/charts/rateBarChart/EndOfBarLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ScaleBand } from 'd3'
import type { MetricConfig } from '../../data/config/MetricConfigTypes'
import { formatValue } from './helpers'

Expand All @@ -6,7 +7,7 @@ interface EndOfBarLabelProps {
d: Record<string, any>
shouldLabelBeInside: boolean
barWidth: number
yScale: any
yScale: ScaleBand<string>
barLabelColor: string
isTinyAndUp: boolean
}
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/charts/rateBarChart/GroupLabelsYAxis.tsx
Original file line number Diff line number Diff line change
@@ -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<string>
getYPosition: (index: number, label: string) => number
}

Expand Down
102 changes: 19 additions & 83 deletions frontend/src/charts/rateBarChart/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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[]
Expand Down Expand Up @@ -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',
)
Expand Down Expand Up @@ -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 (
<div
ref={containerRef}
Expand All @@ -129,75 +113,27 @@ export function RateBarChart(props: RateBarChartProps) {
height={innerHeight}
xScale={xScale}
/>
{/* Y-axis DemographicType label */}
{isSmAndUp && (
<g>
<text
transform={`translate(${-MARGIN.left + Y_AXIS_LABEL_HEIGHT},${innerHeight / 2}) rotate(-90)`}
textAnchor='middle'
className='text-smallest font-semibold p-0 m-0'
aria-label={'Y Axis Label'}
>
{DEMOGRAPHIC_DISPLAY_TYPES_LOWER_CASE[props.demographicType]}
</text>
</g>
)}

<GroupLabelsYAxis
<RoundedBarsWithLabels
{...props}
wrappedLabels={wrappedLabels}
processedData={processedData}
metricConfig={props.metricConfig}
demographicType={props.demographicType}
xScale={xScale}
yScale={yScale}
getYPosition={getYPosition}
isTinyAndUp={isTinyAndUp}
handleTooltip={handleTooltip}
closeTooltip={closeTooltip}
/>
<YAxis
yScale={yScale}
demographicType={props.demographicType}
isSmAndUp={isSmAndUp}
processedData={processedData}
maxLabelWidth={maxLabelWidth}
getYPosition={getYPosition}
fips={props.fips}
/>
{/* 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 (
<g
key={index}
transform={`translate(0,${yPosition})`}
onMouseMove={(e) => handleTooltip(e, d, false)}
onMouseLeave={closeTooltip}
onTouchStart={(e) => {
handleTooltip(e, d, true)
}}
>
<path
d={roundedBarString}
className={
d[props.demographicType] === 'All'
? 'fill-timeYellow'
: 'fill-altGreen'
}
aria-label={barAriaLabel}
/>
<EndOfBarLabel
{...props}
d={d}
shouldLabelBeInside={shouldLabelBeInside}
barWidth={barWidth}
yScale={yScale}
barLabelColor={barLabelColor}
isTinyAndUp={isTinyAndUp}
/>
</g>
)
})}
<XAxis
metricConfig={props.metricConfig}
xScale={xScale}
Expand Down
80 changes: 80 additions & 0 deletions frontend/src/charts/rateBarChart/RoundedBarsWithLabels.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { max, type ScaleBand, type ScaleLinear } from 'd3'
import { useMemo } from 'react'
import type { MetricConfig } from '../../data/config/MetricConfigTypes'
import type { DemographicType } from '../../data/query/Breakdowns'
import type { HetRow } from '../../data/utils/DatasetTypes'
import { LABEL_SWAP_CUTOFF_PERCENT } from './constants'
import EndOfBarLabel from './EndOfBarLabel'
import { buildRoundedBarString } from './helpers'

interface RoundedBarsWithLabelsProps {
processedData: HetRow[]
metricConfig: MetricConfig
demographicType: DemographicType
xScale: ScaleLinear<number, number>
yScale: ScaleBand<string>
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 (
<g
key={index + barAriaLabel}
transform={`translate(0,${yPosition})`}
onMouseMove={(e) => props.handleTooltip(e, d, false)}
onMouseLeave={props.closeTooltip}
onTouchStart={(e) => {
props.handleTooltip(e, d, true)
}}
>
<path
d={roundedBarString}
key={'path' + index + barAriaLabel}
className={
d[props.demographicType] === 'All'
? 'fill-timeYellow'
: 'fill-altGreen'
}
aria-label={barAriaLabel}
/>
<EndOfBarLabel
{...props}
d={d}
shouldLabelBeInside={shouldLabelBeInside}
barWidth={barWidth}
yScale={props.yScale}
barLabelColor={barLabelColor}
isTinyAndUp={props.isTinyAndUp}
/>
</g>
)
})
}
54 changes: 54 additions & 0 deletions frontend/src/charts/rateBarChart/YAxis.tsx
Original file line number Diff line number Diff line change
@@ -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<string>
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 (
<g>
{props.isSmAndUp && (
<g>
<text
transform={`translate(${-MARGIN.left + Y_AXIS_LABEL_HEIGHT},${innerHeight / 2}) rotate(-90)`}
textAnchor='middle'
className='text-smallest font-semibold p-0 m-0'
aria-label={'Y Axis Label'}
>
{DEMOGRAPHIC_DISPLAY_TYPES_LOWER_CASE[props.demographicType]}
</text>
</g>
)}

<GroupLabelsYAxis
{...props}
wrappedLabels={wrappedLabels}
yScale={props.yScale}
getYPosition={props.getYPosition}
fips={props.fips}
/>
</g>
)
}
3 changes: 2 additions & 1 deletion frontend/src/charts/rateBarChart/helpers.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -85,7 +86,7 @@ function getComparisonAllSubGroupLines(
return lines
}

function buildRoundedBarString(barWidth: number, yScale: any) {
function buildRoundedBarString(barWidth: number, yScale: ScaleBand<string>) {
const CORNER_RADIUS = 4

const safeBarWidth = Math.max(0, barWidth)
Expand Down

0 comments on commit 22e16ae

Please sign in to comment.