Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const useEditorPermissions = () => {
'Pie',
'Scatter Plot',
'Spark Line',
'Sankey'
'Sankey',
'Warming Stripes'
]

const visSupportsDateCategoryAxis = () => {
Expand Down Expand Up @@ -63,7 +64,8 @@ export const useEditorPermissions = () => {
'Forest Plot',
'Spark Line',
'Sankey',
'Bump Chart'
'Bump Chart',
'Warming Stripes'
]
if (disabledCharts.includes(visualizationType)) return false
return true
Expand All @@ -77,7 +79,8 @@ export const useEditorPermissions = () => {
'Forest Plot',
'Spark Line',
'Sankey',
'Bump Chart'
'Bump Chart',
'Warming Stripes'
]
if (disabledCharts.includes(visualizationType)) return false
return true
Expand All @@ -93,6 +96,8 @@ export const useEditorPermissions = () => {
return false
case 'Sankey':
return false
case 'Warming Stripes':
return false
default:
return true
}
Expand Down
30 changes: 24 additions & 6 deletions packages/chart/src/components/LinearChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import useIntersectionObserver from '../hooks/useIntersectionObserver'
import Regions from './Regions'
import CategoricalYAxis from './Axis/Categorical.Axis'
import BrushChart from './Brush/BrushController'
import WarmingStripes from './WarmingStripes'

// Helpers
import { isLegendWrapViewport, isMobileFontViewport } from '@cdc/core/helpers/viewports'
Expand Down Expand Up @@ -767,6 +768,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
showTooltip={showTooltip}
/>
)}
{visualizationType === 'Warming Stripes' && (
<WarmingStripes xScale={xScale} yScale={yScale} xMax={xMax} yMax={yMax} />
)}
{visualizationType === 'Box Plot' && config.orientation === 'vertical' && (
<BoxPlotVertical
seriesScale={seriesScale}
Expand Down Expand Up @@ -842,9 +846,16 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
)}
{/* Line chart */}
{/* TODO: Make this just line or combo? */}
{!['Paired Bar', 'Box Plot', 'Area Chart', 'Scatter Plot', 'Deviation Bar', 'Forecasting', 'Bar'].includes(
visualizationType
) &&
{![
'Paired Bar',
'Box Plot',
'Area Chart',
'Scatter Plot',
'Deviation Bar',
'Forecasting',
'Bar',
'Warming Stripes'
].includes(visualizationType) &&
!convertLineToBarGraph && (
<>
<LineChart
Expand Down Expand Up @@ -900,9 +911,16 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
{config.xAxis.brushActive && config.xAxis.type !== 'categorical' && <BrushChart xMax={xMax} yMax={yMax} />}
{/* Line chart */}
{/* TODO: Make this just line or combo? */}
{!['Paired Bar', 'Box Plot', 'Area Chart', 'Scatter Plot', 'Deviation Bar', 'Forecasting', 'Bar'].includes(
visualizationType
) &&
{![
'Paired Bar',
'Box Plot',
'Area Chart',
'Scatter Plot',
'Deviation Bar',
'Forecasting',
'Bar',
'Warming Stripes'
].includes(visualizationType) &&
!convertLineToBarGraph && (
<>
<LineChart
Expand Down
101 changes: 101 additions & 0 deletions packages/chart/src/components/WarmingStripes/WarmingStripes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { useContext, useState } from 'react'
import ConfigContext from '../../ConfigContext'
import { Group } from '@visx/group'
import { scaleSequential } from 'd3-scale'
// @ts-ignore - d3-scale-chromatic has no type declarations
import { interpolateRdBu } from 'd3-scale-chromatic'
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'

type WarmingStripesProps = {
xScale: any
yScale: any
xMax: number
yMax: number
}

const WarmingStripes = ({ xMax, yMax }: WarmingStripesProps) => {
const { transformedData: data, config, formatNumber, interactionLabel } = useContext(ConfigContext)

const [currentHover, setCurrentHover] = useState<number | null>(null)

// Get the data key for the temperature/anomaly values
// Use the first series key as the value column
const valueKey = config.runtime.seriesKeys?.[0]
const xAxisDataKey = config.runtime.originalXAxis?.dataKey || config.xAxis?.dataKey

if (!valueKey || !xAxisDataKey || !data || data.length === 0) {
return null
}

// Calculate the min and max values for the color scale
const values = data.map(d => Number(d[valueKey])).filter(v => !isNaN(v))
const minValue = Math.min(...values)
const maxValue = Math.max(...values)

// Create a sequential color scale from blue (cold) to red (hot)
// interpolateRdBu goes from red to blue, so we invert the domain
const colorScale = scaleSequential(interpolateRdBu).domain([maxValue, minValue])

// Calculate stripe width based on available space
const stripeWidth = xMax / data.length

const handleTooltip = (item: any) => {
const xValue = item[xAxisDataKey]
const yValue = item[valueKey]
const formattedValue = formatNumber(yValue, 'left')

return `<div>
<strong>${config.xAxis.label || xAxisDataKey}:</strong> ${xValue}<br/>
<strong>${config.runtime.seriesLabels?.[valueKey] || valueKey}:</strong> ${formattedValue}
</div>`
}

return (
<Group className='warming-stripes' left={config.yAxis.size}>
{data.map((item, index) => {
const value = Number(item[valueKey])
if (isNaN(value)) return null

const xPosition = index * stripeWidth
const fillColor = colorScale(value) as unknown as string

return (
<rect
key={`stripe-${index}`}
x={xPosition}
y={0}
width={stripeWidth}
height={yMax}
fill={fillColor}
stroke='none'
data-tooltip-html={handleTooltip(item)}
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
tabIndex={-1}
onMouseEnter={() => {
if (currentHover !== index) {
const xValue = item[xAxisDataKey]
const yValue = item[valueKey]

publishAnalyticsEvent({
vizType: config?.type,
vizSubType: getVizSubType(config),
eventType: 'chart_hover',
eventAction: 'hover',
eventLabel: interactionLabel || 'unknown',
vizTitle: getVizTitle(config),
series: valueKey,
specifics: `${xAxisDataKey}: ${xValue}, ${valueKey}: ${yValue}`
})
setCurrentHover(index)
}
}}
onMouseLeave={() => setCurrentHover(null)}
/>
)
})}
</Group>
)
}

export default WarmingStripes
3 changes: 3 additions & 0 deletions packages/chart/src/components/WarmingStripes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import WarmingStripes from './WarmingStripes'

export default WarmingStripes
1 change: 1 addition & 0 deletions packages/chart/src/types/ChartConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type VisualizationType =
| 'Forecasting'
| 'Sankey'
| 'Bump Chart'
| 'Warming Stripes'
export interface PreliminaryDataItem {
column: string
displayLegend: boolean
Expand Down
13 changes: 13 additions & 0 deletions packages/core/assets/icon-warming-stripes.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions packages/editor/src/components/ChooseTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import SankeyIcon from '@cdc/core/assets/icon-sankey.svg'
import ComboChartIcon from '@cdc/core/assets/icon-combo-chart.svg'
import EpiChartIcon from '@cdc/core/assets/icon-epi-chart.svg'
import TableIcon from '@cdc/core/assets/icon-table.svg'
import WarmingStripesIcon from '@cdc/core/assets/icon-warming-stripes.svg'
import Icon from '@cdc/core/components/ui/Icon'

import {
Expand Down Expand Up @@ -434,6 +435,16 @@ const buttons = [
icon: <DeviationIcon />,
content: 'Use deviation bars to display how individual values differ from a target'
},
{
id: 25,
category: 'Charts',
label: 'Warming Stripes',
type: 'chart',
subType: 'Warming Stripes',
orientation: 'vertical',
icon: <WarmingStripesIcon />,
content: 'Display temperature anomalies over time using color-coded vertical stripes.'
},
{
id: 15,
category: 'General',
Expand Down
Loading