Cargo.lock 🔗
@@ -7390,6 +7390,7 @@ name = "storybook"
version = "0.1.0"
dependencies = [
"anyhow",
+ "clap 3.2.25",
"gpui2",
"log",
"rust-embed",
Marshall Bowers created
This PR updates the `storybook` with support for adding stories for
individual components.
### Motivation
Right now we just have one story in the storybook that renders an entire
`WorkspaceElement`.
While iterating on the various UI components, it will be helpful to be
able to create stories of those components just by themselves.
This is especially true for components that have a number of different
states, as we can render the components in all of the various states in
a single layout.
### Explanation
We achieve this by adding a simple CLI to the storybook.
The `storybook` binary now accepts an optional `[STORY]` parameter that
can be used to indicate which story should be loaded. If this parameter
is not provided, it will load the workspace story as it currently does.
Passing a story name will load the corresponding story, if it exists.
For example:
```
cargo run -- elements/avatar
```
<img width="723" alt="Screenshot 2023-09-21 at 10 29 52 PM"
src="https://github.com/zed-industries/zed/assets/1486634/5df489ed-8607-4024-9c19-c5f4541f97c9">
```
cargo run -- components/facepile
```
<img width="785" alt="Screenshot 2023-09-21 at 10 30 07 PM"
src="https://github.com/zed-industries/zed/assets/1486634/e04a4577-7403-405d-b23c-e765b7a06229">
Release Notes:
- N/A
Cargo.lock | 1
crates/storybook/Cargo.toml | 3
crates/storybook/src/stories.rs | 2
crates/storybook/src/stories/components.rs | 1
crates/storybook/src/stories/components/facepile.rs | 69 +++++++++++++
crates/storybook/src/stories/elements.rs | 1
crates/storybook/src/stories/elements/avatar.rs | 41 ++++++++
crates/storybook/src/storybook.rs | 75 ++++++++++++--
crates/storybook/src/workspace.rs | 6 -
9 files changed, 180 insertions(+), 19 deletions(-)
@@ -7390,6 +7390,7 @@ name = "storybook"
version = "0.1.0"
dependencies = [
"anyhow",
+ "clap 3.2.25",
"gpui2",
"log",
"rust-embed",
@@ -9,8 +9,9 @@ name = "storybook"
path = "src/storybook.rs"
[dependencies]
-gpui2 = { path = "../gpui2" }
anyhow.workspace = true
+clap = { version = "3.1", features = ["derive"] }
+gpui2 = { path = "../gpui2" }
log.workspace = true
rust-embed.workspace = true
serde.workspace = true
@@ -0,0 +1,2 @@
+pub mod components;
+pub mod elements;
@@ -0,0 +1 @@
+pub mod facepile;
@@ -0,0 +1,69 @@
+use gpui2::elements::div;
+use gpui2::style::StyleHelpers;
+use gpui2::{rgb, Element, Hsla, IntoElement, ParentElement, ViewContext};
+use ui::{avatar, theme};
+use ui::{facepile, prelude::*};
+
+#[derive(Element, Default)]
+pub struct FacepileStory {}
+
+impl FacepileStory {
+ fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
+ let theme = theme(cx);
+
+ div()
+ .size_full()
+ .flex()
+ .flex_col()
+ .pt_2()
+ .px_4()
+ .font("Zed Mono Extended")
+ .fill(rgb::<Hsla>(0x282c34))
+ .child(
+ div()
+ .text_2xl()
+ .text_color(rgb::<Hsla>(0xffffff))
+ .child(std::any::type_name::<ui::Facepile>()),
+ )
+ .child(
+ div()
+ .flex()
+ .gap_3()
+ .child(facepile(vec![avatar(
+ "https://avatars.githubusercontent.com/u/1714999?v=4",
+ )]))
+ .child(facepile(vec![
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4"),
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4"),
+ ]))
+ .child(facepile(vec![
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4"),
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4"),
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4"),
+ ])),
+ )
+ .child(
+ div()
+ .flex()
+ .gap_3()
+ .child(facepile(vec![avatar(
+ "https://avatars.githubusercontent.com/u/1714999?v=4",
+ )
+ .shape(Shape::RoundedRectangle)]))
+ .child(facepile(vec![
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .shape(Shape::RoundedRectangle),
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .shape(Shape::RoundedRectangle),
+ ]))
+ .child(facepile(vec![
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .shape(Shape::RoundedRectangle),
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .shape(Shape::RoundedRectangle),
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .shape(Shape::RoundedRectangle),
+ ])),
+ )
+ }
+}
@@ -0,0 +1 @@
+pub mod avatar;
@@ -0,0 +1,41 @@
+use gpui2::elements::div;
+use gpui2::style::StyleHelpers;
+use gpui2::{rgb, Element, Hsla, IntoElement, ParentElement, ViewContext};
+use ui::prelude::*;
+use ui::{avatar, theme};
+
+#[derive(Element, Default)]
+pub struct AvatarStory {}
+
+impl AvatarStory {
+ fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
+ let theme = theme(cx);
+
+ div()
+ .size_full()
+ .flex()
+ .flex_col()
+ .pt_2()
+ .px_4()
+ .font("Zed Mono Extended")
+ .fill(rgb::<Hsla>(0x282c34))
+ .child(
+ div()
+ .text_2xl()
+ .text_color(rgb::<Hsla>(0xffffff))
+ .child(std::any::type_name::<ui::Avatar>()),
+ )
+ .child(
+ div()
+ .flex()
+ .gap_3()
+ .child(avatar(
+ "https://avatars.githubusercontent.com/u/1714999?v=4",
+ ))
+ .child(
+ avatar("https://avatars.githubusercontent.com/u/1714999?v=4")
+ .shape(Shape::RoundedRectangle),
+ ),
+ )
+ }
+}
@@ -1,25 +1,66 @@
#![allow(dead_code, unused_variables)]
+mod collab_panel;
+mod stories;
+mod workspace;
+
+use std::str::FromStr;
+
use ::theme as legacy_theme;
-use gpui2::{serde_json, vec2f, view, Element, RectF, ViewContext, WindowBounds};
+use clap::Parser;
+use gpui2::{serde_json, vec2f, view, Element, IntoElement, RectF, ViewContext, WindowBounds};
use legacy_theme::ThemeSettings;
use log::LevelFilter;
use settings::{default_settings, SettingsStore};
use simplelog::SimpleLogger;
+use stories::components::facepile::FacepileStory;
+use stories::elements::avatar::AvatarStory;
use ui::{ElementExt, Theme};
-mod collab_panel;
-mod workspace;
-
gpui2::actions! {
storybook,
[ToggleInspector]
}
+#[derive(Debug, Clone, Copy)]
+enum Story {
+ Element(ElementStory),
+ Component(ComponentStory),
+}
+
+impl FromStr for Story {
+ type Err = anyhow::Error;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ match s.to_ascii_lowercase().as_str() {
+ "elements/avatar" => Ok(Self::Element(ElementStory::Avatar)),
+ "components/facepile" => Ok(Self::Component(ComponentStory::Facepile)),
+ _ => Err(anyhow!("story not found for '{s}'")),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum ElementStory {
+ Avatar,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum ComponentStory {
+ Facepile,
+}
+
+#[derive(Parser)]
+struct Args {
+ story: Option<Story>,
+}
+
fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
- gpui2::App::new(Assets).unwrap().run(|cx| {
+ let args = Args::parse();
+
+ gpui2::App::new(Assets).unwrap().run(move |cx| {
let mut store = SettingsStore::default();
store
.set_default_settings(default_settings().as_ref(), cx)
@@ -34,19 +75,27 @@ fn main() {
center: true,
..Default::default()
},
- |cx| {
- view(|cx| {
- // cx.enable_inspector();
- storybook(&mut ViewContext::new(cx))
- })
+ |cx| match args.story {
+ Some(Story::Element(ElementStory::Avatar)) => {
+ view(|cx| render_story(&mut ViewContext::new(cx), AvatarStory::default()))
+ }
+ Some(Story::Component(ComponentStory::Facepile)) => {
+ view(|cx| render_story(&mut ViewContext::new(cx), FacepileStory::default()))
+ }
+ None => {
+ view(|cx| render_story(&mut ViewContext::new(cx), WorkspaceElement::default()))
+ }
},
);
cx.platform().activate(true);
});
}
-fn storybook<V: 'static>(cx: &mut ViewContext<V>) -> impl Element<V> {
- workspace().themed(current_theme(cx))
+fn render_story<V: 'static, S: IntoElement<V>>(
+ cx: &mut ViewContext<V>,
+ story: S,
+) -> impl Element<V> {
+ story.into_element().themed(current_theme(cx))
}
// Nathan: During the transition to gpui2, we will include the base theme on the legacy Theme struct.
@@ -69,7 +118,7 @@ fn current_theme<V: 'static>(cx: &mut ViewContext<V>) -> Theme {
use anyhow::{anyhow, Result};
use gpui2::AssetSource;
use rust_embed::RustEmbed;
-use workspace::workspace;
+use workspace::WorkspaceElement;
#[derive(RustEmbed)]
#[folder = "../../assets"]
@@ -6,16 +6,12 @@ use gpui2::{
use ui::{chat_panel, project_panel, status_bar, tab_bar, theme, title_bar};
#[derive(Element, Default)]
-struct WorkspaceElement {
+pub struct WorkspaceElement {
left_scroll_state: ScrollState,
right_scroll_state: ScrollState,
tab_bar_scroll_state: ScrollState,
}
-pub fn workspace<V: 'static>() -> impl Element<V> {
- WorkspaceElement::default()
-}
-
impl WorkspaceElement {
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx);