-
Notifications
You must be signed in to change notification settings - Fork 825
Charts: Add standalone Legend component #44245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a3155a6
7f488d8
eb072e6
301b80a
0348c8e
36fe909
f29b9c0
ad3bb4a
cf952ea
f384eb9
dbe4296
5da4e04
266da39
16b3593
6e8dc22
efbfd05
e786906
0541cde
53b138d
0ddf4db
a41fd43
d1ff1cc
2ee14c3
ff6cf8e
385b20d
073be67
6faf5d8
2b8e6da
f6fda18
fc3e9b2
71643f6
57d4fb1
bacb6c0
50cc059
da0a72f
35691cc
2b9e9e9
cd05c7b
1d3463e
291e343
5511c11
557729d
d129f52
973a98b
94cb119
c230f78
7feb72e
f5317aa
e932790
fd53a5f
c6a1d86
b4210b8
5a538fc
f46f875
d9dd0bd
0df684c
4964107
54c99cb
4b4bfde
75df5dc
701da72
bfb564a
cc07742
1f3b3d9
c59730d
7aecb7f
addfb32
1682004
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: minor | ||
Type: added | ||
|
||
Charts: adds a standalone chart legend component |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,12 @@ | ||
import { PatternLines, PatternCircles, PatternWaves, PatternHexagons } from '@visx/pattern'; | ||
import { Axis, BarSeries, BarGroup, Grid, XYChart } from '@visx/xychart'; | ||
import clsx from 'clsx'; | ||
import { useCallback, useId, useState, useRef, useMemo } from 'react'; | ||
import { useCallback, useContext, useId, useState, useRef, useMemo } from 'react'; | ||
import { ChartProvider, useChartId, useChartRegistration } from '../../providers/chart-context'; | ||
import { ChartContext } from '../../providers/chart-context/chart-context'; | ||
import { useChartTheme, useXYChartTheme } from '../../providers/theme'; | ||
import { Legend } from '../legend'; | ||
import { useChartLegendData } from '../legend/use-chart-legend-data'; | ||
import { useChartDataTransform } from '../shared/use-chart-data-transform'; | ||
import { useChartMargin } from '../shared/use-chart-margin'; | ||
import { useElementHeight } from '../shared/use-element-height'; | ||
|
@@ -66,10 +68,14 @@ const BarChartInternal: FC< BarChartProps > = ( { | |
// Generate a unique chart ID to avoid pattern conflicts with multiple charts | ||
const internalChartId = useId(); | ||
const chartId = useChartId( providedChartId ); | ||
const providerTheme = useChartTheme(); | ||
const theme = useXYChartTheme( data ); | ||
|
||
const dataSorted = useChartDataTransform( data ); | ||
|
||
// Create legend items using the reusable hook | ||
const legendItems = useChartLegendData( dataSorted, providerTheme ); | ||
|
||
const chartOptions = useBarChartOptions( dataSorted, horizontal, options ); | ||
const defaultMargin = useChartMargin( height, chartOptions, dataSorted, theme, horizontal ); | ||
const [ legendRef, legendHeight ] = useElementHeight< HTMLDivElement >(); | ||
|
@@ -222,24 +228,17 @@ const BarChartInternal: FC< BarChartProps > = ( { | |
const error = validateData( dataSorted ); | ||
const isDataValid = ! error; | ||
|
||
// Create legend items (hooks must be called in same order every render) | ||
const legendItems = useMemo( | ||
() => | ||
dataSorted.map( ( group, index ) => ( { | ||
label: group.label, // Label for each unique group | ||
value: '', // Empty string since we don't want to show a specific value | ||
color: getColor( group, index ), | ||
shapeStyle: group?.options?.legendShapeStyle, | ||
} ) ), | ||
[ dataSorted, getColor ] | ||
// Memoize metadata to prevent unnecessary re-registration | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's centralize the logic to |
||
const chartMetadata = useMemo( | ||
() => ( { | ||
orientation, | ||
withPatterns, | ||
} ), | ||
[ orientation, withPatterns ] | ||
); | ||
|
||
// Register chart with context only if data is valid | ||
const providerTheme = useChartTheme(); | ||
useChartRegistration( chartId, legendItems, providerTheme, 'bar', isDataValid, { | ||
orientation, | ||
withPatterns, | ||
} ); | ||
useChartRegistration( chartId, legendItems, providerTheme, 'bar', isDataValid, chartMetadata ); | ||
|
||
if ( error ) { | ||
return <div className={ clsx( 'bar-chart', styles[ 'bar-chart' ] ) }>{ error }</div>; | ||
|
@@ -347,17 +346,28 @@ const BarChartInternal: FC< BarChartProps > = ( { | |
className={ styles[ 'bar-chart__legend' ] } | ||
shape={ legendShape } | ||
ref={ legendRef } | ||
chartId={ chartId } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed |
||
/> | ||
) } | ||
</div> | ||
); | ||
}; | ||
|
||
const BarChart: FC< BarChartProps > = props => ( | ||
<ChartProvider> | ||
<BarChartInternal { ...props } /> | ||
</ChartProvider> | ||
); | ||
const BarChart: FC< BarChartProps > = props => { | ||
kangzj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const existingContext = useContext( ChartContext ); | ||
|
||
// If we're already in a ChartProvider context, don't create a new one | ||
if ( existingContext ) { | ||
return <BarChartInternal { ...props } />; | ||
} | ||
|
||
// Otherwise, create our own ChartProvider | ||
return ( | ||
<ChartProvider> | ||
<BarChartInternal { ...props } /> | ||
</ChartProvider> | ||
); | ||
}; | ||
|
||
BarChart.displayName = 'BarChart'; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,5 @@ | ||
export { BaseLegend as Legend } from './base-legend'; | ||
export type { LegendProps } from './types'; | ||
export { Legend } from './legend'; | ||
export { BaseLegend } from './base-legend'; | ||
export { useChartLegendData } from './use-chart-legend-data'; | ||
export type { LegendProps, BaseLegendProps } from './types'; | ||
export type { ChartLegendOptions } from './use-chart-legend-data'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { useContext, useMemo, forwardRef } from 'react'; | ||
import { ChartContext } from '../../providers/chart-context/chart-context'; | ||
import { BaseLegend } from './base-legend'; | ||
import type { LegendProps } from './types'; | ||
|
||
export const Legend = forwardRef< HTMLDivElement, LegendProps >( | ||
( { chartId, items, ...props }, ref ) => { | ||
// Get context but don't throw if it doesn't exist | ||
const context = useContext( ChartContext ); | ||
|
||
// Use useMemo to ensure re-rendering when context changes | ||
const contextItems = useMemo( () => { | ||
return chartId && context ? context.getChartData( chartId )?.legendItems : undefined; | ||
}, [ chartId, context ] ); | ||
|
||
// Use context items if available, otherwise fall back to provided items | ||
const legendItems = ( contextItems || items ) as typeof items; | ||
|
||
if ( ! legendItems ) { | ||
return null; | ||
} | ||
|
||
return <BaseLegend ref={ ref } items={ legendItems } { ...props } />; | ||
} | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default
showValues
istrue
, which will display number of data points next to each series, differing from the previous blank values. AddshowValues: false
to match existing behavior.Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default value for showValues is false as shown here