• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

veeso / tui-realm-stdlib / 22147789116

09 Feb 2026 04:20PM UTC coverage: 77.303% (+4.8%) from 72.482%
22147789116

push

github

hasezoey
refactor(table): switch to use "CommonProps"

18 of 90 new or added lines in 1 file covered. (20.0%)

405 existing lines in 20 files now uncovered.

3944 of 5102 relevant lines covered (77.3%)

4.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.61
/src/components/chart/chart.rs
1
//! ## Chart
2
//!
3
//! A component to plot one or more dataset in a cartesian coordinate system
4

5
use std::any::Any;
6

7
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
8
use tuirealm::props::{
9
    AttrValue, Attribute, Borders, Color, PropPayload, PropValue, Props, Style, Title,
10
};
11
use tuirealm::ratatui::text::Line;
12
use tuirealm::ratatui::{
13
    layout::Rect,
14
    text::Span,
15
    widgets::{Axis, Chart as TuiChart, Dataset as TuiDataset},
16
};
17
use tuirealm::{Frame, MockComponent, State};
18

19
// -- Props
20
use super::dataset::ChartDataset;
21
use crate::prop_ext::CommonProps;
22
use crate::props::{
23
    CHART_X_BOUNDS, CHART_X_LABELS, CHART_X_STYLE, CHART_X_TITLE, CHART_Y_BOUNDS, CHART_Y_LABELS,
24
    CHART_Y_STYLE, CHART_Y_TITLE,
25
};
26

27
/// ### ChartStates
28
///
29
/// chart states
30
#[derive(Default)]
31
pub struct ChartStates {
32
    pub cursor: usize,
33
    pub data: Vec<ChartDataset>,
34
}
35

36
impl ChartStates {
37
    /// ### move_cursor_left
38
    ///
39
    /// Move cursor to the left
40
    pub fn move_cursor_left(&mut self) {
6✔
41
        if self.cursor > 0 {
6✔
42
            self.cursor -= 1;
4✔
43
        }
4✔
44
    }
6✔
45

46
    /// ### move_cursor_right
47
    ///
48
    /// Move cursor to the right
49
    pub fn move_cursor_right(&mut self, data_len: usize) {
6✔
50
        if data_len > 0 && self.cursor + 1 < data_len {
6✔
51
            self.cursor += 1;
4✔
52
        }
4✔
53
    }
6✔
54

55
    /// ### reset_cursor
56
    ///
57
    /// Reset cursor to 0
58
    pub fn reset_cursor(&mut self) {
18✔
59
        self.cursor = 0;
18✔
60
    }
18✔
61

62
    /// ### cursor_at_end
63
    ///
64
    /// Move cursor to the end of the chart
65
    pub fn cursor_at_end(&mut self, data_len: usize) {
6✔
66
        if data_len > 0 {
6✔
67
            self.cursor = data_len - 1;
6✔
68
        } else {
6✔
69
            self.cursor = 0;
×
UNCOV
70
        }
×
71
    }
6✔
72
}
73

74
// -- component
75

76
/// ### Chart
77
///
78
/// A component to display a chart on a cartesian coordinate system.
79
/// The chart can work both in "active" and "disabled" mode.
80
///
81
/// #### Disabled mode
82
///
83
/// When in disabled mode, the chart won't be interactive, so you won't be able to move through data using keys.
84
/// If you have more data than the maximum amount of bars that can be displayed, you'll have to update data to display the remaining entries
85
///
86
/// #### Active mode
87
///
88
/// While in active mode (default) you can put as many entries as you wish. You can move with arrows and END/HOME keys
89
#[derive(Default)]
90
#[must_use]
91
pub struct Chart {
92
    common: CommonProps,
93
    props: Props,
94
    pub states: ChartStates,
95
}
96

