mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-24 17:28:40 +00:00
Update storybook to support stories for individual components (#3010)
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
This commit is contained in:
parent
4628639ac6
commit
66358f2900
9 changed files with 180 additions and 19 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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
|
||||
|
|
2
crates/storybook/src/stories.rs
Normal file
2
crates/storybook/src/stories.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod components;
|
||||
pub mod elements;
|
1
crates/storybook/src/stories/components.rs
Normal file
1
crates/storybook/src/stories/components.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod facepile;
|
69
crates/storybook/src/stories/components/facepile.rs
Normal file
69
crates/storybook/src/stories/components/facepile.rs
Normal file
|
@ -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),
|
||||
])),
|
||||
)
|
||||
}
|
||||
}
|
1
crates/storybook/src/stories/elements.rs
Normal file
1
crates/storybook/src/stories/elements.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod avatar;
|
41
crates/storybook/src/stories/elements/avatar.rs
Normal file
41
crates/storybook/src/stories/elements/avatar.rs
Normal file
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue