Colocate element stories with their elements

Marshall Bowers created

Change summary

crates/storybook2/src/stories.rs                  |   2 
crates/storybook2/src/stories/elements.rs         |   7 
crates/storybook2/src/stories/elements/avatar.rs  |  33 --
crates/storybook2/src/stories/elements/button.rs  | 200 ----------------
crates/storybook2/src/stories/elements/details.rs |  31 --
crates/storybook2/src/stories/elements/icon.rs    |  29 --
crates/storybook2/src/stories/elements/input.rs   |  26 --
crates/storybook2/src/stories/elements/label.rs   |  28 --
crates/storybook2/src/stories/z_index.rs          |   0 
crates/storybook2/src/story_selector.rs           |  16 
crates/ui2/src/elements/avatar.rs                 |  37 +++
crates/ui2/src/elements/button.rs                 | 203 +++++++++++++++++
crates/ui2/src/elements/details.rs                |  35 ++
crates/ui2/src/elements/icon.rs                   |  34 ++
crates/ui2/src/elements/input.rs                  |  30 ++
crates/ui2/src/elements/label.rs                  |  32 ++
16 files changed, 379 insertions(+), 364 deletions(-)

Detailed changes

crates/storybook2/src/stories/elements/avatar.rs 🔗

@@ -1,33 +0,0 @@
-use std::marker::PhantomData;
-
-use ui::prelude::*;
-use ui::Avatar;
-
-use crate::story::Story;
-
-#[derive(Element)]
-pub struct AvatarStory<S: 'static + Send + Sync> {
-    state_type: PhantomData<S>,
-}
-
-impl<S: 'static + Send + Sync> AvatarStory<S> {
-    pub fn new() -> Self {
-        Self {
-            state_type: PhantomData,
-        }
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
-        Story::container(cx)
-            .child(Story::title_for::<_, Avatar<S>>(cx))
-            .child(Story::label(cx, "Default"))
-            .child(Avatar::new(
-                "https://avatars.githubusercontent.com/u/1714999?v=4",
-            ))
-            .child(Story::label(cx, "Rounded rectangle"))
-            .child(
-                Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
-                    .shape(Shape::RoundedRectangle),
-            )
-    }
-}

crates/storybook2/src/stories/elements/button.rs 🔗