97
impl Chart {
98
    /// Set the main foreground color. This may get overwritten by individual text styles.
99
    pub fn foreground(mut self, fg: Color) -> Self {
2✔
100
        self.props.set(Attribute::Foreground, AttrValue::Color(fg));
2✔
101
        self
2✔
102
    }
2✔
103

104
    /// Set the main background color. This may get overwritten by individual text styles.
105
    pub fn background(mut self, bg: Color) -> Self {
2✔
106
        self.props.set(Attribute::Background, AttrValue::Color(bg));
2✔
107
        self
2✔
108
    }
2✔
109

110
    /// Set the main style. This may get overwritten by individual text styles.
111
    ///
112
    /// This option will overwrite any previous [`foreground`](Self::foreground), [`background`](Self::background) and [`modifiers`](Self::modifiers)!
UNCOV
113
    pub fn style(mut self, style: Style) -> Self {
×
UNCOV
114
        self.attr(Attribute::Style, AttrValue::Style(style));
×
UNCOV
115
        self
×
UNCOV
116
    }
×
117

118
    /// Add a border to the component.
119
    pub fn borders(mut self, b: Borders) -> Self {
2✔
120
        self.props.set(Attribute::Borders, AttrValue::Borders(b));
2✔
121
        self
2✔
122
    }
2✔
123

124
    /// Add a title to the component.
125
    pub fn title<T: Into<Title>>(mut self, title: T) -> Self {
2✔
126
        self.attr(Attribute::Title, AttrValue::Title(title.into()));
2✔
127
        self
2✔
128
    }
2✔
129

130
    /// Set whether this component should appear "disabled" (or also known as "locked").
131
    pub fn disabled(mut self, disabled: bool) -> Self {
2✔
132
        self.attr(Attribute::Disabled, AttrValue::Flag(disabled));
2✔
133
        self
2✔
134
    }
2✔
135

136
    /// Set the inactive style for the whole component
UNCOV
137
    pub fn inactive(mut self, s: Style) -> Self {
×
UNCOV
138
        self.props.set(Attribute::FocusStyle, AttrValue::Style(s));
×
UNCOV
139
        self
×
UNCOV
140
    }
×
141

142
    /// Builder-Style function to set the initial data
143
    pub fn data(mut self, data: impl IntoIterator<Item = ChartDataset>) -> Self {
4✔
144
        self.set_data(data.into_iter().collect());
4✔
145
        self
4✔
146
    }
4✔
147

148
    /// Set the bounds for the X-axis.
149
    pub fn x_bounds(mut self, bounds: (f64, f64)) -> Self {
2✔
150
        self.props.set(
2✔
151
            Attribute::Custom(CHART_X_BOUNDS),
2✔
152
            AttrValue::Payload(PropPayload::Pair((
2✔
153
                PropValue::F64(bounds.0),
2✔
154
                PropValue::F64(bounds.1),
2✔
155
            ))),
2✔
156
        );
1✔
157
        self
2✔
158
    }
2✔
159

160
    /// Set the bounds for the Y-axis.
161
    pub fn y_bounds(mut self, bounds: (f64, f64)) -> Self {
2✔
162
        self.props.set(
2✔
163
            Attribute::Custom(CHART_Y_BOUNDS),
2✔
164
            AttrValue::Payload(PropPayload::Pair((
2✔
165
                PropValue::F64(bounds.0),
2✔
166
                PropValue::F64(bounds.1),
2✔
167
            ))),
2✔
168
        );
1✔
169
        self
2✔
170
    }
2✔
171

172
    /// Set labels for the X-Axis, see [`Axis::labels`].
173
    pub fn x_labels(mut self, labels: &[&str]) -> Self {
2✔
174
        self.attr(
2✔
175
            Attribute::Custom(CHART_X_LABELS),
2✔
176
            AttrValue::Payload(PropPayload::Vec(
1✔
177
                labels
2✔
178
                    .iter()
2✔
179
                    .map(|x| PropValue::Str((*x).to_string()))
24✔
180
                    .collect(),
2✔
181
            )),
1✔
182
        );
1✔
183
        self
2✔
184
    }
2✔
185

186
    /// Set labels for the Y-Axis, see [`Axis::labels`].
187
    pub fn y_labels(mut self, labels: &[&str]) -> Self {
2✔
188
        self.attr(
2✔
189
            Attribute::Custom(CHART_Y_LABELS),
2✔
190
            AttrValue::Payload(PropPayload::Vec(
1✔
191
                labels
2✔
192
                    .iter()
2✔
193
                    .map(|x| PropValue::Str((*x).to_string()))
18✔
194
                    .collect(),
2✔
195
            )),
1✔
196
        );
1✔
197
        self
2✔
198
    }
2✔
199

200
    /// Set a specific style for the X-Axis.
201
    pub fn x_style(mut self, s: Style) -> Self {
2✔
202
        self.attr(Attribute::Custom(CHART_X_STYLE), AttrValue::Style(s));
2✔
203
        self
2✔
204
    }
2✔
205

206
    /// Set a specific style for the Y-Axis.
207
    pub fn y_style(mut self, s: Style) -> Self {
2✔
208
        self.attr(Attribute::Custom(CHART_Y_STYLE), AttrValue::Style(s));
2✔
209
        self
2✔
210
    }
2✔
211

212
    /// Give the X axis a title
213
    pub fn x_title<S: Into<String>>(mut self, t: S) -> Self {
2✔
214
        self.props.set(
2✔
215
            Attribute::Custom(CHART_X_TITLE),
2✔
216
            AttrValue::String(t.into()),
2✔
217
        );
1✔
218
        self
2✔
219
    }
2✔
220

221
    /// Give the Y axis a title
222
    pub fn y_title<S: Into<String>>(mut self, t: S) -> Self {
2✔
223
        self.props.set(
2✔
224
            Attribute::Custom(CHART_Y_TITLE),
2✔
225
            AttrValue::String(t.into()),
2✔
226
        );
1✔
227
        self
2✔
228
    }
2✔
229

230
    fn set_data(&mut self, data: Vec<ChartDataset>) {
14✔
231
        self.states.data = data;
14✔
232
        self.states.reset_cursor();
14✔
233
    }
14✔
234

235
    fn is_disabled(&self) -> bool {
10✔
236
        self.props
10✔
237
            .get_or(Attribute::Disabled, AttrValue::Flag(false))
10✔
238
            .unwrap_flag()
10✔
239
    }
10✔
240

241
    /// Get the maximum len among the datasets
242
    fn max_dataset_len(&self) -> usize {
8✔
243
        self.states
8✔
244
            .data
8✔
245
            .iter()
8✔
246
            .map(|v| v.get_data().len())
12✔
247
            .max()
8✔
248
            .unwrap_or(0)
8✔
249
    }
8✔
250

251
    /// Get data to be displayed, starting from provided index at `start`
252
    fn get_tui_data(&self, start: usize) -> Vec<TuiDataset<'_>> {
4✔
253
        self.states
4✔
254
            .data
4✔
255
            .iter()
4✔
256
            .map(|x| x.as_tuichart(start))
6✔
257
            .collect()
4✔
258
    }
