Skip to content

Commit

Permalink
Feature/7 preview cls (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-yin authored Jun 6, 2024
1 parent c2483fc commit bc57c9f
Show file tree
Hide file tree
Showing 20 changed files with 445 additions and 114 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@

This project is a reusable Django app that uses the following technologies:

1. Tailwind CSS for styling
2. Stimulus JS for interactivity
3. django-viewcomponent for preview discovery and rendering
1. [Tailwind CSS](https://tailwindcss.com/)
2. [Stimulus JS](https://stimulus.hotwire.dev/)

## Storybook Controls

Expand Down
Binary file added docs/source/images/bootstrap-preview.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/images/params-editor.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file removed docs/source/images/preview-v1.png
Binary file not shown.
2 changes: 2 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ Topics
:maxdepth: 2

install.md
install_with_viewcomponent.md
write_preview.md
params.md
contributing.md
143 changes: 43 additions & 100 deletions docs/source/install.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
# Installation

```{note}
If you already used `django-viewcomponent`, you can follow the [Installation with django-viewcomponent](install_with_viewcomponent.md).
```

```shell
$ pip install django-lookbook
```

`django-viewcomponent` will also be installed as a dependency.

Then add the app into `INSTALLED_APPS` in settings.py

```python
INSTALLED_APPS = [
...,
"django_viewcomponent",
"django_lookbook",
]
```

Add code below in settings.py

```python
VIEW_COMPONENTS = {
LOOKBOOK = {
# we will put previews in this directory later
"preview_base": ["previews"],
# show_previews is True by default
"show_previews": DEBUG,
}

# to make iframe work
X_FRAME_OPTIONS = "SAMEORIGIN"
```

Notes:

1. `preview_base` is the base path for your previews.
2. `show_previews` is a boolean value, which is used to control whether to show the previews. It is `True` by default, here we set it with same value of `DEBUG`. So the previews will only be shown in the development environment.

Update urls.py

```python
Expand All @@ -36,70 +44,49 @@ from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path("previews/", include("django_viewcomponent.urls")), # new
path("lookbook/", include("django_lookbook.urls")), # new
]
```

Next, let's create *previews/hello_preview.py*
Next, let's create *previews/template_preview.py*

```python
from django_lookbook.preview import LookbookPreview
from django.template import Context, Template
from django_viewcomponent.preview import ViewComponentPreview
from django.template.loader import render_to_string


class HelloComponentPreview(ViewComponentPreview):
def hello_world(self, **kwargs):
class PartialTemplatesPreview(LookbookPreview):

def header(self, **kwargs):
"""
This is a simple test for you to check how doc of the preview works
`includes/header.html` is a partial template, we can write preview for it in this way.
**Markdown syntax is supported in docstring**
"""
template = Template(
"""<div>Hello World</div>""",
)
return template.render(Context({}))
return render_to_string("includes/header.html")

def hello_world_with_name(self, name=None, **kwargs):
def footer(self, **kwargs):
"""
This preview is to display hello world for a specific name
We can write template code directly
"""
name = name if name else "Michael Yin"
template = Template(
"""<div>Hello {{ name }}</div>""",
"""<footer>Hello World</footer>""",
)
return template.render(Context({'name': name}))
return template.render(Context({}))
```

Notes:

1. We create `HelloWorldComponentPreview` which inherits from `ViewComponentPreview`, the class name `HelloComponentPreview` can be seen as a `group` which can contains multiple previews.
2. We define two methods `hello_world` and `hello_world_with_name` which will be used to render the preview
1. We create `PartialTemplatesPreview` which inherits from `LookbookPreview`, the class can be seen as a `group` which can contains multiple previews.
2. We define two methods `header` and `footer` which will be used to render the preview

```bash
├── previews
│   └── hello_preview.py
```

```bash
# create db tables and launch Django server
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
│   └── template_preview.py
```

Now please check on [http://127.0.0.1:8000/lookbook](http://127.0.0.1:8000/lookbook):

1. The preview has been automatically detected and can be seen in the left sidebar
2. You can see the UI of the preview on the right side and final HTML source code can also be seen
3. The docstring of the preview has been extracted and display in the `Notes` tab, `Markdown` syntax is supported

![](./images/preview-v1.png)

Each time we visit a preview, the method would be called and the final result would be displayed in the top iframe.

## Override Template

In some cases, you might need to render HTML code which need work with your own CSS and JS. You can override the `preview` template to include them.

Create *django_viewcomponent/preview.html* in the project `templates` directory
Create *django_lookbook/preview.html* in the project `templates` directory

```html
<!DOCTYPE html>
Expand Down Expand Up @@ -127,66 +114,22 @@ Create *django_viewcomponent/preview.html* in the project `templates` directory
1. We import Bootstrap CSS and JS to the page.
2. `preview_html` is the HTML generated by the preview method.

Now if we refresh the page and check again, the `preview` HTML should be rendered with Bootstrap CSS and JS.

If you have other frontend assets such as Alpine.js, jQuery or CSS file, **you should remember to include them in this template file**.

### Params Editor

Next, let's make our preview work with dynamic parameters.

Update *previews/hello_preview.py*

```python
from django import forms
from django.template import Context, Template
from django_viewcomponent.preview import ViewComponentPreview
from django_lookbook.utils import register_form_class # new


class HelloForm(forms.Form): # new
"""
This is to show how to add parameter editor to preview
"""
name = forms.CharField(
label="Name",
max_length=100,
help_text="Enter name text",
initial="",
)


class HelloComponentPreview(ViewComponentPreview):
def hello_world(self, **kwargs):
"""
This is a simple test for you to check how doc of the preview works
"""
template = Template(
"""<div>Hello World</div>""",
)
return template.render(Context({}))

@register_form_class(HelloForm) # new
def hello_world_with_name(self, name=None, **kwargs):
"""
This preview is to display hello world for a specific name
"""
name = name if name else "Michael Yin"
template = Template(
"""<div>Hello {{ name }}</div>""",
)
return template.render(Context({'name': name}))
```{note}
If you have other frontend assets such as Alpine.js, jQuery or CSS file, you should remember to include them in this template file `django_lookbook/preview.html`.
```

Notes:

1. We defined a `HelloForm`, which is a regular Django form, nothing special.
2. Use `@register_form_class(HelloForm)` to attach the form class to the method `hello_world_with_name`
```bash
# create db tables and launch Django server
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
```

If we check the `hello_world_with_name` preview's `Params` tab, we can see the form.
Now please check on [http://127.0.0.1:8000/lookbook](http://127.0.0.1:8000/lookbook):

And then, let's input `Elon Musk` in the `name` field, we can see the top iframe is also updated in real-time.
1. The preview has been automatically detected and can be seen in the left sidebar
2. You can see the UI of the preview on the right side and final HTML source code can also be seen
3. The docstring of the preview has been extracted and display in the `Notes` tab, `Markdown` syntax is supported

![](./images/preview-v2.png)
Each time we visit a preview, the preview method would be called and the HTML would be displayed in the top iframe.

In this case, the `name` field value is passed to the `hello_world_with_name` method, then it can generate HTML according to the value.
![](./images/bootstrap-preview.jpg)
127 changes: 127 additions & 0 deletions docs/source/install_with_viewcomponent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Installation with django-viewcomponent

```{note}
If your project already used `django-viewcomponent`, you can follow this guide to install `django-lookbook`.
```

```shell
$ pip install django-lookbook
```

Then add the app into `INSTALLED_APPS` in settings.py

```python
INSTALLED_APPS = [
...,
"django_viewcomponent",
"django_lookbook", # new
]
```

Add code below in settings.py

```python
VIEW_COMPONENTS = {
# we will put previews in this directory later
"preview_base": ["previews"],
# show_previews is True by default
"show_previews": DEBUG,
}

# to make iframe work
X_FRAME_OPTIONS = "SAMEORIGIN"
```

Notes:

1. `preview_base` is the base path for your previews.
2. `show_previews` is a boolean value, which is used to control whether to show the previews. It is `True` by default, here we set it with same value of `DEBUG`. So the previews will only be shown in the development environment.

Update urls.py

```python
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path("previews/", include("django_viewcomponent.urls")), # new
path("lookbook/", include("django_lookbook.urls")), # new
]
```

Next, let's create *previews/hello_preview.py*

```python
from django.template import Context, Template
from django_viewcomponent.preview import ViewComponentPreview


class HelloComponentPreview(ViewComponentPreview):
def hello_world(self, **kwargs):
"""
This is a simple test for you to check how doc of the preview works
"""
template = Template(
"""<div>Hello World</div>""",
)
return template.render(Context({}))
```

Notes:

1. We create `HelloWorldComponentPreview` which inherits from `ViewComponentPreview`, the class name `HelloComponentPreview` can be seen as a `group` which can contains multiple previews.
2. We define two methods `hello_world` and `hello_world_with_name` which will be used to render the preview

```bash
├── previews
│   └── hello_preview.py
```

Create *django_viewcomponent/preview.html* in the project `templates` directory

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
</head>
<body>

<div>
{{ preview_html }}
</div>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4"
crossorigin="anonymous"></script>
</body>
</html>
```

1. We import Bootstrap CSS and JS to the page.
2. `preview_html` is the HTML generated by the preview method.

Now if we refresh the page and check again, the `preview` HTML should be rendered with Bootstrap CSS and JS.

```{note}
If you have other frontend assets such as Alpine.js, jQuery or CSS file, you should remember to include them in this template file `django_viewcomponent/preview.html`.
```

```bash
# create db tables and launch Django server
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
```

Now please check on [http://127.0.0.1:8000/lookbook](http://127.0.0.1:8000/lookbook):

1. The preview has been automatically detected and can be seen in the left sidebar
2. You can see the UI of the preview on the right side and final HTML source code can also be seen
3. The docstring of the preview has been extracted and display in the `Notes` tab, `Markdown` syntax is supported

Each time we visit a preview, the method would be called and the final result would be displayed in the top iframe.
Loading

0 comments on commit bc57c9f

Please sign in to comment.