@@ -1,200 +0,0 @@
-use std::marker::PhantomData;
-
-use gpui3::rems;
-use strum::IntoEnumIterator;
-use ui::prelude::*;
-use ui::{h_stack, v_stack, Button, Icon, IconPosition, Label};
-
-use crate::story::Story;
-
-#[derive(Element)]
-pub struct ButtonStory<S: 'static + Send + Sync + Clone> {
-    state_type: PhantomData<S>,
-}
-
-impl<S: 'static + Send + Sync + Clone> ButtonStory<S> {
-    pub fn new() -> Self {
-        Self {
-            state_type: PhantomData,
-        }
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
-        let states = InteractionState::iter();
-
-        Story::container(cx)
-            .child(Story::title_for::<_, Button<S>>(cx))
-            .child(
-                div()
-                    .flex()
-                    .gap_8()
-                    .child(
-                        div()
-                            .child(Story::label(cx, "Ghost (Default)"))
-                            .child(h_stack().gap_2().children(states.clone().map(|state| {
-                                v_stack()
-                                    .gap_1()
-                                    .child(
-                                        Label::new(state.to_string())
-                                            .color(ui::LabelColor::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    )
-                                    .child(
-                                        Button::new("Label")
-                                            .variant(ButtonVariant::Ghost)
-                                            .state(state),
-                                    )
-                            })))
-                            .child(Story::label(cx, "Ghost – Left Icon"))
-                            .child(h_stack().gap_2().children(states.clone().map(|state| {
-                                v_stack()
-                                    .gap_1()
-                                    .child(
-                                        Label::new(state.to_string())
-                                            .color(ui::LabelColor::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    )
-                                    .child(
-                                        Button::new("Label")
-                                            .variant(ButtonVariant::Ghost)
-                                            .icon(Icon::Plus)
-                                            .icon_position(IconPosition::Left)
-                                            .state(state),
-                                    )
-                            })))
-                            .child(Story::label(cx, "Ghost – Right Icon"))
-                            .child(h_stack().gap_2().children(states.clone().map(|state| {
-                                v_stack()
-                                    .gap_1()
-                                    .child(
-                                        Label::new(state.to_string())
-                                            .color(ui::LabelColor::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    )
-                                    .child(
-                                        Button::new("Label")
-                                            .variant(ButtonVariant::Ghost)
-                                            .icon(Icon::Plus)
-                                            .icon_position(IconPosition::Right)
-                                            .state(state),
-                                    )
-                            }))),
-                    )
-                    .child(
-                        div()
-                            .child(Story::label(cx, "Filled"))
-                            .child(h_stack().gap_2().children(states.clone().map(|state| {
-                                v_stack()
-                                    .gap_1()
-                                    .child(
-                                        Label::new(state.to_string())
-                                            .color(ui::LabelColor::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    )
-                                    .child(
-                                        Button::new("Label")
-                                            .variant(ButtonVariant::Filled)
-                                            .state(state),
-                                    )
-                            })))
-                            .child(Story::label(cx, "Filled – Left Button"))
-                            .child(h_stack().gap_2().children(states.clone().map(|state| {
-                                v_stack()
-                                    .gap_1()
-                                    .child(
-                                        Label::new(state.to_string())
-                                            .color(ui::LabelColor::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    )
-                                    .child(
-                                        Button::new("Label")
-                                            .variant(ButtonVariant::Filled)
-                                            .icon(Icon::Plus)
-                                            .icon_position(IconPosition::Left)
-                                            .state(state),
-                                    )
-                            })))
-                            .child(Story::label(cx, "Filled – Right Button"))
-                            .child(h_stack().gap_2().children(states.clone().map(|state| {
-                                v_stack()
-                                    .gap_1()
-                                    .child(
-                                        Label::new(state.to_string())
-                                            .color(ui::LabelColor::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    )
-                                    .child(
-                                        Button::new("Label")
-                                            .variant(ButtonVariant::Filled)
-                                            .icon(Icon::Plus)
-                                            .icon_position(IconPosition::Right)
-                                            .state(state),
-                                    )
-                            }))),
-                    )
-                    .child(
-                        div()
-                            .child(Story::label(cx, "Fixed With"))
-                            .child(h_stack().gap_2().children(states.clone().map(|state| {
-                                v_stack()
-                                    .gap_1()
-                                    .child(
-                                        Label::new(state.to_string())
-                                            .color(ui::LabelColor::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    )
-                                    .child(
-                                        Button::new("Label")
-                                            .variant(ButtonVariant::Filled)
-                                            .state(state)
-                                            .width(Some(rems(6.).into())),
-                                    )
-                            })))
-                            .child(Story::label(cx, "Fixed With – Left Icon"))
-                            .child(h_stack().gap_2().children(states.clone().map(|state| {
-                                v_stack()
-                                    .gap_1()
-                                    .child(
-                                        Label::new(state.to_string())
-                                            .color(ui::LabelColor::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    )
-                                    .child(
-                                        Button::new("Label")
-                                            .variant(ButtonVariant::Filled)
-                                            .state(state)
-                                            .icon(Icon::Plus)
-                                            .icon_position(IconPosition::Left)
-                                            .width(Some(rems(6.).into())),
-                                    )
-                            })))
-                            .child(Story::label(cx, "Fixed With – Right Icon"))
-                            .child(h_stack().gap_2().children(states.clone().map(|state| {
-                                v_stack()
-                                    .gap_1()
-                                    .child(
-                                        Label::new(state.to_string())
-                                            .color(ui::LabelColor::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    )
-                                    .child(
-                                        Button::new("Label")
-                                            .variant(ButtonVariant::Filled)
-                                            .state(state)
-                                            .icon(Icon::Plus)
-                                            .icon_position(IconPosition::Right)
-                                            .width(Some(rems(6.).into())),
-                                    )
-                            }))),
-                    ),
-            )
-            .child(Story::label(cx, "Button with `on_click`"))
-            .child(
-                Button::new("Label")
-                    .variant(ButtonVariant::Ghost)
-                    // NOTE: There currently appears to be a bug in GPUI2 where only the last event handler will fire.
-                    // So adding additional buttons with `on_click`s after this one will cause this `on_click` to not fire.
-                    // .on_click(|_view, _cx| println!("Button clicked.")),
-            )
-    }
-}

crates/storybook2/src/stories/elements/details.rs 🔗

@@ -1,31 +0,0 @@
-use std::marker::PhantomData;
-
-use ui::prelude::*;
-use ui::Details;
-
-use crate::story::Story;
-
-#[derive(Element)]
-pub struct DetailsStory<S: 'static + Send + Sync + Clone> {
-    state_type: PhantomData<S>,
-}
-
-impl<S: 'static + Send + Sync + Clone> DetailsStory<S> {
-    pub fn new() -> Self {
-        Self {
-            state_type: PhantomData,
-        }
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
-        Story::container(cx)
-            .child(Story::title_for::<_, Details<S>>(cx))
-            .child(Story::label(cx, "Default"))
-            .child(Details::new("The quick brown fox jumps over the lazy dog"))
-            .child(Story::label(cx, "With meta"))
-            .child(
-                Details::new("The quick brown fox jumps over the lazy dog")
-                    .meta_text("Sphinx of black quartz, judge my vow."),
-            )
-    }
-}

crates/storybook2/src/stories/elements/icon.rs 🔗

@@ -1,29 +0,0 @@
-use std::marker::PhantomData;
-
-use strum::IntoEnumIterator;
-use ui::prelude::*;
-use ui::{Icon, IconElement};
-
-use crate::story::Story;
-
-#[derive(Element, Default)]
-pub struct IconStory<S: 'static + Send + Sync> {
-    state_type: PhantomData<S>,
-}
-
-impl<S: 'static + Send + Sync> IconStory<S> {
-    pub fn new() -> Self {
-        Self {
-            state_type: PhantomData,
-        }
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
-        let icons = Icon::iter();
-
-        Story::container(cx)
-            .child(Story::title_for::<_, IconElement<S>>(cx))
-            .child(Story::label(cx, "All Icons"))
-            .child(div().flex().gap_3().children(icons.map(IconElement::new)))
-    }
-}

crates/storybook2/src/stories/elements/input.rs 🔗

@@ -1,26 +0,0 @@
-use std::marker::PhantomData;
-
-use ui::prelude::*;
-use ui::Input;
-
-use crate::story::Story;
-
-#[derive(Element)]
-pub struct InputStory<S: 'static + Send + Sync + Clone> {
-    state_type: PhantomData<S>,
-}
-
-impl<S: 'static + Send + Sync + Clone> InputStory<S> {
-    pub fn new() -> Self {
-        Self {
-            state_type: PhantomData,
-        }
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
-        Story::container(cx)
-            .child(Story::title_for::<_, Input<S>>(cx))
-            .child(Story::label(cx, "Default"))
-            .child(div().flex().child(Input::new("Search")))
-    }
-}

crates/storybook2/src/stories/elements/label.rs 🔗

@@ -1,28 +0,0 @@
-use std::marker::PhantomData;
-
-use ui::prelude::*;
-use ui::Label;
-
-use crate::story::Story;
-
-#[derive(Element)]
-pub struct LabelStory<S: 'static + Send + Sync + Clone> {
-    state_type: PhantomData<S>,
-}
-
-impl<S: 'static + Send + Sync + Clone> LabelStory<S> {
-    pub fn new() -> Self {
-        Self {
-            state_type: PhantomData,
-        }
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
-        Story::container(cx)
-            .child(Story::title_for::<_, Label<S>>(cx))
-            .child(Story::label(cx, "Default"))
-            .child(Label::new("Hello, world!"))
-            .child(Story::label(cx, "Highlighted"))
-            .child(Label::new("Hello, world!").with_highlights(vec![0, 1, 2, 7, 8, 12]))
-    }
-}

