8000 [ext] Storybook integration for previewing components · Issue #967 · django-components/django-components · GitHub
[go: up one dir, main page]

Skip to content
[ext] Storybook integration for previewing components #967
Open
@JuroOravec

Description

@JuroOravec

Discussed in #921

Originally posted by AhmedSeraje January 21, 2025
A functionality such as the preview component in django-viewcomponent (https://testdriven.io/blog/django-reusable-components/#previewing-components) would be very welcomed :) Will easily help in speeding up the development


@burakyilmaz321 replied:

I had done a PoC study about this burakyilmaz321/django-components-storybook. I haven't developed it for a long time, but it's possible to integrate Storybook with a Django component.


@JuroOravec replied:

@burakyilmaz321 Oh now this slaps! 🔥

Do you have an example of how it's used in the wild?

IMO we could develop this into a full package that could live under the django-components org and make it the recommended way for previewing our components.

@burakyilmaz321 would you be interested in championing this? As you can see beow, currently I have too little practical experience with Storybook to be able to tell what the abstractions should look like.

1. Passing Storybook params to components

In the demo, I saw that components had defined storybook_parameters attribute.

I think this would make more sense to be a static function (maybe called just storybook), so that it's possible to pre-process the data that's sent from Storybook's UI, before we pass it to Component.render().

The default implementation could look like this (assuming that the the Storybook server uses GET requests):

class Button(Component):
    @staticmethod
    def storybook(cls, request):
        return cls.render(kwargs=requests.GET)

But since it would be a function, then you could do something like this, where in the UI you would choose an option as a plain string, but in the Python code it'd be "hydrated" to more:

STORYBOOK_PRESETS = {
    "primary": { ... },
    "secondary": { ... },
}

class Button(Component):
    ...
    @staticmethod
    def storybook(cls, request):
        preset_name = requests.GET.get("preset", "primary")
        kwargs = STORYBOOK_PRESETS[preset_name]
        return cls.render(kwargs=kwargs)

2. ❌ Automatically generate inputs?

Looking at the PoC, I'm missing how things would be set up in Storybook (I haven't used it personally yet). Where and how do you declare which components you have and what parameters they accept? Do you define them manually? If manually, do you define them next to the component definition (so inside the Django codebase), or separately?

If I remember right, Storybook allows you to pass in components arguments from within the UI (they call it parameters). Our Components can have their input types declared for type hints. We could make use of this and have a CLI command that would:

  1. Search for all components within the Django codebase
  2. For each component class, generate the Storybook *.stories.js file. (WHERE?)
  3. Inside the Storybook files, we'd generate the component parameters from Component's typing, so that those parameters can be set from within Storybook's UI.

‼️‼️ NOTE: Actually NO. I think we'd have to create Python representations for individual stories if we wanted to do it right, and that feels like going too deep into Storybook.

3. ❓ Automatically generate Storybook component bindings for django components?

Instead, what makes more sense is following:

In the Storybook docs, I see that they refer to the components like so:

// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
import type { Meta } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
  //👇 Creates specific parameters at the component level
  parameters: {
    backgrounds: {
      default: 'dark',
    },
  },
};
 
export default meta;

I haven't checked, but I assume that the Button is a function that accepts the parameters and returns a rendered HTML, so like a React component.

What we could do to make the integration with Storybook easier is to generate these functions. E.g. on the inside, our implementation of Button would make a call to server with given params:

‼️‼️ NOTE: ...But then again, maybe this is redundant, because Storybook already has the feature to support server-side rendered components?

Looking at the implementation, it is a bit outdated - in views.py, it should be now sufficient to just call Component.render, as Component.get_context_data shouldn't be called from the outside. So the update view should be:

def component_render_view(request, component_id: str):
    component_cls: Type[Component] = component_registry.all().get(component_id)
    component: Component = component_cls()
    try:
        storybook_parameters = {
            param: request.GET.get(param) for param in component.storybook_parameters
        }
    except AttributeError:
        storybook_parameters = {}

	rendered_component = component.render(
	    kwargs=storybook_parameters,
	)
    return HttpResponse(
		rendered_component,
		headers={"Access-Control-Allow-Origin": "*"},
	)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0