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

veeso / tui-realm-stdlib / 20461661064

23 Dec 2025 01:12PM UTC coverage: 70.347% (+0.2%) from 70.169%
20461661064

Pull #49

github

web-flow
Merge bdda8b81d into 9528c3045
Pull Request #49: Move `Dataset` over to `Chart` and rename to `ChartDataset`

367 of 428 new or added lines in 8 files covered. (85.75%)

3 existing lines in 2 files now uncovered.

3288 of 4674 relevant lines covered (70.35%)

2.16 hits per line

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

71.91
/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
    Alignment, AttrValue, Attribute, Borders, Color, PropPayload, PropValue, Props, Style,
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::props::{
22
    CHART_X_BOUNDS, CHART_X_LABELS, CHART_X_STYLE, CHART_X_TITLE, CHART_Y_BOUNDS, CHART_Y_LABELS,
23
    CHART_Y_STYLE, CHART_Y_TITLE,
24
};
25

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

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

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

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

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

73
// -- component
74

75
/// ### Chart
76
///
77
/// A component to display a chart on a cartesian coordinate system.
78
/// The chart can work both in "active" and "disabled" mode.
79
///
80
/// #### Disabled mode
81
///
82
/// When in disabled mode, the chart won't be interactive, so you won't be able to move through data using keys.
83
/// 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
84
///
85
/// #### Active mode
86
///
87
/// While in active mode (default) you can put as many entries as you wish. You can move with arrows and END/HOME keys
88
#[derive(Default)]
89
#[must_use]
90
pub struct Chart {
91
    props: Props,
92
    pub states: ChartStates,
93
}
94