4✔
259

260
    /// Try downcasting the given [`Box<Any>`] into a concrete type.
261
    fn try_downcast(value: Box<dyn Any + Send + Sync>) -> Option<Vec<ChartDataset>> {
10✔
262
        value
10✔
263
            .downcast::<Vec<ChartDataset>>()
10✔
264
            .map(|v| *v)
10✔
265
            .or_else(|value| value.downcast::<ChartDataset>().map(|v| vec![*v]))
10✔
266
            .ok()
10✔
267
    }
10✔
268

269
    /// Get our data from a [`AttrValue`].
270
    fn data_from_attr(&mut self, attr: AttrValue) {
10✔
271
        if let AttrValue::Payload(PropPayload::Any(val)) = attr {
10✔
272
            if let Some(data) = Self::try_downcast(val) {
10✔
273
                self.set_data(data);
10✔
274
            }
10✔
275
        }
×
276
    }
10✔
277

278
    /// Clone our data into a [`AttrValue`].
279
    fn data_to_attr(&self) -> AttrValue {
×
280
        AttrValue::Payload(PropPayload::Any(Box::new(self.states.data.to_vec())))
×
281
    }
×
282
}
283

284
impl MockComponent for Chart {
285
    fn view(&mut self, render: &mut Frame, area: Rect) {
×
286
        if !self.common.display {
×
287
            return;
×
288
        }
×
289

290
        let normal_style = self.common.style;
×
291

292
        // Create widget
293
        // -- x axis
294
        let mut x_axis: Axis = Axis::default();
×
UNCOV
295
        if let Some((PropValue::F64(floor), PropValue::F64(ceil))) = self
×
UNCOV
296
            .props
×
297
            .get(Attribute::Custom(CHART_X_BOUNDS))
×
298
            .map(|x| x.unwrap_payload().unwrap_pair())
×
299
        {
×
300
            let why_using_vecs_when_you_can_use_useless_arrays: [f64; 2] = [floor, ceil];
×
301
            x_axis = x_axis.bounds(why_using_vecs_when_you_can_use_useless_arrays);
×
302
        }
×
303
        if let Some(PropPayload::Vec(labels)) = self
×
304
            .props
×
305
            .get(Attribute::Custom(CHART_X_LABELS))
×
306
            .map(|x| x.unwrap_payload())
×
307
        {
308
            x_axis = x_axis.labels(labels.iter().cloned().map(|x| Line::from(x.unwrap_str())));
×
309
        }
×
UNCOV
310
        if let Some(s) = self
×
311
            .props
×
312
            .get(Attribute::Custom(CHART_X_STYLE))
×
313
            .map(|x| x.unwrap_style())
×
314
        {
×
315
            x_axis = x_axis.style(s);
×
316
        }
×
317
        if let Some(title) = self
×
318
            .props
×
319
            .get(Attribute::Custom(CHART_X_TITLE))
×
320
            .map(|x| x.unwrap_string())
×
321
        {
×
322
            x_axis = x_axis.title(Span::styled(title, normal_style));
×
323
        }
×
324
        // -- y axis
325
        let mut y_axis: Axis = Axis::default();
×
326
        if let Some((PropValue::F64(floor), PropValue::F64(ceil))) = self
×
UNCOV
327
            .props
×
328
            .get(Attribute::Custom(CHART_Y_BOUNDS))
×
329
            .map(|x| x.unwrap_payload().unwrap_pair())
×
330
        {
×
331
            let why_using_vecs_when_you_can_use_useless_arrays: [f64; 2] = [floor, ceil];
×
332
            y_axis = y_axis.bounds(why_using_vecs_when_you_can_use_useless_arrays);
×
333
        }
×
334
        if let Some(PropPayload::Vec(labels)) = self
×
335
            .props
×
336
            .get(Attribute::Custom(CHART_Y_LABELS))
×
337
            .map(|x| x.unwrap_payload())
×
338
        {
339
            y_axis = y_axis.labels(labels.iter().cloned().map(|x| Line::from(x.unwrap_str())));
×
340
        }
×
UNCOV
341
        if let Some(s) = self
×
342
            .props
×
343
            .get(Attribute::Custom(CHART_Y_STYLE))
×
344
            .map(|x| x.unwrap_style())
×
345
        {
×
346
            y_axis = y_axis.style(s);
×
347
        }
×
348
        if let Some(title) = self
×
349
            .props
×
350
            .get(Attribute::Custom(CHART_Y_TITLE))
×
351
            .map(|x| x.unwrap_string())
×
352
        {
×
353
            y_axis = y_axis.title(Span::styled(title, normal_style));
×
354
        }
×
355

356
        // Get data
357
        let data: Vec<TuiDataset> = self.get_tui_data(self.states.cursor);
×
358
        // Build widget
359
        let mut widget: TuiChart = TuiChart::new(data)
×
UNCOV
360
            .style(normal_style)
×
361
            .x_axis(x_axis)
×
362
            .y_axis(y_axis);
×
363

364
        if let Some(block) = self.common.get_block() {
×
365
            widget = widget.block(block);
×
UNCOV
366
        }
×
367

368
        // Render
369
        render.render_widget(widget, area);
×
UNCOV
370
    }
×
371

372
    fn query(&self, attr: Attribute) -> Option<AttrValue> {
×
373
        if let Some(value) = self.common.get(attr) {
×
374
            return Some(value);
×
UNCOV
375
        }
×
376

377
        if attr == Attribute::Dataset {
×
UNCOV
378
            return Some(self.data_to_attr());
×
UNCOV
379
        }
×
380

UNCOV
381
        self.props.get(attr)
×
UNCOV
382
    }
×
383

384
    fn attr(&mut self, attr: Attribute, value: AttrValue) {
22✔
385
        if let Some(value) = self.common.set(attr, value) {
22✔
386
            if attr == Attribute::Dataset {
20✔
387
                self.data_from_attr(value);
10✔
388
                return;
10✔
389
            }
10✔
390
            self.props.set(attr, value);
10✔
391
        }
2✔
392
    }
22✔
393

394
    fn perform(&mut self, cmd: Cmd) -> CmdResult {
8✔
395
        if !self.is_disabled() {
8✔
396
            match cmd {
4✔
397
                Cmd::Move(Direction::Left) => {
2✔
398
                    self.states.move_cursor_left();
2✔
399
                }
2✔
400
                Cmd::Move(Direction::Right) => {
2✔
401
                    self.states.move_cursor_right(self.max_dataset_len());
2✔
402
                }
2✔
403
                Cmd::GoTo(Position::Begin) => {
2✔
404
                    self.states.reset_cursor();
2✔
405
                }
2✔
406
                Cmd::GoTo(Position::End) => {
2✔
407
                    self.states.cursor_at_end(self.max_dataset_len());
2✔
408
                }
2✔
UNCOV
409
                _ => {}
×
410
            }
UNCOV
411
        }
×
412
        CmdResult::None
8✔
413
    }
8✔
414

415
    fn state(&self) -> State {
2✔
416
        State::None
2✔
417
    }
2✔
418
}
419

