yew_styles/components/dropdown/
dropdown_container.rs

1use crate::styles::{get_palette, get_size, get_style, Palette, Size, Style};
2use stylist::{css, StyleSource};
3use wasm_bindgen_test::*;
4use yew::prelude::*;
5use yew::{utils, App};
6
7/// # Dropdown Container component
8///
9/// ## Features required
10///
11/// dropdown
12///
13/// ## Example
14///
15/// ```rust
16/// use yew::prelude::*;
17/// use yew_styles::dropdown::{Dropdown, DropdownItem};
18/// use yew_styles::styles::{Palette, Size, Style};
19///
20/// pub struct DropDownPage {
21///     link: ComponentLink<Self>,
22///     menu: String,
23/// }
24///
25/// pub enum Msg {
26///     ChangeMenu(String),
27/// }
28///
29/// impl Component for DropDownPage {
30///     type Message = Msg;
31///     type Properties = ();
32///
33///     fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
34///         Self {
35///             link,
36///             menu: String::from(""),
37///         }
38///     }
39///
40///     fn update(&mut self, msg: Self::Message) -> ShouldRender {
41///         match msg {
42///             Msg::ChangeMenu(menu) => {
43///                 self.menu = menu;
44///             }
45///         }
46///         true
47///     }
48///
49///     fn change(&mut self, _props: Self::Properties) -> ShouldRender {
50///         false
51///     }
52///
53///     fn view(&self) -> Html {
54///         <>
55///             <Dropdown
56///                 main_content=html!{<span>{"Menu"}</span>}
57///                 dropdown_style=Style::Outline
58///                 dropdown_palette=Palette::Primary
59///                 >
60///                 <DropdownItem
61///                     onclick_signal=link.callback(|_| Msg::ChangeMenu(String::from("Menu 1")))>{"Menu 1"}</DropdownItem>
62///                 <DropdownItem
63///                     onclick_signal=link.callback(|_| Msg::ChangeMenu(String::from("Menu 2")))>{"Menu 2"}</DropdownItem>
64///                 <DropdownItem
65///                     onclick_signal=link.callback(|_| Msg::ChangeMenu(String::from("Menu 3")))>{"Menu 3"}</DropdownItem>
66///             </Dropdown>
67///             <span>{self.menu.clone()}</span>
68///         </>
69///     }
70/// }
71/// ```
72pub struct Dropdown {
73    props: Props,
74    active: bool,
75    link: ComponentLink<Self>,
76}
77
78#[derive(Clone, Properties, PartialEq)]
79pub struct Props {
80    /// clickeable content to show the dropdown. Required
81    pub main_content: Html,
82    /// Palette style color for dropdown
83    #[prop_or(Palette::Standard)]
84    pub dropdown_palette: Palette,
85    /// Style for dropdown
86    #[prop_or(Style::Regular)]
87    pub dropdown_style: Style,
88    /// Size for dropdown
89    #[prop_or(Size::Medium)]
90    pub dropdown_size: Size,
91    /// General property to add keys
92    #[prop_or_default]
93    pub key: String,
94    /// General property to add custom class styles
95    #[prop_or_default]
96    pub class_name: String,
97    /// General property to add custom id
98    #[prop_or_default]
99    pub id: String,
100    /// Set css styles directly in the component
101    #[prop_or(css!(""))]
102    pub styles: StyleSource<'static>,
103    pub children: Children,
104}
105
106pub enum Msg {
107    ShowDropdown,
108}
109
110impl Component for Dropdown {
111    type Message = Msg;
112    type Properties = Props;
113
114    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
115        Self {
116            props,
117            link,
118            active: false,
119        }
120    }
121
122    fn update(&mut self, msg: Self::Message) -> ShouldRender {
123        match msg {
124            Msg::ShowDropdown => {
125                self.active = !self.active;
126            }
127        }
128        true
129    }
130
131    fn change(&mut self, props: Self::Properties) -> ShouldRender {
132        if self.props != props {
133            return true;
134        }
135        false
136    }
137
138    fn view(&self) -> Html {
139        html! {
140            <div
141                class=classes!("dropdown", self.props.class_name.clone(), get_style(self.props.dropdown_style.clone()), get_palette(self.props.dropdown_palette.clone()), get_size(self.props.dropdown_size.clone()), self.props.styles.clone())
142                id=self.props.id.clone()
143                key=self.props.key.clone()
144                onclick=self.link.callback(|_| Msg::ShowDropdown)
145                >
146                <div class="main-content">{self.props.main_content.clone()}</div>
147                {get_items(self.active, self.props.children.clone())}
148            </div>
149        }
150    }
151}
152
153fn get_items(active: bool, children: Children) -> Html {
154    if active {
155        html! {
156            <ul>
157                {children.clone()}
158            </ul>
159        }
160    } else {
161        html! {}
162    }
163}
164
165wasm_bindgen_test_configure!(run_in_browser);
166
167#[wasm_bindgen_test]
168fn should_create_dropdown_container() {
169    let dropdown_container_props = Props {
170        main_content: html! {<div id="test">{"test"}</div>},
171        dropdown_palette: Palette::Clean,
172        dropdown_size: Size::Medium,
173        dropdown_style: Style::Outline,
174        key: String::from("dropdown-1"),
175        class_name: String::from("class-test"),
176        id: String::from("id-test"),
177        styles: css!("background-color: #918d94;"),
178        children: Children::new(vec![html! {
179            <div id="item">{"Item"}</div>
180        }]),
181    };
182
183    let dropdown_container: App<Dropdown> = App::new();
184
185    dropdown_container.mount_with_props(
186        utils::document().get_element_by_id("output").unwrap(),
187        dropdown_container_props,
188    );
189
190    let content_element = utils::document().get_element_by_id("test").unwrap();
191    assert_eq!(content_element.text_content().unwrap(), "test".to_string());
192}