Skip to main content

oxidize_pdf/charts/
dashboard_integration.rs

1//! Dashboard Integration for Charts
2//!
3//! This module provides wrappers that allow chart components (BarChart, PieChart, LineChart)
4//! to be used within the dashboard framework by implementing the DashboardComponent trait.
5
6use super::{BarChart, LineChart, PieChart};
7use crate::dashboard::{ComponentPosition, ComponentSpan, DashboardComponent, DashboardTheme};
8use crate::error::PdfError;
9use crate::page::Page;
10
11/// Wrapper for BarChart that implements DashboardComponent
12#[derive(Debug, Clone)]
13pub struct DashboardBarChart {
14    chart: BarChart,
15    span: ComponentSpan,
16}
17
18impl DashboardBarChart {
19    /// Create a new dashboard bar chart
20    pub fn new(chart: BarChart) -> Self {
21        Self {
22            chart,
23            span: ComponentSpan::new(6), // Half-width by default
24        }
25    }
26
27    /// Set the column span
28    pub fn span(mut self, columns: u8) -> Self {
29        self.span = ComponentSpan::new(columns);
30        self
31    }
32
33    /// Get a reference to the underlying chart
34    pub fn chart(&self) -> &BarChart {
35        &self.chart
36    }
37
38    /// Get a mutable reference to the underlying chart
39    pub fn chart_mut(&mut self) -> &mut BarChart {
40        &mut self.chart
41    }
42}
43
44impl DashboardComponent for DashboardBarChart {
45    fn render(
46        &self,
47        page: &mut Page,
48        position: ComponentPosition,
49        _theme: &DashboardTheme,
50    ) -> Result<(), PdfError> {
51        // Apply padding to the position
52        let padded = position.with_padding(10.0);
53
54        // Use the chart's existing rendering via ChartExt
55        use crate::charts::ChartExt;
56        page.add_bar_chart(&self.chart, padded.x, padded.y, padded.width, padded.height)
57    }
58
59    fn get_span(&self) -> ComponentSpan {
60        self.span
61    }
62
63    fn set_span(&mut self, span: ComponentSpan) {
64        self.span = span;
65    }
66
67    fn preferred_height(&self, _available_width: f64) -> f64 {
68        250.0 // Reasonable default for bar charts
69    }
70
71    fn minimum_width(&self) -> f64 {
72        200.0
73    }
74
75    fn estimated_render_time_ms(&self) -> u32 {
76        20 + (self.chart.data.len() as u32 * 2) // Base + bars
77    }
78
79    fn estimated_memory_mb(&self) -> f64 {
80        0.2 + (self.chart.data.len() as f64 * 0.01)
81    }
82
83    fn complexity_score(&self) -> u8 {
84        let base_score = 30;
85        let data_complexity = (self.chart.data.len() / 5).min(20) as u8;
86        let feature_score =
87            if self.chart.show_grid { 10 } else { 0 } + if self.chart.show_values { 5 } else { 0 };
88
89        (base_score + data_complexity + feature_score).min(100)
90    }
91
92    fn component_type(&self) -> &'static str {
93        "BarChart"
94    }
95}
96
97/// Wrapper for PieChart that implements DashboardComponent
98#[derive(Debug, Clone)]
99pub struct DashboardPieChart {
100    chart: PieChart,
101    span: ComponentSpan,
102}
103
104impl DashboardPieChart {
105    /// Create a new dashboard pie chart
106    pub fn new(chart: PieChart) -> Self {
107        Self {
108            chart,
109            span: ComponentSpan::new(6), // Half-width by default
110        }
111    }
112
113    /// Set the column span
114    pub fn span(mut self, columns: u8) -> Self {
115        self.span = ComponentSpan::new(columns);
116        self
117    }
118
119    /// Get a reference to the underlying chart
120    pub fn chart(&self) -> &PieChart {
121        &self.chart
122    }
123
124    /// Get a mutable reference to the underlying chart
125    pub fn chart_mut(&mut self) -> &mut PieChart {
126        &mut self.chart
127    }
128}
129
130impl DashboardComponent for DashboardPieChart {
131    fn render(
132        &self,
133        page: &mut Page,
134        position: ComponentPosition,
135        _theme: &DashboardTheme,
136    ) -> Result<(), PdfError> {
137        // Calculate center position and radius based on available space
138        let padded = position.with_padding(20.0);
139        let radius = (padded.width.min(padded.height) / 2.0) - 20.0;
140        let center_x = padded.x + padded.width / 2.0;
141        let center_y = padded.y + padded.height / 2.0;
142
143        // Use the chart's existing rendering via ChartExt
144        use crate::charts::ChartExt;
145        page.add_pie_chart(&self.chart, center_x, center_y, radius)
146    }
147
148    fn get_span(&self) -> ComponentSpan {
149        self.span
150    }
151
152    fn set_span(&mut self, span: ComponentSpan) {
153        self.span = span;
154    }
155
156    fn preferred_height(&self, _available_width: f64) -> f64 {
157        250.0 // Square aspect for pie charts
158    }
159
160    fn minimum_width(&self) -> f64 {
161        200.0
162    }
163
164    fn estimated_render_time_ms(&self) -> u32 {
165        25 + (self.chart.segments.len() as u32 * 3) // Segments are more complex
166    }
167
168    fn estimated_memory_mb(&self) -> f64 {
169        0.15 + (self.chart.segments.len() as f64 * 0.02)
170    }
171
172    fn complexity_score(&self) -> u8 {
173        let base_score = 35; // Pie charts are slightly more complex than bar charts
174        let segment_complexity = (self.chart.segments.len() / 3).min(25) as u8;
175        let feature_score = if self.chart.show_percentages { 5 } else { 0 };
176
177        (base_score + segment_complexity + feature_score).min(100)
178    }
179
180    fn component_type(&self) -> &'static str {
181        "PieChart"
182    }
183}
184
185/// Wrapper for LineChart that implements DashboardComponent
186#[derive(Debug, Clone)]
187pub struct DashboardLineChart {
188    chart: LineChart,
189    span: ComponentSpan,
190}
191
192impl DashboardLineChart {
193    /// Create a new dashboard line chart
194    pub fn new(chart: LineChart) -> Self {
195        Self {
196            chart,
197            span: ComponentSpan::new(6), // Half-width by default
198        }
199    }
200
201    /// Set the column span
202    pub fn span(mut self, columns: u8) -> Self {
203        self.span = ComponentSpan::new(columns);
204        self
205    }
206
207    /// Get a reference to the underlying chart
208    pub fn chart(&self) -> &LineChart {
209        &self.chart
210    }
211
212    /// Get a mutable reference to the underlying chart
213    pub fn chart_mut(&mut self) -> &mut LineChart {
214        &mut self.chart
215    }
216}
217
218impl DashboardComponent for DashboardLineChart {
219    fn render(
220        &self,
221        page: &mut Page,
222        position: ComponentPosition,
223        _theme: &DashboardTheme,
224    ) -> Result<(), PdfError> {
225        // Apply padding to the position
226        let padded = position.with_padding(10.0);
227
228        // Use the chart's existing rendering via ChartExt
229        use crate::charts::ChartExt;
230        page.add_line_chart(&self.chart, padded.x, padded.y, padded.width, padded.height)
231    }
232
233    fn get_span(&self) -> ComponentSpan {
234        self.span
235    }
236
237    fn set_span(&mut self, span: ComponentSpan) {
238        self.span = span;
239    }
240
241    fn preferred_height(&self, _available_width: f64) -> f64 {
242        220.0 // Reasonable default for line charts
243    }
244
245    fn minimum_width(&self) -> f64 {
246        250.0 // Line charts need more width for x-axis
247    }
248
249    fn estimated_render_time_ms(&self) -> u32 {
250        let total_points: u32 = self.chart.series.iter().map(|s| s.data.len() as u32).sum();
251        30 + (total_points * 2) // Base + data points
252    }
253
254    fn estimated_memory_mb(&self) -> f64 {
255        let total_points = self
256            .chart
257            .series
258            .iter()
259            .map(|s| s.data.len())
260            .sum::<usize>();
261        0.25 + (total_points as f64 * 0.01)
262    }
263
264    fn complexity_score(&self) -> u8 {
265        let base_score = 40; // Line charts are more complex
266        let series_complexity = (self.chart.series.len() * 10).min(30) as u8;
267        let feature_score = if self.chart.show_grid { 10 } else { 0 };
268
269        (base_score + series_complexity + feature_score).min(100)
270    }
271
272    fn component_type(&self) -> &'static str {
273        "LineChart"
274    }
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280    use crate::charts::{BarChartBuilder, DataSeries, LineChartBuilder, PieChartBuilder};
281    use crate::graphics::Color;
282
283    // ==================== DashboardBarChart Tests ====================
284
285    #[test]
286    fn test_dashboard_bar_chart_creation() {
287        let chart = BarChartBuilder::new()
288            .simple_data(vec![10.0, 20.0, 30.0])
289            .build();
290
291        let dashboard_chart = DashboardBarChart::new(chart);
292        assert_eq!(dashboard_chart.get_span().columns, 6);
293        assert_eq!(dashboard_chart.component_type(), "BarChart");
294    }
295
296    #[test]
297    fn test_dashboard_bar_chart_span() {
298        let chart = BarChartBuilder::new().simple_data(vec![10.0, 20.0]).build();
299
300        let dashboard_chart = DashboardBarChart::new(chart).span(12);
301        assert_eq!(dashboard_chart.get_span().columns, 12);
302        assert!(dashboard_chart.get_span().is_full_width());
303    }
304
305    #[test]
306    fn test_dashboard_bar_chart_chart_accessor() {
307        let chart = BarChartBuilder::new()
308            .simple_data(vec![1.0, 2.0, 3.0])
309            .build();
310        let dashboard_chart = DashboardBarChart::new(chart);
311
312        assert_eq!(dashboard_chart.chart().data.len(), 3);
313    }
314
315    #[test]
316    fn test_dashboard_bar_chart_chart_mut() {
317        let chart = BarChartBuilder::new().simple_data(vec![1.0, 2.0]).build();
318        let mut dashboard_chart = DashboardBarChart::new(chart);
319
320        dashboard_chart.chart_mut().show_grid = true;
321        assert!(dashboard_chart.chart().show_grid);
322    }
323
324    #[test]
325    fn test_dashboard_bar_chart_set_span() {
326        let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
327        let mut dashboard_chart = DashboardBarChart::new(chart);
328
329        dashboard_chart.set_span(ComponentSpan::new(4));
330        assert_eq!(dashboard_chart.get_span().columns, 4);
331    }
332
333    #[test]
334    fn test_dashboard_bar_chart_preferred_height() {
335        let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
336        let dashboard_chart = DashboardBarChart::new(chart);
337
338        assert_eq!(dashboard_chart.preferred_height(500.0), 250.0);
339    }
340
341    #[test]
342    fn test_dashboard_bar_chart_minimum_width() {
343        let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
344        let dashboard_chart = DashboardBarChart::new(chart);
345
346        assert_eq!(dashboard_chart.minimum_width(), 200.0);
347    }
348
349    #[test]
350    fn test_dashboard_bar_chart_estimated_render_time() {
351        let chart = BarChartBuilder::new()
352            .simple_data(vec![10.0, 20.0, 30.0, 40.0, 50.0])
353            .build();
354        let dashboard_chart = DashboardBarChart::new(chart);
355
356        // 20 base + (5 bars * 2) = 30
357        assert_eq!(dashboard_chart.estimated_render_time_ms(), 30);
358    }
359
360    #[test]
361    fn test_dashboard_bar_chart_estimated_memory() {
362        let chart = BarChartBuilder::new()
363            .simple_data(vec![10.0, 20.0, 30.0])
364            .build();
365        let dashboard_chart = DashboardBarChart::new(chart);
366
367        // 0.2 base + (3 * 0.01) = 0.23
368        let expected = 0.2 + (3.0 * 0.01);
369        assert!((dashboard_chart.estimated_memory_mb() - expected).abs() < 0.001);
370    }
371
372    #[test]
373    fn test_dashboard_bar_chart_clone() {
374        let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
375        let dashboard_chart = DashboardBarChart::new(chart).span(8);
376        let cloned = dashboard_chart.clone();
377
378        assert_eq!(cloned.get_span().columns, 8);
379    }
380
381    #[test]
382    fn test_dashboard_bar_chart_debug() {
383        let chart = BarChartBuilder::new().simple_data(vec![10.0]).build();
384        let dashboard_chart = DashboardBarChart::new(chart);
385
386        let debug_str = format!("{:?}", dashboard_chart);
387        assert!(debug_str.contains("DashboardBarChart"));
388    }
389
390    // ==================== DashboardPieChart Tests ====================
391
392    #[test]
393    fn test_dashboard_pie_chart_creation() {
394        let chart = PieChartBuilder::new()
395            .simple_data(vec![25.0, 35.0, 40.0])
396            .build();
397
398        let dashboard_chart = DashboardPieChart::new(chart);
399        assert_eq!(dashboard_chart.component_type(), "PieChart");
400        assert!(dashboard_chart.complexity_score() > 30);
401    }
402
403    #[test]
404    fn test_dashboard_pie_chart_span() {
405        let chart = PieChartBuilder::new().simple_data(vec![50.0, 50.0]).build();
406        let dashboard_chart = DashboardPieChart::new(chart).span(4);
407
408        assert_eq!(dashboard_chart.get_span().columns, 4);
409    }
410
411    #[test]
412    fn test_dashboard_pie_chart_chart_accessor() {
413        let chart = PieChartBuilder::new()
414            .simple_data(vec![20.0, 30.0, 50.0])
415            .build();
416        let dashboard_chart = DashboardPieChart::new(chart);
417
418        assert_eq!(dashboard_chart.chart().segments.len(), 3);
419    }
420
421    #[test]
422    fn test_dashboard_pie_chart_chart_mut() {
423        let chart = PieChartBuilder::new().simple_data(vec![50.0, 50.0]).build();
424        let mut dashboard_chart = DashboardPieChart::new(chart);
425
426        dashboard_chart.chart_mut().show_percentages = true;
427        assert!(dashboard_chart.chart().show_percentages);
428    }
429
430    #[test]
431    fn test_dashboard_pie_chart_set_span() {
432        let chart = PieChartBuilder::new().simple_data(vec![100.0]).build();
433        let mut dashboard_chart = DashboardPieChart::new(chart);
434
435        dashboard_chart.set_span(ComponentSpan::new(3));
436        assert_eq!(dashboard_chart.get_span().columns, 3);
437    }
438
439    #[test]
440    fn test_dashboard_pie_chart_preferred_height() {
441        let chart = PieChartBuilder::new().simple_data(vec![50.0, 50.0]).build();
442        let dashboard_chart = DashboardPieChart::new(chart);
443
444        assert_eq!(dashboard_chart.preferred_height(400.0), 250.0);
445    }
446
447    #[test]
448    fn test_dashboard_pie_chart_minimum_width() {
449        let chart = PieChartBuilder::new().simple_data(vec![100.0]).build();
450        let dashboard_chart = DashboardPieChart::new(chart);
451
452        assert_eq!(dashboard_chart.minimum_width(), 200.0);
453    }
454
455    #[test]
456    fn test_dashboard_pie_chart_estimated_render_time() {
457        let chart = PieChartBuilder::new()
458            .simple_data(vec![25.0, 25.0, 25.0, 25.0])
459            .build();
460        let dashboard_chart = DashboardPieChart::new(chart);
461
462        // 25 base + (4 segments * 3) = 37
463        assert_eq!(dashboard_chart.estimated_render_time_ms(), 37);
464    }
465
466    #[test]
467    fn test_dashboard_pie_chart_estimated_memory() {
468        let chart = PieChartBuilder::new().simple_data(vec![50.0, 50.0]).build();
469        let dashboard_chart = DashboardPieChart::new(chart);
470
471        // 0.15 base + (2 * 0.02) = 0.19
472        let expected = 0.15 + (2.0 * 0.02);
473        assert!((dashboard_chart.estimated_memory_mb() - expected).abs() < 0.001);
474    }
475
476    #[test]
477    fn test_dashboard_pie_chart_complexity_with_percentages() {
478        let chart = PieChartBuilder::new()
479            .simple_data(vec![50.0, 50.0])
480            .show_percentages(true)
481            .build();
482        let dashboard_chart = DashboardPieChart::new(chart);
483
484        // Should include feature_score of 5 for percentages
485        assert!(dashboard_chart.complexity_score() >= 35);
486    }
487
488    #[test]
489    fn test_dashboard_pie_chart_clone() {
490        let chart = PieChartBuilder::new().simple_data(vec![100.0]).build();
491        let dashboard_chart = DashboardPieChart::new(chart).span(5);
492        let cloned = dashboard_chart.clone();
493
494        assert_eq!(cloned.get_span().columns, 5);
495    }
496
497    #[test]
498    fn test_dashboard_pie_chart_debug() {
499        let chart = PieChartBuilder::new().simple_data(vec![100.0]).build();
500        let dashboard_chart = DashboardPieChart::new(chart);
501
502        let debug_str = format!("{:?}", dashboard_chart);
503        assert!(debug_str.contains("DashboardPieChart"));
504    }
505
506    // ==================== DashboardLineChart Tests ====================
507
508    #[test]
509    fn test_dashboard_line_chart_creation() {
510        let series = DataSeries::new("Series 1", Color::blue()).xy_data(vec![
511            (0.0, 10.0),
512            (1.0, 20.0),
513            (2.0, 15.0),
514        ]);
515        let chart = LineChartBuilder::new()
516            .title("Test Line Chart")
517            .add_series(series)
518            .build();
519
520        let dashboard_chart = DashboardLineChart::new(chart);
521        assert_eq!(dashboard_chart.component_type(), "LineChart");
522        assert_eq!(dashboard_chart.preferred_height(500.0), 220.0);
523    }
524
525    #[test]
526    fn test_dashboard_line_chart_span() {
527        let series = DataSeries::new("Series", Color::red()).xy_data(vec![(0.0, 5.0)]);
528        let chart = LineChartBuilder::new().add_series(series).build();
529        let dashboard_chart = DashboardLineChart::new(chart).span(10);
530
531        assert_eq!(dashboard_chart.get_span().columns, 10);
532    }
533
534    #[test]
535    fn test_dashboard_line_chart_chart_accessor() {
536        let series = DataSeries::new("Test", Color::green()).xy_data(vec![(0.0, 1.0), (1.0, 2.0)]);
537        let chart = LineChartBuilder::new().add_series(series).build();
538        let dashboard_chart = DashboardLineChart::new(chart);
539
540        assert_eq!(dashboard_chart.chart().series.len(), 1);
541    }
542
543    #[test]
544    fn test_dashboard_line_chart_chart_mut() {
545        let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
546        let chart = LineChartBuilder::new().add_series(series).build();
547        let mut dashboard_chart = DashboardLineChart::new(chart);
548
549        dashboard_chart.chart_mut().show_grid = true;
550        assert!(dashboard_chart.chart().show_grid);
551    }
552
553    #[test]
554    fn test_dashboard_line_chart_set_span() {
555        let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
556        let chart = LineChartBuilder::new().add_series(series).build();
557        let mut dashboard_chart = DashboardLineChart::new(chart);
558
559        dashboard_chart.set_span(ComponentSpan::new(8));
560        assert_eq!(dashboard_chart.get_span().columns, 8);
561    }
562
563    #[test]
564    fn test_dashboard_line_chart_minimum_width() {
565        let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
566        let chart = LineChartBuilder::new().add_series(series).build();
567        let dashboard_chart = DashboardLineChart::new(chart);
568
569        assert_eq!(dashboard_chart.minimum_width(), 250.0);
570    }
571
572    #[test]
573    fn test_dashboard_line_chart_estimated_render_time() {
574        let series1 =
575            DataSeries::new("S1", Color::blue()).xy_data(vec![(0.0, 1.0), (1.0, 2.0), (2.0, 3.0)]);
576        let series2 = DataSeries::new("S2", Color::red()).xy_data(vec![(0.0, 2.0), (1.0, 1.0)]);
577        let chart = LineChartBuilder::new()
578            .add_series(series1)
579            .add_series(series2)
580            .build();
581        let dashboard_chart = DashboardLineChart::new(chart);
582
583        // 30 base + (5 points * 2) = 40
584        assert_eq!(dashboard_chart.estimated_render_time_ms(), 40);
585    }
586
587    #[test]
588    fn test_dashboard_line_chart_estimated_memory() {
589        let series = DataSeries::new("Test", Color::blue()).xy_data(vec![
590            (0.0, 1.0),
591            (1.0, 2.0),
592            (2.0, 3.0),
593            (3.0, 4.0),
594        ]);
595        let chart = LineChartBuilder::new().add_series(series).build();
596        let dashboard_chart = DashboardLineChart::new(chart);
597
598        // 0.25 base + (4 points * 0.01) = 0.29
599        let expected = 0.25 + (4.0 * 0.01);
600        assert!((dashboard_chart.estimated_memory_mb() - expected).abs() < 0.001);
601    }
602
603    #[test]
604    fn test_dashboard_line_chart_complexity_with_grid() {
605        let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
606        let chart = LineChartBuilder::new()
607            .add_series(series)
608            .grid(true, Color::gray(0.8), 10)
609            .build();
610        let dashboard_chart = DashboardLineChart::new(chart);
611
612        // 40 base + 10 series complexity + 10 grid = at least 50
613        assert!(dashboard_chart.complexity_score() >= 50);
614    }
615
616    #[test]
617    fn test_dashboard_line_chart_complexity_multiple_series() {
618        let series1 = DataSeries::new("S1", Color::blue()).xy_data(vec![(0.0, 1.0)]);
619        let series2 = DataSeries::new("S2", Color::red()).xy_data(vec![(0.0, 2.0)]);
620        let series3 = DataSeries::new("S3", Color::green()).xy_data(vec![(0.0, 3.0)]);
621        let chart = LineChartBuilder::new()
622            .add_series(series1)
623            .add_series(series2)
624            .add_series(series3)
625            .build();
626        let dashboard_chart = DashboardLineChart::new(chart);
627
628        // More series = higher complexity
629        assert!(dashboard_chart.complexity_score() >= 40);
630    }
631
632    #[test]
633    fn test_dashboard_line_chart_clone() {
634        let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
635        let chart = LineChartBuilder::new().add_series(series).build();
636        let dashboard_chart = DashboardLineChart::new(chart).span(7);
637        let cloned = dashboard_chart.clone();
638
639        assert_eq!(cloned.get_span().columns, 7);
640    }
641
642    #[test]
643    fn test_dashboard_line_chart_debug() {
644        let series = DataSeries::new("Test", Color::blue()).xy_data(vec![(0.0, 1.0)]);
645        let chart = LineChartBuilder::new().add_series(series).build();
646        let dashboard_chart = DashboardLineChart::new(chart);
647
648        let debug_str = format!("{:?}", dashboard_chart);
649        assert!(debug_str.contains("DashboardLineChart"));
650    }
651
652    // ==================== Comparison Tests ====================
653
654    #[test]
655    fn test_complexity_scores() {
656        // Bar chart with many bars should have higher complexity
657        let simple_bar = BarChartBuilder::new().simple_data(vec![10.0, 20.0]).build();
658        let complex_bar = BarChartBuilder::new()
659            .simple_data(vec![10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0])
660            .show_grid(true)
661            .show_values(true)
662            .build();
663
664        let simple_dashboard = DashboardBarChart::new(simple_bar);
665        let complex_dashboard = DashboardBarChart::new(complex_bar);
666
667        assert!(complex_dashboard.complexity_score() > simple_dashboard.complexity_score());
668    }
669
670    #[test]
671    fn test_all_chart_types_component_types() {
672        let bar = DashboardBarChart::new(BarChartBuilder::new().simple_data(vec![1.0]).build());
673        let pie = DashboardPieChart::new(PieChartBuilder::new().simple_data(vec![1.0]).build());
674        let series = DataSeries::new("T", Color::black()).xy_data(vec![(0.0, 1.0)]);
675        let line = DashboardLineChart::new(LineChartBuilder::new().add_series(series).build());
676
677        assert_eq!(bar.component_type(), "BarChart");
678        assert_eq!(pie.component_type(), "PieChart");
679        assert_eq!(line.component_type(), "LineChart");
680    }
681
682    #[test]
683    fn test_empty_data_estimated_values() {
684        // Even with minimal data, estimates should be reasonable
685        let bar = DashboardBarChart::new(BarChartBuilder::new().simple_data(vec![]).build());
686        let pie = DashboardPieChart::new(PieChartBuilder::new().simple_data(vec![]).build());
687
688        assert!(bar.estimated_render_time_ms() >= 20); // Base time
689        assert!(pie.estimated_render_time_ms() >= 25); // Base time
690        assert!(bar.estimated_memory_mb() >= 0.2); // Base memory
691        assert!(pie.estimated_memory_mb() >= 0.15); // Base memory
692    }
693}