crates/storybook2/src/story_selector.rs 🔗

@@ -23,16 +23,14 @@ pub enum ElementStory {
 
 impl ElementStory {
     pub fn story<S: 'static + Send + Sync + Clone>(&self) -> AnyElement<S> {
-        use crate::stories::elements;
-
         match self {
-            Self::Avatar => elements::avatar::AvatarStory::new().into_any(),
-            Self::Button => elements::button::ButtonStory::new().into_any(),
-            Self::Details => elements::details::DetailsStory::new().into_any(),
-            Self::Icon => elements::icon::IconStory::new().into_any(),
-            Self::Input => elements::input::InputStory::new().into_any(),
-            Self::Label => elements::label::LabelStory::new().into_any(),
-            Self::ZIndex => elements::z_index::ZIndexStory::new().into_any(),
+            Self::Avatar => ui::AvatarStory::new().into_any(),
+            Self::Button => ui::ButtonStory::new().into_any(),
+            Self::Details => ui::DetailsStory::new().into_any(),
+            Self::Icon => ui::IconStory::new().into_any(),
+            Self::Input => ui::InputStory::new().into_any(),
+            Self::Label => ui::LabelStory::new().into_any(),
+            Self::ZIndex => crate::stories::z_index::ZIndexStory::new().into_any(),
         }
     }
 }

crates/ui2/src/elements/avatar.rs 🔗

@@ -42,3 +42,40 @@ impl<S: 'static + Send + Sync> Avatar<S> {
             .fill(theme.middle.warning.default.foreground)
     }
 }
+
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+    use crate::Story;
+
+    use super::*;
+
+    #[derive(Element)]
+    pub struct AvatarStory<S: 'static + Send + Sync> {
+        state_type: PhantomData<S>,
+    }
+
+    impl<S: 'static + Send + Sync> AvatarStory<S> {
+        pub fn new() -> Self {
+            Self {
+                state_type: PhantomData,
+            }
+        }
+
+        fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
+            Story::container(cx)
+                .child(Story::title_for::<_, Avatar<S>>(cx))
+                .child(Story::label(cx, "Default"))
+                .child(Avatar::new(
+                    "https://avatars.githubusercontent.com/u/1714999?v=4",
+                ))
+                .child(Story::label(cx, "Rounded rectangle"))
+                .child(
+                    Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
+                        .shape(Shape::RoundedRectangle),
+                )
+        }
+    }
+}

crates/ui2/src/elements/button.rs 🔗

@@ -202,3 +202,206 @@ impl<S: 'static + Send + Sync + Clone> Button<S> {
         el
     }
 }
+
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+    use gpui3::rems;
+    use strum::IntoEnumIterator;
+
+    use crate::{h_stack, v_stack, LabelColor, LabelSize, Story};
+
+    use super::*;
+
+    #[derive(Element)]
+    pub struct ButtonStory<S: 'static + Send + Sync + Clone> {
+        state_type: PhantomData<S>,
+    }
+
+    impl<S: 'static + Send + Sync + Clone> ButtonStory<S> {
+        pub fn new() -> Self {
+            Self {
+                state_type: PhantomData,
+            }
+        }
+
+        fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
+            let states = InteractionState::iter();
+
+            Story::container(cx)
+                .child(Story::title_for::<_, Button<S>>(cx))
+                .child(
+                    div()
+                        .flex()
+                        .gap_8()
+                        .child(
+                            div()
+                                .child(Story::label(cx, "Ghost (Default)"))
+                                .child(h_stack().gap_2().children(states.clone().map(|state| {
+                                    v_stack()
+                                        .gap_1()
+                                        .child(
+                                            Label::new(state.to_string())
+                                                .color(LabelColor::Muted)
+                                                .size(LabelSize::Small),
+                                        )
+                                        .child(
+                                            Button::new("Label")
+                                                .variant(ButtonVariant::Ghost)
+                                                .state(state),
+                                        )
+                                })))
+                                .child(Story::label(cx, "Ghost – Left Icon"))
+                                .child(h_stack().gap_2().children(states.clone().map(|state| {
+                                    v_stack()
+                                        .gap_1()
+                                        .child(
+                                            Label::new(state.to_string())
+                                                .color(LabelColor::Muted)
+                                                .size(LabelSize::Small),
+                                        )
+                                        .child(
+                                            Button::new("Label")
+                                                .variant(ButtonVariant::Ghost)
+                                                .icon(Icon::Plus)
+                                                .icon_position(IconPosition::Left)
+                                                .state(state),
+                                        )
+                                })))
+                                .child(Story::label(cx, "Ghost – Right Icon"))
+                                .child(h_stack().gap_2().children(states.clone().map(|state| {
+                                    v_stack()
+                                        .gap_1()
+                                        .child(
+                                            Label::new(state.to_string())
+                                                .color(LabelColor::Muted)
+                                                .size(LabelSize::Small),
+                                        )
+                                        .child(
+                                            Button::new("Label")
+                                                .variant(ButtonVariant::Ghost)
+                                                .icon(Icon::Plus)
+                                                .icon_position(IconPosition::Right)
+                                                .state(state),
+                                        )
+                                }))),
+                        )
+                        .child(
+                            div()
+                                .child(Story::label(cx, "Filled"))
+                                .child(h_stack().gap_2().children(states.clone().map(|state| {
+                                    v_stack()
+                                        .gap_1()
+                                        .child(
+                                            Label::new(state.to_string())
+                                                .color(LabelColor::Muted)
+                                                .size(LabelSize::Small),
+                                        )
+                                        .child(
+                                            Button::new("Label")
+                                                .variant(ButtonVariant::Filled)
+                                                .state(state),
+                                        )
+                                })))
+                                .child(Story::label(cx, "Filled – Left Button"))
+                                .child(h_stack().gap_2().children(states.clone().map(|state| {
+                                    v_stack()
+                                        .gap_1()
+                                        .child(
+                                            Label::new(state.to_string())
+                                                .color(LabelColor::Muted)
+                                                .size(LabelSize::Small),
+                                        )
+                                        .child(
+                                            Button::new("Label")
+                                                .variant(ButtonVariant::Filled)
+                                                .icon(Icon::Plus)
+                                                .icon_position(IconPosition::Left)
+                                                .state(state),
+                                        )
+                                })))
+                                .child(Story::label(cx, "Filled – Right Button"))
+                                .child(h_stack().gap_2().children(states.clone().map(|state| {
+                                    v_stack()
+                                        .gap_1()
+                                        .child(
+                                            Label::new(state.to_string())
+                                                .color(LabelColor::Muted)
+                                                .size(LabelSize::Small),
+                                        )
+                                        .child(
+                                            Button::new("Label")
+                                                .variant(ButtonVariant::Filled)
+                                                .icon(Icon::Plus)
+                                                .icon_position(IconPosition::Right)
+                                                .state(state),
+                                        )
+                                }))),
+                        )
+                        .child(
+                            div()
+                                .child(Story::label(cx, "Fixed With"))
+                                .child(h_stack().gap_2().children(states.clone().map(|state| {
+                                    v_stack()
+                                        .gap_1()
+                                        .child(
+                                            Label::new(state.to_string())
+                                                .color(LabelColor::Muted)
+                                                .size(LabelSize::Small),
+                                        )
+                                        .child(
+                                            Button::new("Label")
+                                                .variant(ButtonVariant::Filled)
+                                                .state(state)
+                                                .width(Some(rems(6.).into())),
+                                        )
+                                })))
+                                .child(Story::label(cx, "Fixed With – Left Icon"))
+                                .child(h_stack().gap_2().children(states.clone().map(|state| {
+                                    v_stack()
+                                        .gap_1()
+                                        .child(
+                                            Label::new(state.to_string())
+                                                .color(LabelColor::Muted)
+                                                .size(LabelSize::Small),
+                                        )
+                                        .child(
+                                            Button::new("Label")
+                                                .variant(ButtonVariant::Filled)
+                                                .state(state)
+                                                .icon(Icon::Plus)
+                                                .icon_position(IconPosition::Left)
+                                                .width(Some(rems(6.).into())),
+                                        )
+                                })))
+                                .child(Story::label(cx, "Fixed With – Right Icon"))
+                                .child(h_stack().gap_2().children(states.clone().map(|state| {
+                                    v_stack()
+                                        .gap_1()
+                                        .child(
+                                            Label::new(state.to_string())
+                                                .color(LabelColor::Muted)
+                                                .size(LabelSize::Small),
+                                        )
+                                        .child(
+                                            Button::new("Label")
+                                                .variant(ButtonVariant::Filled)
+                                                .state(state)
+                                                .icon(Icon::Plus)
+                                                .icon_position(IconPosition::Right)
+                                                .width(Some(rems(6.).into())),
+                                        )
+                                }))),
+                        ),
+                )
+                .child(Story::label(cx, "Button with `on_click`"))
+                .child(
+                    Button::new("Label").variant(ButtonVariant::Ghost), // NOTE: There currently appears to be a bug in GPUI2 where only the last event handler will fire.
+                                                                        // So adding additional buttons with `on_click`s after this one will cause this `on_click` to not fire.
+                                                                        // .on_click(|_view, _cx| println!("Button clicked.")),
+                )
+        }
+    }
+}