95
impl Chart {
96
    pub fn foreground(mut self, fg: Color) -> Self {
1✔
97
        self.props.set(Attribute::Foreground, AttrValue::Color(fg));
1✔
98
        self
1✔
99
    }
1✔
100

101
    pub fn background(mut self, bg: Color) -> Self {
1✔
102
        self.props.set(Attribute::Background, AttrValue::Color(bg));
1✔
103
        self
1✔
104
    }
1✔
105

106
    pub fn borders(mut self, b: Borders) -> Self {
1✔
107
        self.props.set(Attribute::Borders, AttrValue::Borders(b));
1✔
108
        self
1✔
109
    }
1✔
110

111
    /// Set a title for the Block
112
    pub fn title<S: Into<String>>(mut self, t: S, a: Alignment) -> Self {
1✔
113
        self.props
1✔
114
            .set(Attribute::Title, AttrValue::Title((t.into(), a)));
1✔
115
        self
1✔
116
    }
1✔
117

118
    pub fn disabled(mut self, disabled: bool) -> Self {
1✔
119
        self.attr(Attribute::Disabled, AttrValue::Flag(disabled));
1✔
120
        self
1✔
121
    }
1✔
122

123
    /// Set the inactive style for the whole component
124
    pub fn inactive(mut self, s: Style) -> Self {
×
125
        self.props.set(Attribute::FocusStyle, AttrValue::Style(s));
×
126
        self
×
127
    }
×
128

129
    /// Builder-Style function to set the initial data
130
    pub fn data(mut self, data: impl IntoIterator<Item = ChartDataset>) -> Self {
2✔
131
        self.set_data(data.into_iter().collect());
2✔
132
        self
2✔
133
    }
2✔
134

135
    pub fn x_bounds(mut self, bounds: (f64, f64)) -> Self {
1✔
136
        self.props.set(
1✔
137
            Attribute::Custom(CHART_X_BOUNDS),
1✔
138
            AttrValue::Payload(PropPayload::Tup2((
1✔
139
                PropValue::F64(bounds.0),
1✔
140
                PropValue::F64(bounds.1),
1✔
141
            ))),
1✔
142
        );
143
        self
1✔
144
    }
1✔
145

146
    pub fn y_bounds(mut self, bounds: (f64, f64)) -> Self {
1✔
147
        self.props.set(
1✔
148
            Attribute::Custom(CHART_Y_BOUNDS),
1✔
149
            AttrValue::Payload(PropPayload::Tup2((
1✔
150
                PropValue::F64(bounds.0),
1✔
151
                PropValue::F64(bounds.1),
1✔
152
            ))),
1✔
153
        );
154
        self
1✔
155
    }
1✔
156

157
    pub fn x_labels(mut self, labels: &[&str]) -> Self {
1✔
158
        self.attr(
1✔
159
            Attribute::Custom(CHART_X_LABELS),
1✔
160
            AttrValue::Payload(PropPayload::Vec(
161
                labels
1✔
162
                    .iter()
1✔
163
                    .map(|x| PropValue::Str((*x).to_string()))
12✔
164
                    .collect(),
1✔
165
            )),
166
        );
167
        self
1✔
168
    }
1✔
169

170
    pub fn y_labels(mut self, labels: &[&str]) -> Self {
1✔
171
        self.attr(
1✔
172
            Attribute::Custom(CHART_Y_LABELS),
1✔
173
            AttrValue::Payload(PropPayload::Vec(
174
                labels
1✔
175
                    .iter()
1✔
176
                    .map(|x| PropValue::Str((*x).to_string()))
9✔
177
                    .collect(),
1✔
178
            )),
179
        );
180
        self
1✔
181
    }
1✔
182

183
    pub fn x_style(mut self, s: Style) -> Self {
1✔
184
        self.attr(Attribute::Custom(CHART_X_STYLE), AttrValue::Style(s));
1✔
185
        self
1✔
186
    }
1✔
187

188
    pub fn y_style(mut self, s: Style) -> Self {
1✔
189
        self.attr(Attribute::Custom(CHART_Y_STYLE), AttrValue::Style(s));
1✔
190
        self
1✔
191
    }
1✔
192

193
    /// Give the X axis a title
194
    pub fn x_title<S: Into<String>>(mut self, t: S) -> Self {
1✔
195
        self.props.set(
1✔
196
            Attribute::Custom(CHART_X_TITLE),
1✔
197
            AttrValue::String(t.into()),
1✔
198
        );
199
        self
1✔
200
    }
1✔
201

202
    /// Give the Y axis a title
203
    pub fn y_title<S: Into<String>>(mut self, t: S) -> Self {
1✔
204
        self.props.set(
1✔
205
            Attribute::Custom(CHART_Y_TITLE),
1✔
206
            AttrValue::String(t.into()),
1✔
207
        );
208
        self
1✔
209
    }
1✔
210

211
    fn set_data(&mut self, data: Vec<ChartDataset>) {
7✔
212
        self.states.data = data;
7✔
213
        self.states.reset_cursor();
7✔
214
    }
7✔
215

216
    fn is_disabled(&self) -> bool {
5✔
217
        self.props
5✔
218
            .get_or(Attribute::Disabled, AttrValue::Flag(false))
5✔
219
            .unwrap_flag()
5✔
220
    }
5✔
221

222
    /// Get the maximum len among the datasets
223
    fn max_dataset_len(&self) -> usize {
4✔
224
        self.states
4✔
225
            .data
4✔
226
            .iter()
4✔
227
            .map(|v| v.get_data().len())
6✔
228
            .max()
4✔
229
            .unwrap_or(0)
4✔
230
    }
4✔
231

232
    /// Get data to be displayed, starting from provided index at `start`
233
    fn get_tui_data(&mut self, start: usize) -> Vec<TuiDataset<'_>> {
2✔
234
        self.states
2✔
235
            .data
2✔
236
            .iter()
2✔
237
            .map(|x| x.as_tuichart(start))
3✔
238
            .collect()
2✔
239
    }
2✔
240

241
    /// Try downcasting the given [`Box<Any>`] into a concrete type.
242
    fn try_downcast(value: Box<dyn Any + Send + Sync>) -> Option<Vec<ChartDataset>> {
5✔
243
        value
5✔
244
            .downcast::<Vec<ChartDataset>>()
5✔
245
            .map(|v| *v)
5✔
246
            .or_else(|value| value.downcast::<ChartDataset>().map(|v| vec![*v]))
5✔
247
            .ok()
5✔
248
    }
5✔
249

250
    /// Get our data from a [`AttrValue`].
251
    fn data_from_attr(&mut self, attr: AttrValue) {
5✔
252
        match attr {
5✔
NEW
253
            AttrValue::Dataset(_) => unimplemented!(), // to be removed soon https://github.com/veeso/tui-realm/pull/139
×
254
            AttrValue::Payload(PropPayload::Any(val)) => {
5✔
255
                if let Some(data) = Self::try_downcast(val) {
5✔
256
                    self.set_data(data);
5✔
257
                }
5✔
258
            }
NEW
259
            _ => (),
×
260
        }
261
    }