420
#[cfg(test)]
421
mod test {
422

423
    use super::*;
424

425
    use pretty_assertions::assert_eq;
426
    use tuirealm::{
427
        props::HorizontalAlignment,
428
        ratatui::{symbols::Marker, widgets::GraphType},
429
    };
430

431
    #[test]
432
    fn test_components_chart_states() {
2✔
433
        let mut states: ChartStates = ChartStates::default();
2✔
434
        assert_eq!(states.cursor, 0);
2✔
435
        // Incr
436
        states.move_cursor_right(2);
2✔
437
        assert_eq!(states.cursor, 1);
2✔
438
        // At end
439
        states.move_cursor_right(2);
2✔
440
        assert_eq!(states.cursor, 1);
2✔
441
        // Decr
442
        states.move_cursor_left();
2✔
443
        assert_eq!(states.cursor, 0);
2✔
444
        // At begin
445
        states.move_cursor_left();
2✔
446
        assert_eq!(states.cursor, 0);
2✔
447
        // Move at end
448
        states.cursor_at_end(3);
2✔
449
        assert_eq!(states.cursor, 2);
2✔
450
        states.reset_cursor();
2✔
451
        assert_eq!(states.cursor, 0);
2✔
452
    }
2✔
453

454
    #[test]
455
    fn test_components_chart() {
2✔
456
        let mut component: Chart = Chart::default()
2✔
457
            .disabled(false)
2✔
458
            .background(Color::Reset)
2✔
459
            .foreground(Color::Reset)
2✔
460
            .borders(Borders::default())
2✔
461
            .title(
2✔
462
                Title::from("average temperatures in Udine").alignment(HorizontalAlignment::Center),
2✔
463
            )
1✔
464
            .x_bounds((0.0, 11.0))
2✔
465
            .x_labels(&[
2✔
466
                "january",
2✔
467
                "february",
2✔
468
                "march",
2✔
469
                "april",
2✔
470
                "may",
2✔
471
                "june",
2✔
472
                "july",
2✔
473
                "august",
2✔
474
                "september",
2✔
475
                "october",
2✔
476
                "november",
2✔
477
                "december",
2✔
478
            ])
2✔
479
            .x_style(Style::default().fg(Color::LightBlue))
2✔
480
            .x_title("Temperature (°C)")
2✔
481
            .y_bounds((-5.0, 35.0))
2✔
482
            .y_labels(&["-5", "0", "5", "10", "15", "20", "25", "30", "35"])
2✔
483
            .y_style(Style::default().fg(Color::LightYellow))
2✔
484
            .y_title("Month")
2✔
485
            .data([
2✔
486
                ChartDataset::default()
2✔
487
                    .name("Minimum")
2✔
488
                    .graph_type(GraphType::Scatter)
2✔
489
                    .marker(Marker::Braille)
2✔
490
                    .style(Style::default().fg(Color::Cyan))
2✔
491
                    .data(vec![
2✔
492
                        (0.0, -1.0),
2✔
493
                        (1.0, 1.0),
2✔
494
                        (2.0, 3.0),
2✔
495
                        (3.0, 7.0),
2✔
496
                        (4.0, 11.0),
2✔
497
                        (5.0, 15.0),
2✔
498
                        (6.0, 17.0),
2✔
499
                        (7.0, 17.0),
2✔
500
                        (8.0, 13.0),
2✔
501
                        (9.0, 9.0),
2✔
502
                        (10.0, 4.0),
2✔
503
                        (11.0, 0.0),
2✔
504
                    ]),
2✔
505
                ChartDataset::default()
2✔
506
                    .name("Maximum")
2✔
507
                    .graph_type(GraphType::Line)
2✔
508
                    .marker(Marker::Dot)
2✔
509
                    .style(Style::default().fg(Color::LightRed))
2✔
510
                    .data(vec![
2✔
511
                        (0.0, 7.0),
2✔
512
                        (1.0, 9.0),
2✔
513
                        (2.0, 13.0),
2✔
514
                        (3.0, 17.0),
2✔
515
                        (4.0, 22.0),
2✔
516
                        (5.0, 25.0),
2✔
517
                        (6.0, 28.0),
2✔
518
                        (7.0, 28.0),
2✔
519
                        (8.0, 24.0),
2✔
520
                        (9.0, 19.0),
2✔
521
                        (10.0, 13.0),
2✔
522
                        (11.0, 8.0),
2✔
523
                    ]),
2✔
524
            ]);
2✔
525
        // Commands
1✔
526
        assert_eq!(component.state(), State::None);
2✔
527
        // -> Right
528
        assert_eq!(
2✔
529
            component.perform(Cmd::Move(Direction::Right)),
2✔
530
            CmdResult::None
1✔
531
        );
1✔
532
        assert_eq!(component.states.cursor, 1);
2✔
533
        // <- Left
534
        assert_eq!(
2✔
535
            component.perform(Cmd::Move(Direction::Left)),
2✔
536
            CmdResult::None
1✔
537
        );
1✔
538
        assert_eq!(component.states.cursor, 0);
2✔
539
        // End
540
        assert_eq!(component.perform(Cmd::GoTo(Position::End)), CmdResult::None);
2✔
541
        assert_eq!(component.states.cursor, 11);
2✔
542
        // Home
543
        assert_eq!(
2✔
544
            component.perform(Cmd::GoTo(Position::Begin)),
2✔
545
            CmdResult::None
1✔
546
        );
1✔
547
        assert_eq!(component.states.cursor, 0);
2✔
548
        // component funcs
549
        assert_eq!(component.max_dataset_len(), 12);
2✔
550
        assert_eq!(component.is_disabled(), false);
2✔
551
        assert_eq!(component.get_tui_data(2).len(), 2);
2✔
552

553
        let comp = Chart::default().data([ChartDataset::default()
2✔
554
            .name("Maximum")
2✔
555
            .graph_type(GraphType::Line)
2✔
556
            .marker(Marker::Dot)
2✔
557
            .style(Style::default().fg(Color::LightRed))
2✔
558
            .data(vec![(0.0, 7.0)])]);
2✔
559
        assert!(!comp.get_tui_data(0).is_empty());
2✔
560

561
        // Update and test empty data
562
        component.states.cursor_at_end(12);
2✔
563
        component.attr(
2✔
564
            Attribute::Dataset,
2✔
565
            AttrValue::Payload(PropPayload::Any(Box::new(Vec::<ChartDataset>::new()))),
2✔
566
        );
1✔
567
        assert_eq!(component.max_dataset_len(), 0);
2✔
568
        // Cursor is reset
569
        assert_eq!(component.states.cursor, 0);
2✔
570
    }
2✔
571

572
    #[test]
573
    fn allowed_dataset_attrs() {
2✔
574
        let mut component = Chart::default();
2✔
575
        assert!(component.states.data.is_empty());
2✔
576

577
        // allow overwriting multiple datasets at once
578
        component.attr(
2✔
579
            Attribute::Dataset,
2✔
580
            AttrValue::Payload(PropPayload::Any(Box::new(vec![ChartDataset::default()]))),
2✔
581
        );
1✔
582
        assert_eq!(component.states.data.len(), 1);
2✔
583

584
        component.attr(
2✔
585
            Attribute::Dataset,
2✔
586
            AttrValue::Payload(PropPayload::Any(Box::new(vec![ChartDataset::default()]))),
2✔
587
        );
1✔
588
        assert_eq!(component.states.data.len(), 1);
2✔
589

590
        // allow overwriting using with just one dataset
591
        component.attr(
2✔
592
            Attribute::Dataset,
2✔
593
            AttrValue::Payload(PropPayload::Any(Box::new(ChartDataset::default()))),
2✔
594
        );
1✔
595
        assert_eq!(component.states.data.len(), 1);
2✔
596

597
        component.attr(
2✔
598
            Attribute::Dataset,
2✔
599
            AttrValue::Payload(PropPayload::Any(Box::new(ChartDataset::default()))),
2✔
600
        );
1✔
601
        assert_eq!(component.states.data.len(), 1);
2✔
602
    }
2✔
603
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc