mod tests;
use leptos::web_sys::HtmlElement;
use leptos::IntoView;
pub use tests::play;
pub use tests::test_id;
use utils::prelude::ThreadSafe;
use crate::RouteDef;
pub trait Step {
type Story: Story;
fn description(&self) -> &'static str;
fn run(&self, canvas: &HtmlElement, story: &mut Self::Story) -> Result<(), &'static str>;
}
pub trait Play {
type Story: Story;
fn description(&self) -> &'static str;
fn steps(&self) -> Vec<Box<dyn Step<Story = Self::Story>>>;
}
impl<T: Play + ?Sized> Play for Box<T> {
type Story = T::Story;
fn description(&self) -> &'static str {
self.as_ref().description()
}
fn steps(&self) -> Vec<Box<dyn Step<Story = Self::Story>>> {
self.as_ref().steps()
}
}
const STORY_DESC: &str = r############"
# New Story
# Cheat sheet
You've just created a new story. There are a few steps to get it working
1. Implement `fn view(&self) -> impl IntoView` so `leptos_forge` can showcase your component
2. Implement `fn controls(&self) -> impl IntoView` so you can control your component and easily test it's behavior in `leptos_forge`
3. Implement `fn description(&self) -> &'static str` where you describe what your component does
## Implementing `fn view(&self) -> impl IntoView`
In here you define how your component should show up in canvas area (grey one at the center) in `leptos_forge`. In most of a cases
your implementation will be something like this:
```rust
...
fn view(&self) -> impl IntoView {
view!{
<YourComponent prop:my_prop1={self.my_prop1} prop:my_prop2={self.my_prop2} ... />
}
}
...
```
## Implementing `fn controls(&self) -> impl IntoView`
In this section you define a set of controls that you can use to change the state of your component. Ready to use components can be
found in the `ui_components::widgets` module.
> [!TIP]
> You can also create your own custom controls that better suit your needs.
>
> This method is here to give you full freedom on how you would like to control your components
## Implementing `fn description(&self) -> &'static str`
It's the last but probably the most important part of implementing the your story.
While creating a description you should try to explain
1. What is your component about
2. How it should/shouldn't be used
3. When you should use it
4. You should describe the properties and their default values
"############;
pub trait Story: Default + Copy {
fn view(&self) -> impl IntoView {}
fn controls(&self) -> impl IntoView {}
fn description(&self) -> &'static str {
STORY_DESC
}
fn plays(&self) -> Vec<Box<dyn Play<Story = Self>>> {
Vec::new()
}
fn subroutes(&self) -> Vec<RouteDef> {
vec![]
}
}
pub trait IntoStory: Default + Copy {
type Story: Story + ThreadSafe;
fn into_story(self) -> Self::Story;
}
impl<T: Story + ThreadSafe> IntoStory for T {
type Story = T;
fn into_story(self) -> Self::Story {
T::default()
}
}