5✔
262

263
    /// Clone our data into a [`AttrValue`].
NEW
264
    fn data_to_attr(&self) -> AttrValue {
×
NEW
265
        AttrValue::Payload(PropPayload::Any(Box::new(self.states.data.to_vec())))
×
266
    }
×
267
}
268

269
impl MockComponent for Chart {
270
    fn view(&mut self, render: &mut Frame, area: Rect) {
×
271
        if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) == AttrValue::Flag(true) {
×
272
            let foreground = self
×
273
                .props
×
274
                .get_or(Attribute::Foreground, AttrValue::Color(Color::Reset))
×
275
                .unwrap_color();
×
276
            let background = self
×
277
                .props
×
278
                .get_or(Attribute::Background, AttrValue::Color(Color::Reset))
×
279
                .unwrap_color();
×
280
            let borders = self
×
281
                .props
×
282
                .get_or(Attribute::Borders, AttrValue::Borders(Borders::default()))
×
283
                .unwrap_borders();
×
284
            let title = self
×
285
                .props
×
286
                .get_ref(Attribute::Title)
×
287
                .and_then(|x| x.as_title())
×
288
                // this needs to be cloned as "self" is later mutably borrowed, while this immutably borrows "self"
289
                .cloned();
×
290
            let focus = self
×
291
                .props
×
292
                .get_or(Attribute::Focus, AttrValue::Flag(false))
×
293
                .unwrap_flag();
×
294
            let inactive_style = self
×
295
                .props
×
296
                .get(Attribute::FocusStyle)
×
297
                .map(|x| x.unwrap_style());
×
298
            let normal_style = Style::default().fg(foreground).bg(background);
×
299
            let active: bool = if self.is_disabled() { true } else { focus };
×
300
            let div = crate::utils::get_block(borders, title.as_ref(), active, inactive_style);
×
301
            // Create widget
302
            // -- x axis
303
            let mut x_axis: Axis = Axis::default();
×
304
            if let Some((PropValue::F64(floor), PropValue::F64(ceil))) = self
×
305
                .props
×
306
                .get(Attribute::Custom(CHART_X_BOUNDS))
×
307
                .map(|x| x.unwrap_payload().unwrap_tup2())
×
308
            {
×
309
                let why_using_vecs_when_you_can_use_useless_arrays: [f64; 2] = [floor, ceil];
×
310
                x_axis = x_axis.bounds(why_using_vecs_when_you_can_use_useless_arrays);
×
311
            }
×
312
            if let Some(PropPayload::Vec(labels)) = self
×
313
                .props
×
314
                .get(Attribute::Custom(CHART_X_LABELS))
×
315
                .map(|x| x.unwrap_payload())
×
316
            {
317
                x_axis = x_axis.labels(labels.iter().cloned().map(|x| Line::from(x.unwrap_str())));
×
318
            }
×
319
            if let Some(s) = self
×
320
                .props
×
321
                .get(Attribute::Custom(CHART_X_STYLE))
×
322
                .map(|x| x.unwrap_style())
×
323
            {
×
324
                x_axis = x_axis.style(s);
×
325
            }
×
326
            if let Some(title) = self
×
327
                .props
×
328
                .get(Attribute::Custom(CHART_X_TITLE))
×
329
                .map(|x| x.unwrap_string())
×
330
            {
×
331
                x_axis = x_axis.title(Span::styled(title, normal_style));
×
332
            }
×
333
            // -- y axis
334
            let mut y_axis: Axis = Axis::default();
×
335
            if let Some((PropValue::F64(floor), PropValue::F64(ceil))) = self
×
336
                .props
×
337
                .get(Attribute::Custom(CHART_Y_BOUNDS))
×
338
                .map(|x| x.unwrap_payload().unwrap_tup2())
×
339
            {
×
340
                let why_using_vecs_when_you_can_use_useless_arrays: [f64; 2] = [floor, ceil];
×
341
                y_axis = y_axis.bounds(why_using_vecs_when_you_can_use_useless_arrays);
×
342
            }
×
343
            if let Some(PropPayload::Vec(labels)) = self
×
344
                .props
×
345
                .get(Attribute::Custom(CHART_Y_LABELS))
×
346
                .map(|x| x.unwrap_payload())
×
347
            {
348
                y_axis = y_axis.labels(labels.iter().cloned().map(|x| Line::from(x.unwrap_str())));
×
349
            }
×
350
            if let Some(s) = self
×
351
                .props
×
352
                .get(Attribute::Custom(CHART_Y_STYLE))
×
353
                .map(|x| x.unwrap_style())
×
354
            {
×
355
                y_axis = y_axis.style(s);
×
356
            }
×
357
            if let Some(title) = self
×
358
                .props
×
359
                .get(Attribute::Custom(CHART_Y_TITLE))
×
360
                .map(|x| x.unwrap_string())
×
361
            {
×
362
                y_axis = y_axis.title(Span::styled(title, normal_style));
×
363
            }
×
364
            // Get data
NEW
365
            let data: Vec<TuiDataset> = self.get_tui_data(self.states.cursor);
×
366
            // Build widget
367
            let widget: TuiChart = TuiChart::new(data)
×
368
                .style(normal_style)
×
369
                .block(div)
×
370
                .x_axis(x_axis)
×
371
                .y_axis(y_axis);
×
372
            // Render
373
            render.render_widget(widget, area);
×
374
        }
×
375
    }
