yew_styles 0.11.0

Framework styles for yew
Documentation
use crate::styles::{get_palette, get_size, get_style, Palette, Size, Style};
use stylist::{css, StyleSource};
use wasm_bindgen_test::*;
use web_sys::window;
use yew::prelude::*;
use yew::{utils, App};

/// # Button component
///
/// ## Features required
///
/// button
///
/// ## Example
///
/// ```rust
/// use yew::prelude::*;
/// use yew::services::ConsoleService;
/// use yew_styles::{
///     button::{Button},
///     styles::{Palette, Style, Size},
/// };
///
/// pub struct App {
///   link: ComponentLink<Self>,
/// }
///
/// pub enum Msg {
///   Clicked(String),
/// }
/// #[derive(Clone, Properties)]
/// pub struct Props {}
///
/// impl Component for App {
///     type Message = Msg;
///     type Properties = Props;
///
///     fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
///         App {
///             link
///         }
///     }
///
///     fn update(&mut self, msg: Self::Message) -> ShouldRender {
///         match msg {
///             Msg::Clicked(greeting) => {
///                 let mut console = ConsoleService::log(&format!("{}", menu));
///             }
///         }
///         false
///     }
///
///     fn change(&mut self, _props: Self::Properties) -> ShouldRender {
///         false
///     }
///
///     fn view(&self) -> Html {
///        html! {
///          <Button
///             onclick_signal=link.callback(move |_| Msg::Clicked(String::from("Hello world")))
///             class_name="hello-world"
///             button_palette=Pallete::Standard
///             button_style=Style::Light
///             button_size=Size::Medium
///          >{"Greeting"}</Button>
///        }
///     }
/// }
/// ```
pub struct Button {
    link: ComponentLink<Self>,
    props: ButtonProps,
}

#[derive(PartialEq)]
struct ButtonProps {
    button_palette: String,
    button_size: String,
    button_style: String,
    class_name: String,
    id: String,
    key: String,
    code_ref: NodeRef,
    onclick_signal: Callback<MouseEvent>,
    styles: StyleSource<'static>,
    children: Children,
}

impl From<Props> for ButtonProps {
    fn from(props: Props) -> Self {
        ButtonProps {
            button_palette: get_palette(props.button_palette),
            button_size: get_size(props.button_size),
            button_style: get_style(props.button_style),
            class_name: props.class_name,
            id: props.id,
            key: props.key,
            code_ref: props.code_ref,
            onclick_signal: props.onclick_signal,
            styles: props.styles,
            children: props.children,
        }
    }
}

#[derive(Clone, Properties, PartialEq)]
pub struct Props {
    /// Type botton style. Default `Palette::Standard`
    #[prop_or(Palette::Standard)]
    pub button_palette: Palette,
    /// General property to add custom class styles
    #[prop_or_default]
    pub class_name: String,
    /// General property to add custom id
    #[prop_or_default]
    pub id: String,
    /// General property to get the ref of the component
    #[prop_or_default]
    pub code_ref: NodeRef,
    /// General property to add keys
    #[prop_or_default]
    pub key: String,
    /// Three diffent button standard sizes. Default `Size::Medium`
    #[prop_or(Size::Medium)]
    pub button_size: Size,
    /// Button styles. Default `Style::Regular`
    #[prop_or(Style::Regular)]
    pub button_style: Style,
    /// Click event for button. Required
    pub onclick_signal: Callback<MouseEvent>,
    /// Set css styles directly in the component
    #[prop_or(css!(""))]
    pub styles: StyleSource<'static>,
    pub children: Children,
}

pub enum Msg {
    Clicked(MouseEvent),
}

impl Component for Button {
    type Message = Msg;
    type Properties = Props;

    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            link,
            props: ButtonProps::from(props),
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Clicked(mouse_event) => {
                self.props.onclick_signal.emit(mouse_event);
            }
        };

        true
    }

    fn change(&mut self, props: Self::Properties) -> ShouldRender {
        let prop_mapped = ButtonProps::from(props);
        if self.props != prop_mapped {
            self.props = prop_mapped;
            return true;
        }
        true
    }

    fn view(&self) -> Html {
        html! {
            <button
                onclick=self.link.callback(Msg::Clicked)
                class=classes!("button",
                    self.props.button_palette.clone(),
                    self.props.button_size.clone(),
                    self.props.button_style.clone(),
                    self.props.class_name.clone(),
                    self.props.styles.clone(),
                )
                key=self.props.key.clone()
                ref=self.props.code_ref.clone()
                id=self.props.id.clone()
            > { self.props.children.clone() }
            </button>
        }
    }
}

wasm_bindgen_test_configure!(run_in_browser);

#[wasm_bindgen_test]
fn should_trigger_action_when_button_clicked() {
    let body = window().unwrap().document().unwrap().body().unwrap();

    let element = window()
        .unwrap()
        .document()
        .unwrap()
        .create_element("div")
        .unwrap();
    element.set_text_content(Some("home"));
    element.set_id("menu");

    body.append_child(&element).unwrap();

    let onchange_name = Callback::from(|_| {
        let content = window()
            .unwrap()
            .document()
            .unwrap()
            .get_element_by_id("menu")
            .unwrap();

        content.set_text_content(Some("about"));
    });

    let props = Props {
        class_name: String::from("test-button"),
        id: String::from("button-id-test"),
        key: "".to_string(),
        code_ref: NodeRef::default(),
        button_size: Size::Medium,
        button_style: Style::Regular,
        onclick_signal: onchange_name,
        button_palette: Palette::Standard,
        styles: css!("background-color: #918d94;"),
        children: Children::new(vec![html! {<div id="submenu">{"another menu"}</div>}]),
    };

    let mouse_event = MouseEvent::new("click").unwrap();

    props.onclick_signal.emit(mouse_event);

    let updated_content = window()
        .unwrap()
        .document()
        .unwrap()
        .get_element_by_id("menu")
        .unwrap()
        .text_content()
        .unwrap();

    assert_eq!(updated_content, String::from("about"));
}

#[wasm_bindgen_test]
fn should_create_button_component() {
    let props = Props {
        class_name: String::from("test-button"),
        id: String::from("button-id-test"),
        key: "".to_string(),
        code_ref: NodeRef::default(),
        button_size: Size::Medium,
        button_style: Style::Regular,
        onclick_signal: Callback::noop(),
        button_palette: Palette::Standard,
        styles: css!("background-color: #918d94;"),
        children: Children::new(vec![html! {<div id="result">{"result"}</div>}]),
    };

    let button: App<Button> = App::new();
    button.mount_with_props(
        utils::document().get_element_by_id("output").unwrap(),
        props,
    );

    let button_element = utils::document()
        .get_elements_by_tag_name("button")
        .get_with_index(0)
        .unwrap();

    let child = button_element.first_element_child().unwrap();

    assert_eq!(button_element.tag_name(), "BUTTON");
    assert_eq!(child.id(), "result");
}