crates/ui2/src/elements/details.rs 🔗

@@ -38,3 +38,38 @@ impl<S: 'static + Send + Sync + Clone> Details<S> {
             .children(self.meta.map(|m| m))
     }
 }
+
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+    use crate::Story;
+
+    use super::*;
+
+    #[derive(Element)]
+    pub struct DetailsStory<S: 'static + Send + Sync + Clone> {
+        state_type: PhantomData<S>,
+    }
+
+    impl<S: 'static + Send + Sync + Clone> DetailsStory<S> {
+        pub fn new() -> Self {
+            Self {
+                state_type: PhantomData,
+            }
+        }
+
+        fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
+            Story::container(cx)
+                .child(Story::title_for::<_, Details<S>>(cx))
+                .child(Story::label(cx, "Default"))
+                .child(Details::new("The quick brown fox jumps over the lazy dog"))
+                .child(Story::label(cx, "With meta"))
+                .child(
+                    Details::new("The quick brown fox jumps over the lazy dog")
+                        .meta_text("Sphinx of black quartz, judge my vow."),
+                )
+        }
+    }
+}

crates/ui2/src/elements/icon.rs 🔗

@@ -182,3 +182,37 @@ impl<S: 'static + Send + Sync> IconElement<S> {
         sized_svg.flex_none().path(self.icon.path()).fill(fill)
     }
 }
