@@ -94,6 +94,126 @@ const getVerticalAxes = (nameKey, tickColor, xLabel, yDomain, yTicks, valueForma
9494 ) ,
9595} ) ;
9696
97+ const getChartAxes = ( {
98+ isHorizontal,
99+ tickColor,
100+ valueFormatter,
101+ nameKey,
102+ xLabel,
103+ yLabel,
104+ ...props
105+ } ) =>
106+ isHorizontal
107+ ? getHorizontalAxes ( {
108+ xDomain : props . xDomain ,
109+ xTicks : props . xTicks ,
110+ valueFormatter,
111+ tickColor,
112+ xLabel,
113+ nameKey,
114+ yCategoryWidth : props . yCategoryWidth ,
115+ yTickFormatter : props . yTickFormatter ,
116+ showYAxisTitle : props . showYAxisTitle ,
117+ yLabel,
118+ } )
119+ : getVerticalAxes (
120+ nameKey ,
121+ tickColor ,
122+ xLabel ,
123+ props . yDomain ,
124+ props . yTicks ,
125+ valueFormatter ,
126+ yLabel ,
127+ ) ;
128+
129+ const GraphHeader = ( { title, metricLabel, showMetricPill, headerChips, darkMode } ) => (
130+ < div
131+ className = { styles . graphTitle }
132+ style = { { display : 'flex' , alignItems : 'center' , color : darkMode ? '#e1e1e1' : undefined } }
133+ >
134+ < span style = { { flex : 1 } } > { title } </ span >
135+ { showMetricPill && (
136+ < span className = { styles . metricPill } style = { { marginRight : 12 } } >
137+ { metricLabel }
138+ </ span >
139+ ) }
140+
141+ < div style = { { display : 'flex' , gap : 16 } } >
142+ { headerChips . map ( ( chip , index ) => (
143+ < div key = { index } style = { { textAlign : 'center' , lineHeight : 1.1 } } >
144+ < div style = { { fontSize : 12 , fontWeight : 600 , color : darkMode ? '#e1e1e1' : undefined } } >
145+ { chip . label }
146+ </ div >
147+ < div style = { { fontSize : 11 , color : darkMode ? '#a0b0c8' : '#777' , letterSpacing : 0.2 } } >
148+ { String ( chip . value ) . toUpperCase ( ) }
149+ </ div >
150+ </ div >
151+ ) ) }
152+ </ div >
153+ </ div >
154+ ) ;
155+
156+ const NoDataMessage = ( { height, darkMode } ) => (
157+ < div
158+ style = { {
159+ height,
160+ display : 'flex' ,
161+ alignItems : 'center' ,
162+ justifyContent : 'center' ,
163+ color : darkMode ? '#e1e1e1' : '#666' ,
164+ } }
165+ >
166+ No data available
167+ </ div >
168+ ) ;
169+
170+ const CompareChart = ( {
171+ data,
172+ height,
173+ isHorizontal,
174+ margins,
175+ gridColor,
176+ axes,
177+ valueKey,
178+ barColor,
179+ barSize,
180+ valueFormatter,
181+ tooltipLabel,
182+ metricLabel,
183+ title,
184+ darkMode,
185+ } ) => (
186+ < ResponsiveContainer width = "100%" height = { height } >
187+ < BarChart data = { data } layout = { isHorizontal ? 'vertical' : 'horizontal' } margin = { margins } >
188+ < CartesianGrid strokeDasharray = "3 3" stroke = { gridColor } />
189+ { axes . xAxis }
190+ { axes . yAxis }
191+
192+ < Tooltip
193+ formatter = { value => [ valueFormatter ( value ) , tooltipLabel || metricLabel || title ] }
194+ labelFormatter = { label => `${ label } ` }
195+ contentStyle = { {
196+ background : darkMode ? '#1c2541' : '#fff' ,
197+ border : `1px solid ${ darkMode ? '#3a506b' : '#ccc' } ` ,
198+ color : darkMode ? '#e1e1e1' : '#333' ,
199+ } }
200+ itemStyle = { { color : darkMode ? '#e1e1e1' : '#333' } }
201+ labelStyle = { { color : darkMode ? '#e1e1e1' : '#333' , fontWeight : 600 } }
202+ />
203+
204+ < Bar dataKey = { valueKey } radius = { [ 4 , 4 , 4 , 4 ] } fill = { barColor } barSize = { barSize } >
205+ < LabelList
206+ dataKey = { valueKey }
207+ position = { isHorizontal ? 'right' : 'top' }
208+ formatter = { valueFormatter }
209+ style = { { fontSize : 15 , fontWeight : 600 } }
210+ offset = { 8 }
211+ />
212+ </ Bar >
213+ </ BarChart >
214+ </ ResponsiveContainer >
215+ ) ;
216+
97217export function CompareBarGraph ( {
98218 title,
99219 metricLabel,
@@ -106,7 +226,7 @@ export function CompareBarGraph({
106226 xLabel,
107227 yLabel,
108228 barColor = '#3b82f6' ,
109- valueFormatter = v => v ,
229+ valueFormatter = value => value ,
110230 headerChips = [ ] ,
111231 xDomain,
112232 yDomain,
@@ -124,31 +244,22 @@ export function CompareBarGraph({
124244 const tickColor = darkMode ? '#e1e1e1' : '#444' ;
125245 const gridColor = darkMode ? '#3a506b' : '#e0e0e0' ;
126246
127- const horizontalAxes = getHorizontalAxes ( {
128- xDomain,
129- xTicks,
130- valueFormatter,
247+ const axes = getChartAxes ( {
248+ isHorizontal,
131249 tickColor,
132- xLabel ,
250+ valueFormatter ,
133251 nameKey,
252+ xLabel,
253+ yLabel,
254+ xDomain,
255+ yDomain,
256+ xTicks,
257+ yTicks,
134258 yCategoryWidth,
135259 yTickFormatter,
136260 showYAxisTitle,
137- yLabel,
138261 } ) ;
139262
140- const verticalAxes = getVerticalAxes (
141- nameKey ,
142- tickColor ,
143- xLabel ,
144- yDomain ,
145- yTicks ,
146- valueFormatter ,
147- yLabel ,
148- ) ;
149-
150- const renderedAxes = isHorizontal ? horizontalAxes : verticalAxes ;
151-
152263 return (
153264 < Card
154265 className = { `${ styles . graphCard } ${ darkMode ? styles . darkCard : '' } ` }
@@ -157,97 +268,84 @@ export function CompareBarGraph({
157268 }
158269 >
159270 < CardBody className = { `${ styles . graphCardBody } ${ darkMode ? styles . darkCardBody : '' } ` } >
160- { /* Title row + chips */ }
161- < div
162- className = { styles . graphTitle }
163- style = { { display : 'flex' , alignItems : 'center' , color : darkMode ? '#e1e1e1' : undefined } }
164- >
165- < span style = { { flex : 1 } } > { title } </ span >
166- { showMetricPill && (
167- < span className = { styles . metricPill } style = { { marginRight : 12 } } >
168- { metricLabel }
169- </ span >
170- ) }
171-
172- { /* chips on the right */ }
173- < div style = { { display : 'flex' , gap : 16 } } >
174- { headerChips . map ( ( c , i ) => (
175- < div key = { i } style = { { textAlign : 'center' , lineHeight : 1.1 } } >
176- < div
177- style = { { fontSize : 12 , fontWeight : 600 , color : darkMode ? '#e1e1e1' : undefined } }
178- >
179- { c . label }
180- </ div >
181- < div
182- style = { { fontSize : 11 , color : darkMode ? '#a0b0c8' : '#777' , letterSpacing : 0.2 } }
183- >
184- { String ( c . value ) . toUpperCase ( ) }
185- </ div >
186- </ div >
187- ) ) }
188- </ div >
189- </ div >
271+ < GraphHeader
272+ title = { title }
273+ metricLabel = { metricLabel }
274+ showMetricPill = { showMetricPill }
275+ headerChips = { headerChips }
276+ darkMode = { darkMode }
277+ />
190278
191- { /* chart */ }
192- < div
193- className = { styles . graphCanvas }
194- style = { {
195- width : '100%' ,
196- minHeight : `${ height } px` ,
197- } }
198- >
279+ < div className = { styles . graphCanvas } style = { { width : '100%' , minHeight : `${ height } px` } } >
199280 { data ?. length ? (
200- < ResponsiveContainer width = "100%" height = { height } >
201- < BarChart
202- data = { data }
203- layout = { isHorizontal ? 'vertical' : 'horizontal' }
204- margin = { margins }
205- >
206- < CartesianGrid strokeDasharray = "3 3" stroke = { gridColor } />
207- { renderedAxes . xAxis }
208- { renderedAxes . yAxis }
209-
210- < Tooltip
211- formatter = { v => [ valueFormatter ( v ) , tooltipLabel || metricLabel || title ] }
212- labelFormatter = { lbl => `${ lbl } ` }
213- contentStyle = { {
214- background : darkMode ? '#1c2541' : '#fff' ,
215- border : `1px solid ${ darkMode ? '#3a506b' : '#ccc' } ` ,
216- color : darkMode ? '#e1e1e1' : '#333' ,
217- } }
218- itemStyle = { { color : darkMode ? '#e1e1e1' : '#333' } }
219- labelStyle = { { color : darkMode ? '#e1e1e1' : '#333' , fontWeight : 600 } }
220- />
221- < Bar dataKey = { valueKey } radius = { [ 4 , 4 , 4 , 4 ] } fill = { barColor } barSize = { barSize } >
222- < LabelList
223- dataKey = { valueKey }
224- position = { isHorizontal ? 'right' : 'top' }
225- formatter = { valueFormatter }
226- style = { { fontSize : 15 , fontWeight : 600 } }
227- offset = { 8 }
228- />
229- </ Bar >
230- </ BarChart >
231- </ ResponsiveContainer >
281+ < CompareChart
282+ data = { data }
283+ height = { height }
284+ isHorizontal = { isHorizontal }
285+ margins = { margins }
286+ gridColor = { gridColor }
287+ axes = { axes }
288+ valueKey = { valueKey }
289+ barColor = { barColor }
290+ barSize = { barSize }
291+ valueFormatter = { valueFormatter }
292+ tooltipLabel = { tooltipLabel }
293+ metricLabel = { metricLabel }
294+ title = { title }
295+ darkMode = { darkMode }
296+ />
232297 ) : (
233- < div
234- style = { {
235- height,
236- display : 'flex' ,
237- alignItems : 'center' ,
238- justifyContent : 'center' ,
239- color : darkMode ? '#e1e1e1' : '#666' ,
240- } }
241- >
242- No data available
243- </ div >
298+ < NoDataMessage height = { height } darkMode = { darkMode } />
244299 ) }
245300 </ div >
246301 </ CardBody >
247302 </ Card >
248303 ) ;
249304}
250305
306+ GraphHeader . propTypes = {
307+ title : PropTypes . string . isRequired ,
308+ metricLabel : PropTypes . string ,
309+ showMetricPill : PropTypes . bool . isRequired ,
310+ headerChips : PropTypes . arrayOf (
311+ PropTypes . shape ( {
312+ label : PropTypes . string . isRequired ,
313+ value : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . number ] ) . isRequired ,
314+ } ) ,
315+ ) . isRequired ,
316+ darkMode : PropTypes . bool . isRequired ,
317+ } ;
318+
319+ NoDataMessage . propTypes = {
320+ height : PropTypes . number . isRequired ,
321+ darkMode : PropTypes . bool . isRequired ,
322+ } ;
323+
324+ CompareChart . propTypes = {
325+ data : PropTypes . arrayOf ( PropTypes . object ) . isRequired ,
326+ height : PropTypes . number . isRequired ,
327+ isHorizontal : PropTypes . bool . isRequired ,
328+ margins : PropTypes . shape ( {
329+ top : PropTypes . number ,
330+ right : PropTypes . number ,
331+ bottom : PropTypes . number ,
332+ left : PropTypes . number ,
333+ } ) . isRequired ,
334+ gridColor : PropTypes . string . isRequired ,
335+ axes : PropTypes . shape ( {
336+ xAxis : PropTypes . node . isRequired ,
337+ yAxis : PropTypes . node . isRequired ,
338+ } ) . isRequired ,
339+ valueKey : PropTypes . string . isRequired ,
340+ barColor : PropTypes . string . isRequired ,
341+ barSize : PropTypes . number ,
342+ valueFormatter : PropTypes . func . isRequired ,
343+ tooltipLabel : PropTypes . string ,
344+ metricLabel : PropTypes . string ,
345+ title : PropTypes . string . isRequired ,
346+ darkMode : PropTypes . bool . isRequired ,
347+ } ;
348+
251349CompareBarGraph . propTypes = {
252350 title : PropTypes . string . isRequired ,
253351 metricLabel : PropTypes . string ,
0 commit comments