1- import React from 'react' ;
1+ import React , { useMemo } from 'react' ;
22import PropTypes from 'prop-types' ;
3- import { AreaChart as PfAreaChart } from 'patternfly-react' ;
4- import { getAreaChartConfig } from '../../../../../services/charts/AreaChartService' ;
3+ import {
4+ Chart ,
5+ ChartArea ,
6+ ChartAxis ,
7+ ChartGroup ,
8+ ChartStack ,
9+ ChartThemeColor ,
10+ ChartVoronoiContainer ,
11+ ChartLegend ,
12+ } from '@patternfly/react-charts' ;
513import { noop } from '../../../../common/helpers' ;
614import { translate as __ } from '../../../../common/I18n' ;
715import MessageBox from '../../MessageBox' ;
@@ -11,35 +19,187 @@ const AreaChart = ({
1119 onclick,
1220 noDataMsg,
1321 config,
14- unloadData,
22+ unloadData, // eslint-disable-line no-unused-vars
1523 xAxisDataLabel,
1624 yAxisLabel,
1725 size,
1826} ) => {
19- const chartConfig = getAreaChartConfig ( {
20- data,
21- config,
22- onclick,
23- yAxisLabel,
24- xAxisDataLabel,
25- size,
26- } ) ;
27-
28- if ( chartConfig . data . columns . length ) {
29- return < PfAreaChart { ...chartConfig } unloadBeforeLoad = { unloadData } /> ;
27+ const chartData = useMemo ( ( ) => {
28+ if ( ! data || data . length === 0 ) {
29+ return null ;
30+ }
31+
32+ // Extract timestamps from first array
33+ const timeColumn = data . find ( col => col [ 0 ] === xAxisDataLabel ) ;
34+ if ( ! timeColumn ) {
35+ return null ;
36+ }
37+
38+ const timestamps = timeColumn . slice ( 1 ) ;
39+ const dates = timestamps . map ( epochSecs => new Date ( epochSecs * 1000 ) ) ;
40+
41+ // Process other data columns
42+ const series = data
43+ . filter ( col => col [ 0 ] !== xAxisDataLabel )
44+ . map ( col => {
45+ const name = col [ 0 ] ;
46+ const values = col . slice ( 1 ) ;
47+ return {
48+ name,
49+ data : values . map ( ( value , index ) => ( {
50+ x : dates [ index ] ,
51+ y : value ,
52+ name,
53+ } ) ) ,
54+ } ;
55+ } ) ;
56+
57+ return series . length > 0 ? series : null ;
58+ } , [ data , xAxisDataLabel ] ) ;
59+
60+ const handleClick = useMemo (
61+ ( ) => ( event , datum ) => {
62+ if ( onclick && datum && datum . name ) {
63+ onclick ( { id : datum . name } ) ;
64+ }
65+ } ,
66+ [ onclick ]
67+ ) ;
68+
69+ if ( ! chartData ) {
70+ return < MessageBox msg = { noDataMsg } icontype = "info" /> ;
3071 }
31- return < MessageBox msg = { noDataMsg } icontype = "info" /> ;
72+
73+ const chartHeight = size ?. height || 250 ;
74+ const chartWidth = size ?. width || undefined ;
75+ const padding = { bottom : 75 , left : 75 , right : 50 , top : 50 } ;
76+
77+ const legendData = chartData . map ( series => ( { name : series . name } ) ) ;
78+
79+ return (
80+ < div style = { { height : chartHeight , width : chartWidth || '100%' } } >
81+ < Chart
82+ ariaDesc = "Area chart"
83+ ariaTitle = { yAxisLabel || 'Area chart' }
84+ containerComponent = {
85+ < ChartVoronoiContainer
86+ labels = { ( { datum } ) => {
87+ const value = datum . y0 !== undefined ? datum . y - datum . y0 : datum . y ;
88+ const seriesName = datum . childName || datum . name ;
89+
90+ // Show timestamp for the first series
91+ const firstSeriesName = chartData [ 0 ] ?. name ;
92+ const isFirstSeries = seriesName === firstSeriesName ;
93+
94+ let label = '' ;
95+
96+ // Add timestamp as first line for first series only
97+ if ( isFirstSeries && datum . x ) {
98+ const date = datum . x instanceof Date ? datum . x : new Date ( datum . x ) ;
99+ const dateStr = date . toLocaleDateString ( undefined , { month : 'numeric' , day : 'numeric' } ) ;
100+ const timeStr = date . toLocaleTimeString ( undefined , { hour : 'numeric' , minute : '2-digit' , hour12 : true } ) ;
101+ label = `${ dateStr } , ${ timeStr } \n` ;
102+ }
103+
104+ // Add series value if non-zero
105+ if ( value !== 0 ) {
106+ const valueLine = seriesName ? `${ seriesName } : ${ Math . round ( value ) } ` : `${ Math . round ( value ) } ` ;
107+ label += valueLine ;
108+ } else if ( isFirstSeries ) {
109+ // Show first series even if zero to ensure timestamp displays
110+ const valueLine = seriesName ? `${ seriesName } : ${ Math . round ( value ) } ` : `${ Math . round ( value ) } ` ;
111+ label += valueLine ;
112+ }
113+
114+ return label || null ;
115+ } }
116+ constrainToVisibleArea
117+ />
118+ }
119+ height = { chartHeight }
120+ width = { chartWidth }
121+ padding = { padding }
122+ themeColor = { ChartThemeColor . orange }
123+ legendData = { legendData }
124+ legendOrientation = "horizontal"
125+ legendPosition = "bottom"
126+ legendComponent = { < ChartLegend /> }
127+ events = { [
128+ {
129+ target : 'data' ,
130+ eventHandlers : {
131+ onClick : ( ) => [
132+ {
133+ target : 'data' ,
134+ mutation : props => {
135+ handleClick ( null , props . datum ) ;
136+ return null ;
137+ } ,
138+ } ,
139+ ] ,
140+ } ,
141+ } ,
142+ ] }
143+ >
144+ < ChartAxis
145+ tickFormat = { t => {
146+ if ( config === 'timeseries' && t instanceof Date ) {
147+ return t . toLocaleDateString ( undefined , {
148+ month : 'short' ,
149+ day : 'numeric' ,
150+ } ) ;
151+ }
152+ return t ;
153+ } }
154+ fixLabelOverlap
155+ />
156+ < ChartAxis
157+ dependentAxis
158+ showGrid
159+ label = { yAxisLabel }
160+ tickFormat = { t => Math . round ( t ) }
161+ />
162+ { config === 'timeseries' ? (
163+ < ChartStack >
164+ { chartData . map ( series => (
165+ < ChartArea key = { series . name } data = { series . data } name = { series . name } />
166+ ) ) }
167+ </ ChartStack >
168+ ) : (
169+ < ChartGroup >
170+ { chartData . map ( series => (
171+ < ChartArea key = { series . name } data = { series . data } name = { series . name } />
172+ ) ) }
173+ </ ChartGroup >
174+ ) }
175+ </ Chart >
176+ </ div >
177+ ) ;
32178} ;
33179
34180AreaChart . propTypes = {
181+ /** Array of data arrays. First element of each array is the label, rest are values.
182+ * For timeseries, one array should have xAxisDataLabel as first element and timestamps as subsequent values.
183+ * Example: [['time', 1614449768, 1614451500], ['CentOS 7.9', 3, 5], ['CentOS 7.6', 2, 1]]
184+ */
35185 data : PropTypes . arrayOf ( PropTypes . array ) ,
186+ /** Function called when clicking on data points. Receives object with 'id' property containing the series name. */
36187 onclick : PropTypes . func ,
188+ /** Message to display when no data is available */
37189 noDataMsg : PropTypes . string ,
190+ /** Chart configuration type. Only 'timeseries' is currently supported. */
38191 config : PropTypes . oneOf ( [ 'timeseries' ] ) ,
192+ /** Legacy prop for compatibility - no longer used in PatternFly 5 */
39193 unloadData : PropTypes . bool ,
194+ /** Label for the x-axis data column (must match first element of one data array) */
40195 xAxisDataLabel : PropTypes . string ,
196+ /** Label for the y-axis */
41197 yAxisLabel : PropTypes . string ,
42- size : PropTypes . object ,
198+ /** Optional size object with width and/or height properties */
199+ size : PropTypes . shape ( {
200+ height : PropTypes . number ,
201+ width : PropTypes . number ,
202+ } ) ,
43203} ;
44204
45205AreaChart . defaultProps = {
0 commit comments