×
376

377
    fn query(&self, attr: Attribute) -> Option<AttrValue> {
×
NEW
378
        if attr == Attribute::Dataset {
×
NEW
379
            return Some(self.data_to_attr());
×
NEW
380
        }
×
381

382
        self.props.get(attr)
×
383
    }
×
384

385
    fn attr(&mut self, attr: Attribute, value: AttrValue) {
10✔
386
        if attr == Attribute::Dataset {
10✔
387
            self.data_from_attr(value);
5✔
388
            return;
5✔
389
        }
5✔
390
        self.props.set(attr, value);
5✔
391
    }
10✔
392

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

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

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

422
    use super::*;
423

424
    use pretty_assertions::assert_eq;
425
    use tuirealm::ratatui::{symbols::Marker, widgets::GraphType};
426

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

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

547
        let mut comp = Chart::default().data([ChartDataset::default()
1✔
548
            .name("Maximum")
1✔
549
            .graph_type(GraphType::Line)
1✔
550
            .marker(Marker::Dot)
1✔
551
            .style(Style::default().fg(Color::LightRed))
1✔
552
            .data(vec![(0.0, 7.0)])]);
1✔
553
        assert!(!comp.get_tui_data(0).is_empty());
1✔
554

555
        // Update and test empty data
556
        component.states.cursor_at_end(12);
1✔
557
        component.attr(
1✔
558
            Attribute::Dataset,
1✔
559
            AttrValue::Payload(PropPayload::Any(Box::new(Vec::<ChartDataset>::new()))),
1✔
560
        );
561
        assert_eq!(component.max_dataset_len(), 0);
1✔
562
        // Cursor is reset
563
        assert_eq!(component.states.cursor, 0);
1✔
564
    }
1✔
565

566
    #[test]
567
    fn allowed_dataset_attrs() {
1✔
568
        let mut component = Chart::default();
1✔
569
        assert!(component.states.data.is_empty());
1✔
570

571
        // allow overwriting multiple datasets at once
572
        component.attr(
1✔
573
            Attribute::Dataset,
1✔
574
            AttrValue::Payload(PropPayload::Any(Box::new(vec![ChartDataset::default()]))),
1✔
575
        );
576
        assert_eq!(component.states.data.len(), 1);
1✔
577

578
        component.attr(
1✔
579
            Attribute::Dataset,
1✔
580
            AttrValue::Payload(PropPayload::Any(Box::new(vec![ChartDataset::default()]))),
1✔
581
        );
582
        assert_eq!(component.states.data.len(), 1);
1✔
583

584
        // allow overwriting using with just one dataset
585
        component.attr(
1✔
586
            Attribute::Dataset,
1✔
587
            AttrValue::Payload(PropPayload::Any(Box::new(ChartDataset::default()))),
1✔
588
        );
589
        assert_eq!(component.states.data.len(), 1);
1✔
590

591
        component.attr(
1✔
592
            Attribute::Dataset,
1✔
593
            AttrValue::Payload(PropPayload::Any(Box::new(ChartDataset::default()))),
1✔
594
        );
595
        assert_eq!(component.states.data.len(), 1);
1✔
596
    }
1✔
597
}
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