+
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+    use strum::IntoEnumIterator;
+
+    use crate::Story;
+
+    use super::*;
+
+    #[derive(Element, Default)]
+    pub struct IconStory<S: 'static + Send + Sync> {
+        state_type: PhantomData<S>,
+    }
+
+    impl<S: 'static + Send + Sync> IconStory<S> {
+        pub fn new() -> Self {
+            Self {
+                state_type: PhantomData,
+            }
+        }
+
+        fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
+            let icons = Icon::iter();
+
+            Story::container(cx)
+                .child(Story::title_for::<_, IconElement<S>>(cx))
+                .child(Story::label(cx, "All Icons"))
+                .child(div().flex().gap_3().children(icons.map(IconElement::new)))
+        }
+    }
+}

crates/ui2/src/elements/input.rs 🔗

@@ -108,3 +108,33 @@ impl<S: 'static + Send + Sync> Input<S> {
             )
     }
 }
+
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+    use crate::Story;
+
+    use super::*;
+
+    #[derive(Element)]
+    pub struct InputStory<S: 'static + Send + Sync + Clone> {
+        state_type: PhantomData<S>,
+    }
+
+    impl<S: 'static + Send + Sync + Clone> InputStory<S> {
+        pub fn new() -> Self {
+            Self {
+                state_type: PhantomData,
+            }
+        }
+
+        fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
+            Story::container(cx)
+                .child(Story::title_for::<_, Input<S>>(cx))
+                .child(Story::label(cx, "Default"))
+                .child(div().flex().child(Input::new("Search")))
+        }
+    }
+}

crates/ui2/src/elements/label.rs 🔗

@@ -163,3 +163,35 @@ struct Run {
     pub text: String,
     pub color: Hsla,
 }
+
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+    use crate::Story;
+
+    use super::*;
+
+    #[derive(Element)]
+    pub struct LabelStory<S: 'static + Send + Sync + Clone> {
+        state_type: PhantomData<S>,
+    }
+
+    impl<S: 'static + Send + Sync + Clone> LabelStory<S> {
+        pub fn new() -> Self {
+            Self {
+                state_type: PhantomData,
+            }
+        }
+
+        fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
+            Story::container(cx)
+                .child(Story::title_for::<_, Label<S>>(cx))
+                .child(Story::label(cx, "Default"))
+                .child(Label::new("Hello, world!"))
+                .child(Story::label(cx, "Highlighted"))
+                .child(Label::new("Hello, world!").with_highlights(vec![0, 1, 2, 7, 8, 12]))
+        }
+    }
+}