diff --git a/CHANGELOG.md b/CHANGELOG.md index eab1cfc5..0aefb75a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a - [[#163](https://github.com/plotly/plotly.rs/pull/163)] Added `DensityMapbox`. - [[#161](https://github.com/plotly/plotly.rs/pull/161)] Added `Axis` `scaleanchor` settter. - [[#159](https://github.com/plotly/plotly.rs/pull/159)] Make `heat_map` module public to expose `Smoothing enum`. +- [[#157](https://github.com/plotly/plotly.rs/pull/157)] Fix `HeatMap`'s setters for correctly setting `zmin`, `zmax` and `zmin` independent of `Z` input type. +- [[#154](https://github.com/plotly/plotly.rs/pull/154)] Improve ergonomics of `Title` and `LegendGroupTitle` structs: `new` method now takes no arguments as per other structs, whilst a new `with_text()` constructor is added for convenience. Where other structs contain a `Title` (and `LegendGroupTitle`), users can now call the `title()` (and `legend_group_title()`) method with anything that `impl`s `Into`, viz. `String`, `&String`, `&str` and `Title`. - [[#153](https://github.com/plotly/plotly.rs/pull/153)] Added `LayoutScene`. ## [0.8.4] - 2023-07-09 diff --git a/examples/3d_charts/src/main.rs b/examples/3d_charts/src/main.rs index e575daf1..ed484f73 100644 --- a/examples/3d_charts/src/main.rs +++ b/examples/3d_charts/src/main.rs @@ -3,7 +3,7 @@ use ndarray::Array; use plotly::{ color::Rgb, - common::{ColorBar, ColorScale, ColorScalePalette, Font, Marker, MarkerSymbol, Mode, Title}, + common::{ColorBar, ColorScale, ColorScalePalette, Font, Marker, MarkerSymbol, Mode}, layout::{Axis, Camera, Layout, LayoutScene, Legend, Margin, ProjectionType}, Mesh3D, Plot, Scatter3D, Surface, }; @@ -69,7 +69,7 @@ fn customized_scatter3d_plot() { let front_color: Rgb = Rgb::new(255, 255, 255); let layout = Layout::new() - .title("Helix".into()) + .title("Helix") .legend(Legend::new().x(0.9).y(0.9)) .font(Font::new().color(front_color)) .paper_background_color(background_color) @@ -77,19 +77,19 @@ fn customized_scatter3d_plot() { LayoutScene::new() .x_axis( Axis::new() - .title("x (A meaningful axis name goes here)".into()) + .title("x (A meaningful axis name goes here)") .tick_angle(0f64) .grid_color(front_color) .color(front_color), ) .y_axis( Axis::new() - .title(Title::new("This is the label of the Y axis")) + .title("This is the label of the Y axis") .tick_format(".1f") .grid_color(front_color) .color(front_color), ) - .z_axis(Axis::new().title("".into()).tick_values(vec![])) + .z_axis(Axis::new().title("").tick_values(vec![])) .aspect_mode(plotly::layout::AspectMode::Manual) .aspect_ratio((3.0, 1.0, 1.0).into()) .camera( @@ -213,7 +213,7 @@ fn colorscale_plot() { let layout = Layout::new() .font(Font::new().size(18).family("Palatino-Linotype")) - .title(format!("Colorscale: {colorscale:?}").as_str().into()) + .title(format!("Colorscale: {colorscale:?}")) .width(1200) .height(1000) .scene( diff --git a/examples/basic_charts/src/main.rs b/examples/basic_charts/src/main.rs index 9408c755..14f31adb 100644 --- a/examples/basic_charts/src/main.rs +++ b/examples/basic_charts/src/main.rs @@ -5,7 +5,7 @@ use plotly::{ color::{NamedColor, Rgb, Rgba}, common::{ ColorScale, ColorScalePalette, DashType, Fill, Font, Line, LineShape, Marker, Mode, - Orientation, Title, + Orientation, }, layout::{Axis, BarMode, Layout, Legend, TicksDirection, TraceOrder}, sankey::{Line as SankeyLine, Link, Node}, @@ -118,9 +118,9 @@ fn data_labels_hover() { plot.add_trace(trace2); let layout = Layout::new() - .title("Data Labels Hover".into()) - .x_axis(Axis::new().title("x".into()).range(vec![0.75, 5.25])) - .y_axis(Axis::new().title("y".into()).range(vec![0., 8.])); + .title("Data Labels Hover") + .x_axis(Axis::new().title("x").range(vec![0.75, 5.25])) + .y_axis(Axis::new().title("y").range(vec![0., 8.])); plot.set_layout(layout); plot.show(); @@ -143,7 +143,7 @@ fn data_labels_on_the_plot() { plot.add_trace(trace2); let layout = Layout::new() - .title("Data Labels on the Plot".into()) + .title("Data Labels on the Plot") .x_axis(Axis::new().range(vec![0.75, 5.25])) .y_axis(Axis::new().range(vec![0., 8.])); plot.set_layout(layout); @@ -216,14 +216,14 @@ fn colored_and_styled_scatter_plot() { .marker(Marker::new().color(Rgb::new(142, 124, 195)).size(12)); let layout = Layout::new() - .title(Title::new("Quarter 1 Growth")) + .title("Quarter 1 Growth") .x_axis( Axis::new() - .title(Title::new("GDP per Capita")) + .title("GDP per Capita") .show_grid(false) .zero_line(false), ) - .y_axis(Axis::new().title(Title::new("Percent")).show_line(false)); + .y_axis(Axis::new().title("Percent").show_line(false)); let mut plot = Plot::new(); plot.add_trace(trace1); plot.add_trace(trace2); @@ -280,7 +280,7 @@ fn adding_names_to_line_and_scatter_plot() { .mode(Mode::LinesMarkers) .name("Scatter + Lines"); - let layout = Layout::new().title(Title::new("Adding Names to Line and Scatter Plot")); + let layout = Layout::new().title("Adding Names to Line and Scatter Plot"); let mut plot = Plot::new(); plot.add_trace(trace1); plot.add_trace(trace2); @@ -305,7 +305,7 @@ fn line_and_scatter_styling() { .marker(Marker::new().color(Rgb::new(128, 0, 128)).size(12)) .line(Line::new().color(Rgb::new(128, 0, 128)).width(1.0)); - let layout = Layout::new().title(Title::new("Line and Scatter Styling")); + let layout = Layout::new().title("Line and Scatter Styling"); let mut plot = Plot::new(); plot.add_trace(trace1); plot.add_trace(trace2); @@ -326,7 +326,7 @@ fn styling_line_plot() { .line(Line::new().color(Rgb::new(55, 128, 191)).width(1.0)); let layout = Layout::new() - .title(Title::new("Styling Line Plot")) + .title("Styling Line Plot") .width(500) .height(500); let mut plot = Plot::new(); @@ -595,7 +595,7 @@ fn basic_sankey_diagram() { ); let layout = Layout::new() - .title("Basic Sankey".into()) + .title("Basic Sankey") .font(Font::new().size(10)); let mut plot = Plot::new(); diff --git a/examples/financial_charts/src/main.rs b/examples/financial_charts/src/main.rs index 2805482b..1473699e 100644 --- a/examples/financial_charts/src/main.rs +++ b/examples/financial_charts/src/main.rs @@ -3,7 +3,7 @@ use std::env; use std::path::PathBuf; -use plotly::common::{TickFormatStop, Title}; +use plotly::common::TickFormatStop; use plotly::layout::{Axis, RangeSelector, RangeSlider, SelectorButton, SelectorStep, StepMode}; use plotly::{Candlestick, Layout, Ohlc, Plot, Scatter}; use serde::Deserialize; @@ -50,7 +50,7 @@ fn time_series_plot_with_custom_date_range() { let layout = Layout::new() .x_axis(Axis::new().range(vec!["2016-07-01", "2016-12-31"])) - .title(Title::new("Manually Set Date Range")); + .title("Manually Set Date Range"); plot.set_layout(layout); plot.show(); @@ -68,7 +68,7 @@ fn time_series_with_range_slider() { let layout = Layout::new() .x_axis(Axis::new().range_slider(RangeSlider::new().visible(true))) - .title(Title::new("Manually Set Date Range")); + .title("Manually Set Date Range"); plot.set_layout(layout); plot.show(); diff --git a/examples/scientific_charts/src/main.rs b/examples/scientific_charts/src/main.rs index 34ace161..bea229e2 100644 --- a/examples/scientific_charts/src/main.rs +++ b/examples/scientific_charts/src/main.rs @@ -2,7 +2,7 @@ use std::f64::consts::PI; -use plotly::common::{ColorScale, ColorScalePalette, Font, Title}; +use plotly::common::{ColorScale, ColorScalePalette, Font}; use plotly::contour::Contours; use plotly::{Contour, HeatMap, Layout, Plot}; @@ -47,7 +47,7 @@ fn colorscale_for_contour_plot() { ]; let trace = Contour::new_z(z).color_scale(ColorScale::Palette(ColorScalePalette::Jet)); - let layout = Layout::new().title(Title::new("Colorscale for Contour Plot")); + let layout = Layout::new().title("Colorscale for Contour Plot"); let mut plot = Plot::new(); plot.set_layout(layout); plot.add_trace(trace); @@ -68,7 +68,7 @@ fn customizing_size_and_range_of_a_contour_plots_contours() { .auto_contour(false) .contours(Contours::new().start(0.0).end(8.0).size(2)); - let layout = Layout::new().title(Title::new("Customizing Size and Range of Contours")); + let layout = Layout::new().title("Customizing Size and Range of Contours"); let mut plot = Plot::new(); plot.set_layout(layout); plot.add_trace(trace); @@ -91,7 +91,7 @@ fn customizing_spacing_between_x_and_y_ticks() { .dy(10.0) .y0(10.0); - let layout = Layout::new().title(Title::new("Customizing Size and Range of Contours")); + let layout = Layout::new().title("Customizing Size and Range of Contours"); let mut plot = Plot::new(); plot.set_layout(layout); plot.add_trace(trace); @@ -136,7 +136,7 @@ fn customized_heat_map() { .color_scale(colorscale.into()); let layout = Layout::new() - .title(Title::new("Customized Heatmap")) + .title("Customized Heatmap") .font(Font::new().size(32)); let mut plot = Plot::new(); diff --git a/examples/shapes/src/main.rs b/examples/shapes/src/main.rs index 871986d1..84eaafc0 100644 --- a/examples/shapes/src/main.rs +++ b/examples/shapes/src/main.rs @@ -138,7 +138,7 @@ fn creating_tangent_lines_with_shapes() { plot.add_trace(trace); let mut layout = - Layout::new().title("$f(x)=x\\sin(x^2)+1\\\\ f\'(x)=\\sin(x^2)+2x^2\\cos(x^2)$".into()); + Layout::new().title("$f(x)=x\\sin(x^2)+1\\\\ f\'(x)=\\sin(x^2)+2x^2\\cos(x^2)$"); layout.add_shape( Shape::new() diff --git a/examples/statistical_charts/src/main.rs b/examples/statistical_charts/src/main.rs index b8c83d34..f1c00633 100644 --- a/examples/statistical_charts/src/main.rs +++ b/examples/statistical_charts/src/main.rs @@ -4,7 +4,7 @@ use ndarray::Array; use plotly::{ box_plot::{BoxMean, BoxPoints}, color::{NamedColor, Rgb, Rgba}, - common::{ErrorData, ErrorType, Line, Marker, Mode, Orientation, Title}, + common::{ErrorData, ErrorType, Line, Marker, Mode, Orientation}, histogram::{Bins, Cumulative, HistFunc, HistNorm}, layout::{Axis, BarMode, BoxMode, Layout, Margin}, Bar, BoxPlot, Histogram, Plot, Scatter, @@ -203,11 +203,7 @@ fn grouped_box_plot() { plot.add_trace(trace3); let layout = Layout::new() - .y_axis( - Axis::new() - .title(Title::new("normalized moisture")) - .zero_line(false), - ) + .y_axis(Axis::new().title("normalized moisture").zero_line(false)) .box_mode(BoxMode::Group); plot.set_layout(layout); @@ -248,7 +244,7 @@ fn box_plot_styling_outliers() { .marker(Marker::new().color(Rgb::new(107, 174, 214))) .box_points(BoxPoints::Outliers); - let layout = Layout::new().title(Title::new("Box Plot Styling Outliers")); + let layout = Layout::new().title("Box Plot Styling Outliers"); let mut plot = Plot::new(); plot.set_layout(layout); @@ -274,7 +270,7 @@ fn box_plot_styling_mean_and_standard_deviation() { .name("Mean and Standard Deviation") .marker(Marker::new().color(Rgb::new(8, 81, 156))) .box_mean(BoxMean::StandardDeviation); - let layout = Layout::new().title(Title::new("Box Plot Styling Mean and Standard Deviation")); + let layout = Layout::new().title("Box Plot Styling Mean and Standard Deviation"); let mut plot = Plot::new(); plot.set_layout(layout); @@ -321,12 +317,8 @@ fn grouped_horizontal_box_plot() { plot.add_trace(trace3); let layout = Layout::new() - .title(Title::new("Grouped Horizontal Box Plot")) - .x_axis( - Axis::new() - .title(Title::new("normalized moisture")) - .zero_line(false), - ) + .title("Grouped Horizontal Box Plot") + .x_axis(Axis::new().title("normalized moisture").zero_line(false)) .box_mode(BoxMode::Group); plot.set_layout(layout); @@ -370,9 +362,7 @@ fn fully_styled_box_plot() { let mut plot = Plot::new(); let layout = Layout::new() - .title(Title::new( - "Points Scored by the Top 9 Scoring NBA Players in 2012", - )) + .title("Points Scored by the Top 9 Scoring NBA Players in 2012") .y_axis( Axis::new() .auto_range(true) @@ -522,9 +512,9 @@ fn colored_and_styled_histograms() { .auto_bin_x(false) .x_bins(Bins::new(-3.2, 4.0, 0.06)); let layout = Layout::new() - .title(Title::new("Colored and Styled Histograms")) - .x_axis(Axis::new().title(Title::new("Value"))) - .y_axis(Axis::new().title(Title::new("Count"))) + .title("Colored and Styled Histograms") + .x_axis(Axis::new().title("Value")) + .y_axis(Axis::new().title("Count")) .bar_mode(BarMode::Overlay) .bar_gap(0.05) .bar_group_gap(0.2); diff --git a/examples/subplots/src/main.rs b/examples/subplots/src/main.rs index d96616a0..c78bde5a 100644 --- a/examples/subplots/src/main.rs +++ b/examples/subplots/src/main.rs @@ -194,7 +194,7 @@ fn multiple_custom_sized_subplots() { plot.add_trace(trace4); let layout = Layout::new() - .title(Title::new("Multiple Custom Sized Subplots")) + .title("Multiple Custom Sized Subplots") .x_axis(Axis::new().domain(&[0., 0.45]).anchor("y1")) .y_axis(Axis::new().domain(&[0.5, 1.]).anchor("x1")) .x_axis2(Axis::new().domain(&[0.55, 1.]).anchor("y2")) @@ -220,11 +220,11 @@ fn two_y_axes() { plot.add_trace(trace2); let layout = Layout::new() - .title(Title::new("Double Y Axis Example")) - .y_axis(Axis::new().title(Title::new("yaxis title"))) + .title("Double Y Axis Example") + .y_axis(Axis::new().title("yaxis title")) .y_axis2( Axis::new() - .title(Title::new("yaxis2 title").font(Font::new().color(Rgb::new(148, 103, 189)))) + .title(Title::from("yaxis2 title").font(Font::new().color(Rgb::new(148, 103, 189)))) .tick_font(Font::new().color(Rgb::new(148, 103, 189))) .overlaying("y") .side(AxisSide::Right), @@ -249,17 +249,17 @@ fn multiple_axes() { plot.add_trace(trace4); let layout = Layout::new() - .title(Title::new("multiple y-axes example")) + .title("multiple y-axes example") .width(800) .x_axis(Axis::new().domain(&[0.3, 0.7])) .y_axis( Axis::new() - .title(Title::new("yaxis title").font(Font::new().color("#1f77b4"))) + .title(Title::from("yaxis title").font(Font::new().color("#1f77b4"))) .tick_font(Font::new().color("#1f77b4")), ) .y_axis2( Axis::new() - .title(Title::new("yaxis2 title").font(Font::new().color("#ff7f0e"))) + .title(Title::from("yaxis2 title").font(Font::new().color("#ff7f0e"))) .tick_font(Font::new().color("#ff7f0e")) .anchor("free") .overlaying("y") @@ -268,7 +268,7 @@ fn multiple_axes() { ) .y_axis3( Axis::new() - .title(Title::new("yaxis3 title").font(Font::new().color("#d62728"))) + .title(Title::from("yaxis3 title").font(Font::new().color("#d62728"))) .tick_font(Font::new().color("#d62728")) .anchor("x") .overlaying("y") @@ -276,7 +276,7 @@ fn multiple_axes() { ) .y_axis4( Axis::new() - .title(Title::new("yaxis4 title").font(Font::new().color("#9467bd"))) + .title(Title::from("yaxis4 title").font(Font::new().color("#9467bd"))) .tick_font(Font::new().color("#9467bd")) .anchor("free") .overlaying("y") diff --git a/examples/wasm-yew-minimal/src/main.rs b/examples/wasm-yew-minimal/src/main.rs index f38ab848..cb0fb2bc 100644 --- a/examples/wasm-yew-minimal/src/main.rs +++ b/examples/wasm-yew-minimal/src/main.rs @@ -9,8 +9,7 @@ pub fn plot_component() -> Html { let trace = Scatter::new(vec![0, 1, 2], vec![2, 1, 0]); plot.add_trace(trace); - let layout = - plotly::Layout::new().title(plotly::common::Title::new("Displaying a Chart in Yew")); + let layout = plotly::Layout::new().title("Displaying a Chart in Yew"); plot.set_layout(layout); async move { diff --git a/plotly/src/common/mod.rs b/plotly/src/common/mod.rs index 4777afb5..74956fa2 100644 --- a/plotly/src/common/mod.rs +++ b/plotly/src/common/mod.rs @@ -58,14 +58,36 @@ pub enum HoverInfo { #[serde_with::skip_serializing_none] #[derive(Serialize, Clone, Debug, Default)] pub struct LegendGroupTitle { - text: String, + text: Option<String>, font: Option<Font>, } +impl From<&str> for LegendGroupTitle { + fn from(title: &str) -> Self { + LegendGroupTitle::with_text(title) + } +} + +impl From<String> for LegendGroupTitle { + fn from(value: String) -> Self { + LegendGroupTitle::with_text(value) + } +} + +impl From<&String> for LegendGroupTitle { + fn from(value: &String) -> Self { + LegendGroupTitle::with_text(value) + } +} + impl LegendGroupTitle { - pub fn new(text: &str) -> Self { - Self { - text: text.to_string(), + pub fn new() -> Self { + Default::default() + } + + pub fn with_text<S: Into<String>>(text: S) -> Self { + LegendGroupTitle { + text: Some(text.into()), ..Default::default() } } @@ -976,8 +998,8 @@ impl ColorBar { self } - pub fn title(mut self, title: Title) -> Self { - self.title = Some(title); + pub fn title<T: Into<Title>>(mut self, title: T) -> Self { + self.title = Some(title.into()); self } @@ -1235,7 +1257,7 @@ impl Pad { #[serde_with::skip_serializing_none] #[derive(Serialize, Clone, Debug, Default)] pub struct Title { - text: String, + text: Option<String>, font: Option<Font>, side: Option<Side>, #[serde(rename = "xref")] @@ -1253,14 +1275,30 @@ pub struct Title { impl From<&str> for Title { fn from(title: &str) -> Self { - Title::new(title) + Title::with_text(title) + } +} + +impl From<String> for Title { + fn from(value: String) -> Self { + Title::with_text(value) + } +} + +impl From<&String> for Title { + fn from(value: &String) -> Self { + Title::with_text(value) } } impl Title { - pub fn new(text: &str) -> Self { + pub fn new() -> Self { + Default::default() + } + + pub fn with_text<S: Into<String>>(text: S) -> Self { Title { - text: text.to_owned(), + text: Some(text.into()), ..Default::default() } } @@ -1694,7 +1732,7 @@ mod tests { .tick_width(55) .tick0(0.0) .ticks(Ticks::Outside) - .title(Title::new("title")) + .title(Title::new()) .x(5.0) .x_anchor(Anchor::Bottom) .x_pad(2.2) @@ -1735,7 +1773,7 @@ mod tests { "tickwidth": 55, "tick0": 0.0, "ticks": "outside", - "title": {"text": "title"}, + "title": {}, "x": 5.0, "xanchor": "bottom", "xpad": 2.2, @@ -2172,6 +2210,15 @@ mod tests { assert_eq!(to_value(Reference::Paper).unwrap(), json!("paper")); } + #[test] + #[rustfmt::skip] + fn test_serialize_legend_group_title() { + assert_eq!(to_value(LegendGroupTitle::new()).unwrap(), json!({})); + assert_eq!(to_value(LegendGroupTitle::with_text("title_str").font(Font::default())).unwrap(), json!({"font": {}, "text": "title_str"})); + assert_eq!(to_value(LegendGroupTitle::from(String::from("title_string"))).unwrap(), json!({"text" : "title_string"})); + assert_eq!(to_value(LegendGroupTitle::from(&String::from("title_string"))).unwrap(), json!({"text" : "title_string"})); + } + #[test] fn test_serialize_pad() { let pad = Pad::new(1, 2, 3); @@ -2186,7 +2233,7 @@ mod tests { #[test] fn test_serialize_title() { - let title = Title::new("title") + let title = Title::with_text("title") .font(Font::new()) .side(Side::Top) .x_ref(Reference::Paper) @@ -2307,4 +2354,13 @@ mod tests { assert_eq!(to_value(HoverOn::PointsAndFills).unwrap(), json!("points+fills")); } + + #[test] + #[allow(clippy::needless_borrows_for_generic_args)] + fn test_title_method_can_take_string() { + ColorBar::new().title("Title"); + ColorBar::new().title(String::from("Title")); + ColorBar::new().title(&String::from("Title")); + ColorBar::new().title(Title::with_text("Title")); + } } diff --git a/plotly/src/layout/mod.rs b/plotly/src/layout/mod.rs index d0d53f8d..7a70c23e 100644 --- a/plotly/src/layout/mod.rs +++ b/plotly/src/layout/mod.rs @@ -2180,7 +2180,7 @@ mod tests { .y(2.0) .y_anchor(Anchor::Left) .valign(VAlign::Middle) - .title(Title::new("title")) + .title("title") .group_click(GroupClick::ToggleItem) .item_width(50); @@ -2430,7 +2430,7 @@ mod tests { let axis = Axis::new() .visible(false) .color("#678123") - .title(Title::new("title")) + .title(Title::with_text("title")) .type_(AxisType::Date) .auto_range(false) .range_mode(RangeMode::NonNegative) @@ -2962,7 +2962,7 @@ mod tests { #[test] fn test_serialize_layout_template() { let layout_template = LayoutTemplate::new() - .title("Title".into()) + .title("Title") .show_legend(false) .legend(Legend::new()) .margin(Margin::new()) @@ -3104,7 +3104,9 @@ mod tests { #[test] fn test_serialize_layout() { let layout = Layout::new() - .title("Title".into()) + .title("Title") + .title(String::from("Title")) + .title(Title::with_text("Title")) .show_legend(false) .legend(Legend::new()) .margin(Margin::new()) diff --git a/plotly/src/layout/themes.rs b/plotly/src/layout/themes.rs index a4fb2774..a687caa7 100644 --- a/plotly/src/layout/themes.rs +++ b/plotly/src/layout/themes.rs @@ -62,7 +62,7 @@ pub static PLOTLY_WHITE: Lazy<Template> = Lazy::new(|| { .hover_mode(HoverMode::Closest) .paper_background_color("#ffffff") .plot_background_color("#ffffff") - .title(Title::new("").x(0.05)) + .title(Title::new().x(0.05)) .x_axis( Axis::new() .auto_margin(true) @@ -138,7 +138,7 @@ pub static PLOTLY_DARK: Lazy<Template> = Lazy::new(|| { .hover_mode(HoverMode::Closest) .paper_background_color("#111111") .plot_background_color("#111111") - .title(Title::new("").x(0.05)) + .title(Title::new().x(0.05)) .x_axis( Axis::new() .auto_margin(true) @@ -165,6 +165,31 @@ mod tests { use super::*; use crate::*; + #[test] + fn test_plotly_default() { + let template = &*DEFAULT; + let layout = Layout::new().template(template); + let mut plot = Plot::new(); + plot.set_layout(layout); + plot.add_trace(Bar::new(vec![0], vec![1])); + + let expected = r##"{"template":{"layout":{}}}"##; // etc... + assert!(plot.to_json().contains(expected)); + } + + #[test] + fn test_plotly_white() { + let template = &*PLOTLY_WHITE; + let layout = Layout::new().template(template); + let mut plot = Plot::new(); + plot.set_layout(layout); + plot.add_trace(Bar::new(vec![0], vec![1])); + dbg!(plot.to_json()); + + let expected = r##"{"template":{"layout":{"title":{"x":0.05},"font":{"color":"#2a3f5f"}"##; // etc... + assert!(plot.to_json().contains(expected)); + } + #[test] fn test_plotly_dark() { let template = &*PLOTLY_DARK; @@ -173,8 +198,7 @@ mod tests { plot.set_layout(layout); plot.add_trace(Bar::new(vec![0], vec![1])); - let expected = - r##"{"template":{"layout":{"title":{"text":"","x":0.05},"font":{"color":"#f2f5fa"}"##; // etc... + let expected = r##"{"template":{"layout":{"title":{"x":0.05},"font":{"color":"#f2f5fa"}"##; // etc... assert!(plot.to_json().contains(expected)); } } diff --git a/plotly/src/layout/update_menu.rs b/plotly/src/layout/update_menu.rs index eea463f0..f662a7f2 100644 --- a/plotly/src/layout/update_menu.rs +++ b/plotly/src/layout/update_menu.rs @@ -246,10 +246,7 @@ mod tests { use serde_json::{json, to_value}; use super::*; - use crate::{ - common::{Title, Visible}, - Layout, - }; + use crate::{common::Visible, Layout}; #[test] fn test_serialize_button_method() { @@ -315,7 +312,7 @@ mod tests { Visible::True, Visible::False, ])) - .push_relayout(Layout::modify_title(Title::new("Hello"))) + .push_relayout(Layout::modify_title("Hello")) .push_relayout(Layout::modify_width(20)) .build(); diff --git a/plotly/src/plot.rs b/plotly/src/plot.rs index 03fe4ae0..d02fe92d 100644 --- a/plotly/src/plot.rs +++ b/plotly/src/plot.rs @@ -161,7 +161,7 @@ impl Traces { /// plot.add_trace(trace2); /// plot.add_trace(trace3); /// -/// let layout = Layout::new().title("<b>Line and Scatter Plot</b>".into()); +/// let layout = Layout::new().title("<b>Line and Scatter Plot</b>"); /// plot.set_layout(layout); /// /// # if false { // We don't actually want to try and display the plot in a browser when running a doctest. @@ -463,7 +463,7 @@ impl Plot { fn show_with_default_app(temp_path: &str) { use std::process::Command; Command::new("open") - .args(&[temp_path]) + .args([temp_path]) .output() .expect(DEFAULT_HTML_APP_NOT_FOUND); } @@ -548,7 +548,7 @@ mod tests { #[test] fn test_plot_serialize_with_layout() { let mut plot = create_test_plot(); - let layout = Layout::new().title("Title".into()); + let layout = Layout::new().title("Title"); plot.set_layout(layout); let expected = json!({ @@ -597,7 +597,7 @@ mod tests { #[test] fn test_layout_to_json() { let mut plot = create_test_plot(); - let layout = Layout::new().title("TestTitle".into()); + let layout = Layout::new().title("TestTitle"); plot.set_layout(layout); let expected = json!({ @@ -651,7 +651,7 @@ mod tests { assert!(!dst.exists()); } - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "linux")] #[test] #[cfg(feature = "kaleido")] fn test_save_to_png() { @@ -663,7 +663,7 @@ mod tests { assert!(!dst.exists()); } - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "linux")] #[test] #[cfg(feature = "kaleido")] fn test_save_to_jpeg() { @@ -675,7 +675,7 @@ mod tests { assert!(!dst.exists()); } - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "linux")] #[test] #[cfg(feature = "kaleido")] fn test_save_to_svg() { @@ -699,7 +699,7 @@ mod tests { assert!(!dst.exists()); } - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "linux")] #[test] #[cfg(feature = "kaleido")] fn test_save_to_pdf() { @@ -711,7 +711,7 @@ mod tests { assert!(!dst.exists()); } - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "linux")] #[test] #[cfg(feature = "kaleido")] fn test_save_to_webp() { diff --git a/plotly/src/traces/bar.rs b/plotly/src/traces/bar.rs index 16300ae6..0acaed0d 100644 --- a/plotly/src/traces/bar.rs +++ b/plotly/src/traces/bar.rs @@ -159,7 +159,7 @@ mod tests { .inside_text_anchor(TextAnchor::End) .inside_text_font(Font::new()) .legend_group("legend-group") - .legend_group_title(LegendGroupTitle::new("legend-group-title")) + .legend_group_title("legend-group-title") .marker(Marker::new()) .name("Bar") .offset(5) diff --git a/plotly/src/traces/box_plot.rs b/plotly/src/traces/box_plot.rs index 5b8e9c91..8903c480 100644 --- a/plotly/src/traces/box_plot.rs +++ b/plotly/src/traces/box_plot.rs @@ -283,7 +283,7 @@ mod tests { .jitter(0.5) .line(Line::new()) .legend_group("one") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .lower_fence(vec![0., 1.]) .marker(Marker::new()) .mean(vec![12., 13.]) diff --git a/plotly/src/traces/candlestick.rs b/plotly/src/traces/candlestick.rs index 2fe6a197..64b25a5b 100644 --- a/plotly/src/traces/candlestick.rs +++ b/plotly/src/traces/candlestick.rs @@ -144,7 +144,7 @@ mod tests { .visible(Visible::True) .show_legend(false) .legend_group("group_1") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .opacity(0.3) .text_array(vec!["text", "here"]) .text("text here") diff --git a/plotly/src/traces/contour.rs b/plotly/src/traces/contour.rs index 844f55c2..576e58f1 100644 --- a/plotly/src/traces/contour.rs +++ b/plotly/src/traces/contour.rs @@ -339,8 +339,11 @@ where Box::new(self) } - pub fn legend_group_title(mut self, legend_group_title: LegendGroupTitle) -> Box<Self> { - self.legend_group_title = Some(legend_group_title); + pub fn legend_group_title( + mut self, + legend_group_title: impl Into<LegendGroupTitle>, + ) -> Box<Self> { + self.legend_group_title = Some(legend_group_title.into()); Box::new(self) } @@ -583,7 +586,7 @@ mod tests { .hover_template_array(vec!["ok {1}", "ok {2}"]) .hover_text(vec!["p3", "p4"]) .legend_group("group_1") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .line(Line::new()) .n_contours(5) .name("contour trace") diff --git a/plotly/src/traces/heat_map.rs b/plotly/src/traces/heat_map.rs index 07c216ed..811dcfb6 100644 --- a/plotly/src/traces/heat_map.rs +++ b/plotly/src/traces/heat_map.rs @@ -206,7 +206,7 @@ mod tests { .hover_template_array(vec!["tmpl1", "tmpl2"]) .hover_text(vec!["hov", "er"]) .legend_group("1") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .name("name") .opacity(0.99) .reverse_scale(false) diff --git a/plotly/src/traces/histogram.rs b/plotly/src/traces/histogram.rs index 7ae6dd59..b4b9b97f 100644 --- a/plotly/src/traces/histogram.rs +++ b/plotly/src/traces/histogram.rs @@ -410,7 +410,7 @@ mod tests { .hover_text("hover_text") .hover_text_array(vec!["hover_text_1", "hover_text_2"]) .legend_group("legendgroup") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .marker(Marker::new()) .n_bins_x(5) .n_bins_y(10) diff --git a/plotly/src/traces/image.rs b/plotly/src/traces/image.rs index 4efbf0c9..d081f9b4 100644 --- a/plotly/src/traces/image.rs +++ b/plotly/src/traces/image.rs @@ -408,7 +408,7 @@ mod tests { .name("image name") .visible(Visible::True) .legend_rank(1000) - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .opacity(0.5) .ids(vec!["one"]) .x0(0.0) diff --git a/plotly/src/traces/mesh3d.rs b/plotly/src/traces/mesh3d.rs index fae98c9a..fc45d781 100644 --- a/plotly/src/traces/mesh3d.rs +++ b/plotly/src/traces/mesh3d.rs @@ -493,7 +493,7 @@ mod tests { .show_legend(true) .legend_rank(1000) .legend_group("legend_group") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .opacity(0.5) .ids(vec!["one"]) .face_color(vec!["#ff00ff"]) diff --git a/plotly/src/traces/ohlc.rs b/plotly/src/traces/ohlc.rs index cf1b4e50..7067514f 100644 --- a/plotly/src/traces/ohlc.rs +++ b/plotly/src/traces/ohlc.rs @@ -28,7 +28,7 @@ use crate::{ /// let expected = serde_json::json!({ /// "type": "ohlc", /// "x": ["2022-08-22", "2022-08-23"], -/// "open": [5, 6], +/// "open": [5, 6], /// "high": [8, 10], /// "low": [2, 4], /// "close": [6, 7] @@ -133,7 +133,7 @@ mod test { .hover_text("1") .increasing(Direction::Increasing { line: Line::new() }) .legend_group("legendgroup") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .line(Line::new()) .name("ohlc_trace") .opacity(0.4) diff --git a/plotly/src/traces/sankey.rs b/plotly/src/traces/sankey.rs index 1a53d2c8..be96afa2 100644 --- a/plotly/src/traces/sankey.rs +++ b/plotly/src/traces/sankey.rs @@ -436,7 +436,7 @@ mod tests { .name("sankey") .visible(true) .legend_rank(1000) - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .ids(vec!["one"]) .hover_info(HoverInfo::All) .hover_label(Label::new()) diff --git a/plotly/src/traces/scatter.rs b/plotly/src/traces/scatter.rs index caa2a076..fe9f5b9c 100644 --- a/plotly/src/traces/scatter.rs +++ b/plotly/src/traces/scatter.rs @@ -456,7 +456,7 @@ mod tests { .hover_template_array(vec!["hover_template"]) .ids(vec!["1"]) .legend_group("legend_group") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .line(Line::new()) .marker(Marker::new()) .meta("meta") diff --git a/plotly/src/traces/scatter3d.rs b/plotly/src/traces/scatter3d.rs index 66c72c03..412ba1f8 100644 --- a/plotly/src/traces/scatter3d.rs +++ b/plotly/src/traces/scatter3d.rs @@ -367,7 +367,7 @@ mod tests { .ids(vec!["1"]) .legend_group("legend_group") .legend_rank(1000) - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .line(Line::new()) .marker(Marker::new()) .meta("meta") diff --git a/plotly/src/traces/scatter_mapbox.rs b/plotly/src/traces/scatter_mapbox.rs index 1ee3c164..76348e6a 100644 --- a/plotly/src/traces/scatter_mapbox.rs +++ b/plotly/src/traces/scatter_mapbox.rs @@ -311,7 +311,7 @@ mod tests { .show_legend(true) .legend_rank(1000) .legend_group("legend group") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .opacity(0.5) .mode(Mode::LinesText) .ids(vec!["one"]) diff --git a/plotly/src/traces/scatter_polar.rs b/plotly/src/traces/scatter_polar.rs index ae3ed244..a9101607 100644 --- a/plotly/src/traces/scatter_polar.rs +++ b/plotly/src/traces/scatter_polar.rs @@ -368,7 +368,7 @@ mod tests { .hover_text_array(vec!["hover_text"]) .ids(vec!["1"]) .legend_group("legend_group") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .line(Line::new()) .marker(Marker::new()) .meta("meta") diff --git a/plotly/src/traces/surface.rs b/plotly/src/traces/surface.rs index b4110d3b..3f692543 100644 --- a/plotly/src/traces/surface.rs +++ b/plotly/src/traces/surface.rs @@ -328,7 +328,7 @@ mod tests { .hover_text("hover_text") .hover_text_array(vec!["hover_text_1"]) .legend_group("legend_group") - .legend_group_title(LegendGroupTitle::new("Legend Group Title")) + .legend_group_title("Legend Group Title") .lighting(Lighting::new()) .light_position(Position::new(0, 0, 0)) .name("surface_trace") diff --git a/plotly_derive/src/field_setter.rs b/plotly_derive/src/field_setter.rs index f330be21..ca13d1d3 100644 --- a/plotly_derive/src/field_setter.rs +++ b/plotly_derive/src/field_setter.rs @@ -140,6 +140,8 @@ enum FieldType { OptionString, OptionNumOrString, OptionNumOrStringCollection, + OptionTitle, + OptionLegendGroupTitle, OptionOther(syn::Type), } @@ -202,6 +204,8 @@ impl FieldType { FieldType::OptionNumOrStringCollection => quote![crate::private::NumOrStringCollection], FieldType::OptionOther(inner) => quote![#inner], FieldType::OptionBoxOther(inner) => quote![Box<#inner>], + FieldType::OptionTitle => quote![Title], + FieldType::OptionLegendGroupTitle => quote![LegendGroupTitle], } } @@ -225,6 +229,8 @@ impl FieldType { ["Box", "Color"] => FieldType::OptionBoxColor, ["Box", ..] => FieldType::OptionBoxOther(types.get(2).cloned().unwrap()), ["Vec", "Box", "Color"] => FieldType::OptionVecBoxColor, + ["Title"] => FieldType::OptionTitle, + ["LegendGroupTitle"] => FieldType::OptionLegendGroupTitle, _ => FieldType::OptionOther(types.get(1).cloned().unwrap()), } } @@ -344,6 +350,12 @@ impl FieldReceiver { quote![value.into()], quote![], ), + FieldType::OptionTitle => (quote![impl Into<Title>], quote![value.into()], quote![]), + FieldType::OptionLegendGroupTitle => ( + quote![impl Into<LegendGroupTitle>], + quote![value.into()], + quote![], + ), }; struct ModifyEnum { diff --git a/plotly_kaleido/src/lib.rs b/plotly_kaleido/src/lib.rs index 199c77fe..f6641c25 100644 --- a/plotly_kaleido/src/lib.rs +++ b/plotly_kaleido/src/lib.rs @@ -239,7 +239,8 @@ mod tests { assert_eq!(to_value(kaleido_data).unwrap(), expected); } - #[cfg(not(target_os = "windows"))] + // This seems to fail unpredictably on MacOs. + #[cfg(target_os = "linux")] #[test] fn test_save_png() { let test_plot = create_test_plot(); @@ -250,7 +251,8 @@ mod tests { assert!(std::fs::remove_file(dst.as_path()).is_ok()); } - #[cfg(not(target_os = "windows"))] + // This seems to fail unpredictably on MacOs. + #[cfg(target_os = "linux")] #[test] fn test_save_jpeg() { let test_plot = create_test_plot(); @@ -261,7 +263,8 @@ mod tests { assert!(std::fs::remove_file(dst.as_path()).is_ok()); } - #[cfg(not(target_os = "windows"))] + // This seems to fail unpredictably on MacOs. + #[cfg(target_os = "linux")] #[test] fn test_save_webp() { let test_plot = create_test_plot(); @@ -272,7 +275,8 @@ mod tests { assert!(std::fs::remove_file(dst.as_path()).is_ok()); } - #[cfg(not(target_os = "windows"))] + // This seems to fail unpredictably on MacOs. + #[cfg(target_os = "linux")] #[test] fn test_save_svg() { let test_plot = create_test_plot(); @@ -283,7 +287,8 @@ mod tests { assert!(std::fs::remove_file(dst.as_path()).is_ok()); } - #[cfg(not(target_os = "windows"))] + // This seems to fail unpredictably on MacOs. + #[cfg(target_os = "linux")] #[test] fn test_save_pdf() { let test_plot = create_test_plot(); @@ -294,6 +299,7 @@ mod tests { assert!(std::fs::remove_file(dst.as_path()).is_ok()); } + // This doesn't work for some reason #[test] #[ignore] fn test_save_eps() {