1use super::canvas;
14use super::color::*;
15use super::common::*;
16use super::component::*;
17use super::params::*;
18use super::theme::{get_default_theme_name, get_theme, Theme, DEFAULT_Y_AXIS_WIDTH};
19use super::util::*;
20use super::Canvas;
21use crate::charts::measure_text_width_family;
22use charts_rs_derive::Chart;
23use serde::{Deserialize, Serialize};
24use std::sync::Arc;
25
26#[derive(Serialize, Deserialize, Clone, Debug, Default, Chart)]
27pub struct BarChart {
28 pub width: f32,
29 pub height: f32,
30 pub x: f32,
31 pub y: f32,
32 pub margin: Box,
33 pub series_list: Vec<Series>,
34 pub font_family: String,
35 pub background_color: Color,
36 pub is_light: bool,
37
38 pub title_text: String,
40 pub title_font_size: f32,
41 pub title_font_color: Color,
42 pub title_font_weight: Option<String>,
43 pub title_margin: Option<Box>,
44 pub title_align: Align,
45 pub title_height: f32,
46
47 pub sub_title_text: String,
49 pub sub_title_font_size: f32,
50 pub sub_title_font_color: Color,
51 pub sub_title_font_weight: Option<String>,
52 pub sub_title_margin: Option<Box>,
53 pub sub_title_align: Align,
54 pub sub_title_height: f32,
55
56 pub legend_font_size: f32,
58 pub legend_font_color: Color,
59 pub legend_font_weight: Option<String>,
60 pub legend_align: Align,
61 pub legend_margin: Option<Box>,
62 pub legend_category: LegendCategory,
63 pub legend_show: Option<bool>,
64
65 pub x_axis_data: Vec<String>,
67 pub x_axis_height: f32,
68 pub x_axis_stroke_color: Color,
69 pub x_axis_font_size: f32,
70 pub x_axis_font_color: Color,
71 pub x_axis_font_weight: Option<String>,
72 pub x_axis_name_gap: f32,
73 pub x_axis_name_rotate: f32,
74 pub x_axis_margin: Option<Box>,
75 pub x_axis_hidden: bool,
76 pub x_boundary_gap: Option<bool>,
77
78 pub y_axis_hidden: bool,
80 pub y_axis_configs: Vec<YAxisConfig>,
81
82 pub grid_stroke_color: Color,
84 pub grid_stroke_width: f32,
85
86 pub series_stroke_width: f32,
88 pub series_label_font_color: Color,
89 pub series_label_font_size: f32,
90 pub series_label_font_weight: Option<String>,
91 pub series_label_formatter: String,
92 pub series_colors: Vec<Color>,
93 pub series_symbol: Option<Symbol>,
94 pub series_smooth: bool,
95 pub series_fill: bool,
96
97 pub radius: Option<f32>,
98}
99
100impl BarChart {
101 pub fn from_json(data: &str) -> canvas::Result<BarChart> {
103 let mut b = BarChart {
104 ..Default::default()
105 };
106 let value = b.fill_option(data)?;
107 if let Some(x_axis_hidden) = get_bool_from_value(&value, "x_axis_hidden") {
108 b.x_axis_hidden = x_axis_hidden;
109 }
110 if let Some(y_axis_hidden) = get_bool_from_value(&value, "y_axis_hidden") {
111 b.y_axis_hidden = y_axis_hidden;
112 }
113 if let Some(radius) = get_f32_from_value(&value, "radius") {
114 b.radius = Some(radius);
115 }
116 Ok(b)
117 }
118 pub fn new_with_theme(
120 mut series_list: Vec<Series>,
121 x_axis_data: Vec<String>,
122 theme: &str,
123 ) -> BarChart {
124 series_list
127 .iter_mut()
128 .enumerate()
129 .for_each(|(index, item)| {
130 item.index = Some(index);
131 });
132 let mut b = BarChart {
133 series_list,
134 x_axis_data,
135 ..Default::default()
136 };
137 let theme = get_theme(theme);
138 b.fill_theme(theme);
139 b
140 }
141 pub fn new(series_list: Vec<Series>, x_axis_data: Vec<String>) -> BarChart {
143 BarChart::new_with_theme(series_list, x_axis_data, &get_default_theme_name())
144 }
145 pub fn svg(&self) -> canvas::Result<String> {
147 let mut c = Canvas::new_width_xy(self.width, self.height, self.x, self.y);
148
149 self.render_background(c.child(Box::default()));
150 let mut x_axis_height = self.x_axis_height;
151 if self.x_axis_hidden {
152 x_axis_height = 0.0;
153 }
154 c.margin = self.margin.clone();
155
156 let title_height = self.render_title(c.child(Box::default()));
157
158 let legend_height = self.render_legend(c.child(Box::default()));
159 let axis_top = if legend_height > title_height {
161 legend_height
162 } else {
163 title_height
164 };
165
166 let (left_y_axis_values, mut left_y_axis_width) = self.get_y_axis_values(0);
167 if self.y_axis_hidden {
168 left_y_axis_width = 0.0;
169 }
170 let mut exist_right_y_axis = false;
171 for series in self.series_list.iter() {
173 if series.y_axis_index != 0 {
174 exist_right_y_axis = true;
175 }
176 }
177 let mut right_y_axis_values = AxisValues::default();
178 let mut right_y_axis_width = 0.0_f32;
179 if !self.y_axis_hidden && exist_right_y_axis {
180 (right_y_axis_values, right_y_axis_width) = self.get_y_axis_values(1);
181 }
182
183 let axis_height = c.height() - x_axis_height - axis_top;
184 let axis_width = c.width() - left_y_axis_width - right_y_axis_width;
185 if axis_top > 0.0 {
187 c = c.child(Box {
188 top: axis_top,
189 ..Default::default()
190 });
191 }
192
193 self.render_grid(
194 c.child(Box {
195 left: left_y_axis_width,
196 ..Default::default()
197 }),
198 axis_width,
199 axis_height,
200 );
201
202 if left_y_axis_width > 0.0 {
204 self.render_y_axis(
205 c.child(Box::default()),
206 left_y_axis_values.data.clone(),
207 axis_height,
208 left_y_axis_width,
209 0,
210 );
211 }
212 if right_y_axis_width > 0.0 {
214 self.render_y_axis(
215 c.child(Box {
216 left: c.width() - right_y_axis_width,
217 ..Default::default()
218 }),
219 right_y_axis_values.data.clone(),
220 axis_height,
221 right_y_axis_width,
222 1,
223 );
224 }
225
226 if !self.x_axis_hidden {
228 self.render_x_axis(
229 c.child(Box {
230 top: c.height() - x_axis_height,
231 left: left_y_axis_width,
232 right: right_y_axis_width,
233 ..Default::default()
234 }),
235 self.x_axis_data.clone(),
236 axis_width,
237 );
238 }
239
240 let max_height = c.height() - x_axis_height;
242 let mut bar_series_list = vec![];
243 let mut line_series_list = vec![];
244 self.series_list.iter().for_each(|item| {
246 if let Some(ref cat) = item.category {
247 if *cat == SeriesCategory::Line {
248 line_series_list.push(item);
249 return;
250 }
251 }
252 bar_series_list.push(item);
253 });
254
255 let y_axis_values_list = vec![&left_y_axis_values, &right_y_axis_values];
256 let mut bar_series_labels_list = self.render_bar(
257 c.child(Box {
258 left: left_y_axis_width,
259 right: right_y_axis_width,
260 ..Default::default()
261 }),
262 &bar_series_list,
263 &y_axis_values_list,
264 max_height,
265 self.x_axis_data.len(),
266 self.radius,
267 );
268
269 let mut line_series_labels_list = self.render_line(
270 c.child(Box {
271 left: left_y_axis_width,
272 right: right_y_axis_width,
273 ..Default::default()
274 }),
275 &line_series_list,
276 &y_axis_values_list,
277 max_height,
278 axis_height,
279 self.x_axis_data.len(),
280 );
281
282 bar_series_labels_list.append(&mut line_series_labels_list);
283
284 self.render_series_label(
285 c.child(Box {
286 left: left_y_axis_width,
287 right: right_y_axis_width,
288 ..Default::default()
289 }),
290 bar_series_labels_list,
291 );
292
293 c.svg()
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::BarChart;
300 use crate::{
301 Box, LegendCategory, SeriesCategory, NIL_VALUE, THEME_ANT, THEME_DARK, THEME_GRAFANA,
302 };
303 use pretty_assertions::assert_eq;
304 #[test]
305 fn bar_chart_basic() {
306 let mut bar_chart = BarChart::new(
307 vec![
308 (
309 "Email",
310 vec![120.0, 132.0, 101.0, 134.0, 90.0, 230.0, 210.0],
311 )
312 .into(),
313 (
314 "Union Ads",
315 vec![220.0, 182.0, 191.0, 234.0, 290.0, 330.0, 310.0],
316 )
317 .into(),
318 (
319 "Direct",
320 vec![320.0, 332.0, 301.0, 334.0, 390.0, 330.0, 320.0],
321 )
322 .into(),
323 (
324 "Search Engine",
325 vec![820.0, 932.0, 901.0, 934.0, 1290.0, 1330.0, 1320.0],
326 )
327 .into(),
328 ],
329 vec![
330 "Mon".to_string(),
331 "Tue".to_string(),
332 "Wed".to_string(),
333 "Thu".to_string(),
334 "Fri".to_string(),
335 "Sat".to_string(),
336 "Sun".to_string(),
337 ],
338 );
339 bar_chart.y_axis_configs[0].axis_width = Some(55.0);
340 bar_chart.title_text = "Bar Chart".to_string();
341 bar_chart.legend_margin = Some(Box {
342 top: 35.0,
343 bottom: 10.0,
344 ..Default::default()
345 });
346 bar_chart.legend_category = LegendCategory::RoundRect;
347 bar_chart.y_axis_configs[0].axis_formatter = Some("{c} ml".to_string());
348 bar_chart.series_list[0].label_show = true;
349 bar_chart.series_list[0].colors = Some(vec![None, Some("#a90000".into())]);
350 assert_eq!(
351 include_str!("../../asset/bar_chart/basic.svg"),
352 bar_chart.svg().unwrap()
353 );
354 }
355 #[test]
356 fn bar_chart_basic_dark() {
357 let mut bar_chart = BarChart::new_with_theme(
358 vec![
359 (
360 "Email",
361 vec![120.0, 132.0, 101.0, 134.0, 90.0, 230.0, 210.0],
362 )
363 .into(),
364 (
365 "Union Ads",
366 vec![220.0, 182.0, 191.0, 234.0, 290.0, 330.0, 310.0],
367 )
368 .into(),
369 (
370 "Direct",
371 vec![320.0, 332.0, 301.0, 334.0, 390.0, 330.0, 320.0],
372 )
373 .into(),
374 (
375 "Search Engine",
376 vec![820.0, 932.0, 901.0, 934.0, 1290.0, 1330.0, 1320.0],
377 )
378 .into(),
379 ],
380 vec![
381 "Mon".to_string(),
382 "Tue".to_string(),
383 "Wed".to_string(),
384 "Thu".to_string(),
385 "Fri".to_string(),
386 "Sat".to_string(),
387 "Sun".to_string(),
388 ],
389 THEME_DARK,
390 );
391 bar_chart.y_axis_configs[0].axis_width = Some(55.0);
392 bar_chart.title_text = "Bar Chart".to_string();
393 bar_chart.legend_margin = Some(Box {
394 top: 35.0,
395 bottom: 10.0,
396 ..Default::default()
397 });
398 bar_chart.radius = Some(5.0);
399 bar_chart.y_axis_configs[0].axis_formatter = Some("{c} ml".to_string());
400 bar_chart.series_list[0].label_show = true;
401 bar_chart.legend_category = LegendCategory::Circle;
402 assert_eq!(
403 include_str!("../../asset/bar_chart/basic_dark.svg"),
404 bar_chart.svg().unwrap()
405 );
406 }
407
408 #[test]
409 fn bar_chart_basic_ant() {
410 let mut bar_chart = BarChart::new_with_theme(
411 vec![
412 (
413 "Email",
414 vec![120.0, 132.0, 101.0, 134.0, 90.0, 230.0, 210.0],
415 )
416 .into(),
417 (
418 "Union Ads",
419 vec![220.0, 182.0, 191.0, 234.0, 290.0, 330.0, 310.0],
420 )
421 .into(),
422 (
423 "Direct",
424 vec![320.0, 332.0, 301.0, 334.0, 390.0, 330.0, 320.0],
425 )
426 .into(),
427 (
428 "Search Engine",
429 vec![820.0, 932.0, 901.0, 934.0, 1290.0, 1330.0, 1320.0],
430 )
431 .into(),
432 ],
433 vec![
434 "Mon".to_string(),
435 "Tue".to_string(),
436 "Wed".to_string(),
437 "Thu".to_string(),
438 "Fri".to_string(),
439 "Sat".to_string(),
440 "Sun".to_string(),
441 ],
442 THEME_ANT,
443 );
444 bar_chart.y_axis_configs[0].axis_width = Some(55.0);
445 bar_chart.title_text = "Bar Chart".to_string();
446 bar_chart.legend_margin = Some(Box {
447 top: 35.0,
448 bottom: 10.0,
449 ..Default::default()
450 });
451 bar_chart.y_axis_configs[0].axis_formatter = Some("{c} ml".to_string());
452 bar_chart.series_list[0].label_show = true;
453 assert_eq!(
454 include_str!("../../asset/bar_chart/basic_ant.svg"),
455 bar_chart.svg().unwrap()
456 );
457 }
458
459 #[test]
460 fn bar_chart_basic_grafana() {
461 let mut bar_chart = BarChart::new_with_theme(
462 vec![
463 (
464 "Email",
465 vec![120.0, 132.0, 101.0, 134.0, 90.0, 230.0, 210.0],
466 )
467 .into(),
468 (
469 "Union Ads",
470 vec![220.0, 182.0, 191.0, 234.0, 290.0, 330.0, 310.0],
471 )
472 .into(),
473 (
474 "Direct",
475 vec![320.0, 332.0, 301.0, 334.0, 390.0, 330.0, 320.0],
476 )
477 .into(),
478 (
479 "Search Engine",
480 vec![820.0, 932.0, 901.0, 934.0, 1290.0, 1330.0, 1320.0],
481 )
482 .into(),
483 ],
484 vec![
485 "Mon".to_string(),
486 "Tue".to_string(),
487 "Wed".to_string(),
488 "Thu".to_string(),
489 "Fri".to_string(),
490 "Sat".to_string(),
491 "Sun".to_string(),
492 ],
493 THEME_GRAFANA,
494 );
495 bar_chart.y_axis_configs[0].axis_width = Some(55.0);
496 bar_chart.title_text = "Bar Chart".to_string();
497 bar_chart.legend_margin = Some(Box {
498 top: 35.0,
499 bottom: 10.0,
500 ..Default::default()
501 });
502 bar_chart.y_axis_configs[0].axis_formatter = Some("{c} ml".to_string());
503 bar_chart.series_list[0].label_show = true;
504 assert_eq!(
505 include_str!("../../asset/bar_chart/basic_grafana.svg"),
506 bar_chart.svg().unwrap()
507 );
508 }
509
510 #[test]
511 fn bar_chart_y_axis_min_max() {
512 let mut bar_chart = BarChart::new_with_theme(
513 vec![
514 (
515 "Email",
516 vec![120.0, 132.0, 101.0, 134.0, 90.0, 230.0, 210.0],
517 )
518 .into(),
519 (
520 "Union Ads",
521 vec![220.0, 182.0, 191.0, 234.0, 290.0, 330.0, 310.0],
522 )
523 .into(),
524 (
525 "Direct",
526 vec![320.0, 332.0, 301.0, 334.0, 390.0, 330.0, 320.0],
527 )
528 .into(),
529 (
530 "Search Engine",
531 vec![820.0, 932.0, 901.0, 934.0, 1290.0, 1330.0, 1320.0],
532 )
533 .into(),
534 ],
535 vec![
536 "Mon".to_string(),
537 "Tue".to_string(),
538 "Wed".to_string(),
539 "Thu".to_string(),
540 "Fri".to_string(),
541 "Sat".to_string(),
542 "Sun".to_string(),
543 ],
544 THEME_GRAFANA,
545 );
546 bar_chart.y_axis_configs[0].axis_width = Some(55.0);
547 bar_chart.y_axis_configs[0].axis_max = Some(1500.0);
548 bar_chart.title_text = "Bar Chart".to_string();
549 bar_chart.legend_margin = Some(Box {
550 top: 35.0,
551 bottom: 10.0,
552 ..Default::default()
553 });
554 bar_chart.y_axis_configs[0].axis_formatter = Some("{c} ml".to_string());
555 bar_chart.series_list[0].label_show = true;
556 assert_eq!(
557 include_str!("../../asset/bar_chart/y_axis_min_max.svg"),
558 bar_chart.svg().unwrap()
559 );
560 }
561
562 #[test]
563 fn bar_chart_line_mixin() {
564 let mut bar_chart = BarChart::new(
565 vec![
566 (
567 "Email",
568 vec![120.0, 132.0, 101.0, 134.0, 90.0, 230.0, 210.0],
569 )
570 .into(),
571 (
572 "Union Ads",
573 vec![220.0, 182.0, 191.0, 234.0, 290.0, 330.0, 310.0],
574 )
575 .into(),
576 (
577 "Direct",
578 vec![320.0, 332.0, 301.0, 334.0, 390.0, 330.0, 320.0],
579 )
580 .into(),
581 (
582 "Search Engine",
583 vec![820.0, 932.0, 901.0, 934.0, 1290.0, 1330.0, 1320.0],
584 )
585 .into(),
586 ],
587 vec![
588 "Mon".to_string(),
589 "Tue".to_string(),
590 "Wed".to_string(),
591 "Thu".to_string(),
592 "Fri".to_string(),
593 "Sat".to_string(),
594 "Sun".to_string(),
595 ],
596 );
597 bar_chart.series_list[0].category = Some(SeriesCategory::Line);
598 bar_chart.y_axis_configs[0].axis_width = Some(55.0);
599 bar_chart.title_text = "Bar Chart".to_string();
600 bar_chart.legend_margin = Some(Box {
601 top: 35.0,
602 bottom: 10.0,
603 ..Default::default()
604 });
605 bar_chart.legend_category = LegendCategory::Rect;
606 bar_chart.y_axis_configs[0].axis_formatter = Some("{c} ml".to_string());
607 bar_chart.series_list[0].label_show = true;
608 bar_chart.series_list[3].label_show = true;
609
610 assert_eq!(
611 include_str!("../../asset/bar_chart/line_mixin.svg"),
612 bar_chart.svg().unwrap()
613 );
614
615 #[cfg(feature = "image-encoder")]
616 {
617 use crate::svg_to_jpeg;
618 let buf = svg_to_jpeg(&bar_chart.svg().unwrap()).unwrap();
619 std::fs::write("./asset/image/line_mixin.jpeg", buf).unwrap();
620 }
621 }
622
623 #[test]
624 fn bar_chart_two_y_axis() {
625 let mut bar_chart = BarChart::new(
626 vec![
627 ("Evaporation", vec![2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6]).into(),
628 (
629 "Precipitation",
630 vec![2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6],
631 )
632 .into(),
633 ("Temperature", vec![2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3]).into(),
634 ],
635 vec![
636 "Mon".to_string(),
637 "Tue".to_string(),
638 "Wed".to_string(),
639 "Thu".to_string(),
640 "Fri".to_string(),
641 "Sat".to_string(),
642 "Sun".to_string(),
643 ],
644 );
645 bar_chart.series_list[2].category = Some(SeriesCategory::Line);
646 bar_chart.series_list[2].y_axis_index = 1;
647
648 bar_chart.y_axis_configs[0].axis_width = Some(55.0);
649 bar_chart.title_text = "Bar Chart".to_string();
650 bar_chart.legend_margin = Some(Box {
651 top: 35.0,
652 bottom: 10.0,
653 ..Default::default()
654 });
655 bar_chart.legend_category = LegendCategory::Rect;
656 bar_chart.y_axis_configs[0].axis_formatter = Some("{c} ml".to_string());
657 bar_chart
658 .y_axis_configs
659 .push(bar_chart.y_axis_configs[0].clone());
660 bar_chart.y_axis_configs[1].axis_formatter = Some("{c} °C".to_string());
661 assert_eq!(
662 include_str!("../../asset/bar_chart/two_y_axis.svg"),
663 bar_chart.svg().unwrap()
664 );
665 }
666
667 #[test]
668 fn bar_chart_value_count_unequal() {
669 let mut bar_chart = BarChart::new(
670 vec![
671 ("Email", vec![120.0, 132.0, 101.0, 134.0, 90.0, 230.0]).into(),
672 (
673 "Union Ads",
674 vec![220.0, 182.0, 191.0, 234.0, 290.0, 330.0, 310.0],
675 )
676 .into(),
677 (
678 "Direct",
679 vec![320.0, 332.0, 301.0, 334.0, 390.0, 330.0, 320.0],
680 )
681 .into(),
682 (
683 "Search Engine",
684 vec![820.0, 932.0, 901.0, 934.0, 1290.0, 1330.0, 1320.0],
685 )
686 .into(),
687 ],
688 vec![
689 "Mon".to_string(),
690 "Tue".to_string(),
691 "Wed".to_string(),
692 "Thu".to_string(),
693 "Fri".to_string(),
694 "Sat".to_string(),
695 "Sun".to_string(),
696 ],
697 );
698 bar_chart.y_axis_configs[0].axis_width = Some(55.0);
699 bar_chart.title_text = "Bar Chart".to_string();
700 bar_chart.legend_margin = Some(Box {
701 top: 35.0,
702 bottom: 10.0,
703 ..Default::default()
704 });
705 bar_chart.y_axis_configs[0].axis_formatter = Some("{c} ml".to_string());
706 bar_chart.series_list[0].label_show = true;
707 bar_chart.series_list[0].start_index = 1;
708 assert_eq!(
709 include_str!("../../asset/bar_chart/value_count_unequal.svg"),
710 bar_chart.svg().unwrap()
711 );
712 }
713
714 #[test]
715 fn bar_chart_nil_value() {
716 let mut bar_chart = BarChart::new(
717 vec![
718 (
719 "Email",
720 vec![120.0, NIL_VALUE, 132.0, 101.0, 134.0, 90.0, 230.0],
721 )
722 .into(),
723 (
724 "Union Ads",
725 vec![220.0, 182.0, 191.0, NIL_VALUE, 290.0, 330.0, 310.0],
726 )
727 .into(),
728 (
729 "Direct",
730 vec![320.0, 332.0, 301.0, 334.0, 390.0, NIL_VALUE, 320.0],
731 )
732 .into(),
733 (
734 "Search Engine",
735 vec![NIL_VALUE, 932.0, 901.0, 934.0, 1290.0, 1330.0, 1320.0],
736 )
737 .into(),
738 ],
739 vec![
740 "Mon".to_string(),
741 "Tue".to_string(),
742 "Wed".to_string(),
743 "Thu".to_string(),
744 "Fri".to_string(),
745 "Sat".to_string(),
746 "Sun".to_string(),
747 ],
748 );
749 bar_chart.y_axis_configs[0].axis_width = Some(55.0);
750 bar_chart.title_text = "Bar Chart".to_string();
751 bar_chart.legend_margin = Some(Box {
752 top: 35.0,
753 bottom: 10.0,
754 ..Default::default()
755 });
756 bar_chart.y_axis_configs[0].axis_formatter = Some("{c} ml".to_string());
757 bar_chart.series_list[0].label_show = true;
758 assert_eq!(
759 include_str!("../../asset/bar_chart/nil_value.svg"),
760 bar_chart.svg().unwrap()
761 );
762 }
763
764 #[test]
765 fn bar_chart_no_axis() {
766 let mut bar_chart = BarChart::new(
767 vec![
768 (
769 "Email",
770 vec![120.0, 132.0, 101.0, 134.0, 90.0, 230.0, 210.0],
771 )
772 .into(),
773 (
774 "Union Ads",
775 vec![220.0, 182.0, 191.0, 234.0, 290.0, 330.0, 310.0],
776 )
777 .into(),
778 (
779 "Direct",
780 vec![320.0, 332.0, 301.0, 334.0, 390.0, 330.0, 320.0],
781 )
782 .into(),
783 (
784 "Search Engine",
785 vec![820.0, 932.0, 901.0, 934.0, 1290.0, 1330.0, 1320.0],
786 )
787 .into(),
788 ],
789 vec![
790 "Mon".to_string(),
791 "Tue".to_string(),
792 "Wed".to_string(),
793 "Thu".to_string(),
794 "Fri".to_string(),
795 "Sat".to_string(),
796 "Sun".to_string(),
797 ],
798 );
799 bar_chart.x_axis_hidden = true;
800 bar_chart.y_axis_hidden = true;
801 bar_chart.title_text = "Bar Chart".to_string();
802 bar_chart.legend_margin = Some(Box {
803 top: 35.0,
804 bottom: 10.0,
805 ..Default::default()
806 });
807
808 assert_eq!(
809 include_str!("../../asset/bar_chart/no_axis.svg"),
810 bar_chart.svg().unwrap()
811 );
812 }
813
814 #[test]
815 fn bar_chart_custom_label_formatter() {
816 let mut bar_chart = BarChart::new(
817 vec![("values", vec![0.0, 0.25, 0.5, 0.75, 1.0]).into()],
818 vec!["1".into(), "2".into(), "3".into(), "4".into(), "5".into()],
819 );
820
821 bar_chart.series_list[0].label_show = true;
822 bar_chart.series_label_formatter = "{:.1}".to_string();
823
824 assert_eq!(
825 include_str!("../../asset/bar_chart/custom_label_formatter.svg").trim(),
826 bar_chart.svg().unwrap()
827 );
828 }
829
830 #[test]
831 fn test_render_legend_center() {
832 let bar_chart = BarChart::from_json(
833 r###"{
834 "legend_align": "center",
835 "series_list": [
836 {
837 "name": "None",
838 "label_show": true,
839 "data": [
840 44
841 ]
842 },
843 {
844 "name": "M1 - End of Inception",
845 "label_show": true,
846 "data": [
847 78
848 ]
849 },
850 {
851 "name": "M2 - End of Elaboration",
852 "label_show": true,
853 "data": [
854 26
855 ]
856 },
857 {
858 "name": "M3 - End of Construction",
859 "label_show": true,
860 "data": [
861 3
862 ]
863 }
864 ],
865 "type": "bar",
866 "x_axis_data": [
867 "Milestones"
868 ]
869}"###,
870 )
871 .unwrap();
872 assert_eq!(
873 include_str!("../../asset/bar_chart/legend_center.svg").trim(),
874 bar_chart.svg().unwrap()
875 );
876 }
877}