From 8de499b619504a856e46d010e109f9b15bdf04e7 Mon Sep 17 00:00:00 2001 From: Thibaud Colas Date: Wed, 28 Oct 2020 13:51:19 +0000 Subject: [PATCH 1/6] Add basic favicon --- docs/images/favicon.png | Bin 0 -> 5005 bytes docs/images/logo.svg | 26 ++++++++++++++++++++++++++ mkdocs.yml | 4 ++++ 3 files changed, 30 insertions(+) create mode 100644 docs/images/favicon.png create mode 100644 docs/images/logo.svg diff --git a/docs/images/favicon.png b/docs/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..128e432654f65154792870694f62322077687569 GIT binary patch literal 5005 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 AXaE2J literal 0 HcmV?d00001 diff --git a/docs/images/logo.svg b/docs/images/logo.svg new file mode 100644 index 00000000..10a6f2a8 --- /dev/null +++ b/docs/images/logo.svg @@ -0,0 +1,26 @@ + + + u267B-blackuniversalrecyclingsymbol + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index e269d9c9..b9c5156c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,10 @@ dev_addr: 0.0.0.0:8001 theme: name: material font: false + palette: + primary: indigo + logo: images/logo.svg + favicon: images/favicon.png markdown_extensions: - codehilite - admonition From f16f4e7851738dce7c5f419a7d8c828c791025fa Mon Sep 17 00:00:00 2001 From: Thibaud Colas Date: Wed, 28 Oct 2020 13:51:55 +0000 Subject: [PATCH 2/6] Add "last updated at" at the bottom of documentation pages --- mkdocs.yml | 4 +++- pyproject.toml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index b9c5156c..cad8f547 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,7 +24,9 @@ markdown_extensions: - pymdownx.tasklist: custom_checkbox: true - pymdownx.tilde - +plugins: + - search + - git-revision-date nav: # Auto-magically copied from the root of the project in the CI build. - 'Home': 'README.md' diff --git a/pyproject.toml b/pyproject.toml index dd11da5f..9bc16558 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ isort = {version = "^4.3", extras = ["pyproject"]} mkdocs = "^1.1.2" mkdocs-material = "^5.5.14" pymdown-extensions = "^8.0" +mkdocs-git-revision-date-plugin = "^0.3.1" [tool.isort] known_first_party = "pattern_library" From e22e9fbf98813b5795709aed5d521735c9c475e7 Mon Sep 17 00:00:00 2001 From: Thibaud Colas Date: Thu, 29 Oct 2020 12:32:12 +0000 Subject: [PATCH 3/6] Add first draft of docs overhaul --- .travis.yml | 1 - CONTRIBUTING.md | 11 + README.md | 147 ++-------- docs/README.md | 5 + docs/getting-started.md | 170 ++++++++++++ docs/guides/customizing-template-rendering.md | 23 ++ docs/guides/defining-template-context.md | 59 ++++ .../overriding-template-tags.md} | 257 +----------------- docs/images/favicon.svg | 26 ++ .../getting-started/PatternLibraryEmpty.png | Bin 0 -> 24235 bytes .../getting-started-complete.png | Bin 0 -> 34606 bytes docs/images/pattern-library-screenshot.webp | Bin 0 -> 57682 bytes .../images}/pattern-library-talk-youtube.webp | Bin docs/index.md | 31 +++ docs/reference/concepts.md | 24 ++ docs/reference/examples.md | 101 +++++++ docs/reference/related-projects.md | 13 + mkdocs.yml | 16 +- 18 files changed, 507 insertions(+), 377 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/getting-started.md create mode 100644 docs/guides/customizing-template-rendering.md create mode 100644 docs/guides/defining-template-context.md rename docs/{overview.md => guides/overriding-template-tags.md} (56%) create mode 100644 docs/images/favicon.svg create mode 100644 docs/images/getting-started/PatternLibraryEmpty.png create mode 100644 docs/images/getting-started/getting-started-complete.png create mode 100644 docs/images/pattern-library-screenshot.webp rename {.github => docs/images}/pattern-library-talk-youtube.webp (100%) create mode 100644 docs/index.md create mode 100644 docs/reference/concepts.md create mode 100644 docs/reference/examples.md create mode 100644 docs/reference/related-projects.md diff --git a/.travis.yml b/.travis.yml index f530f5c6..9b2383cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,6 @@ jobs: - pip install poetry - poetry install script: - - cp README.md docs/README.md - poetry run mkdocs build deploy: - provider: pages diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4d252e7a..14e29c87 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,6 +53,17 @@ npm run build npm run start ``` +### Documentation + +The project’s documentation website is built with [MkDocs](https://www.mkdocs.org/). + +```sh +# One-off build. +poetry run mkdocs build +# Rebuild the docs as you work on them +poetry run mkdocs serve +``` + ## Running the tests To run the python tests, use the script in the root of the repo: diff --git a/README.md b/README.md index 33baef00..b874daeb 100644 --- a/README.md +++ b/README.md @@ -1,150 +1,41 @@ -# Django pattern library +# [django-pattern-library](https://torchbox.github.io/django-pattern-library/) [![PyPI](https://img.shields.io/pypi/v/django-pattern-library.svg)](https://pypi.org/project/django-pattern-library/) [![PyPI downloads](https://img.shields.io/pypi/dm/django-pattern-library.svg)](https://pypi.org/project/django-pattern-library/) [![Travis](https://travis-ci.com/torchbox/django-pattern-library.svg?branch=master)](https://travis-ci.com/torchbox/django-pattern-library) [![Total alerts](https://img.shields.io/lgtm/alerts/g/torchbox/django-pattern-library.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/torchbox/django-pattern-library/alerts/) -A module for Django that helps you to build pattern libraries. +> UI pattern libraries for Django templates ![Screenshot of the pattern library UI, with navigation, pattern rendering, and configuration](https://raw.githubusercontent.com/torchbox/django-pattern-library/master/.github/pattern-library-screenshot.webp) -## Documentation - -Documentation is located on GitHub in [`docs/`](https://github.com/torchbox/django-pattern-library/tree/master/docs). - -## Objective - -At the moment, the main focus is to allow developers and designers -use exactly the same Django templates in a design pattern library -and in production code. - -There are a lot of alternative solutions for building -pattern libraries already. Have a look at [Pattern Lab](http://patternlab.io/) and -[Astrum](http://astrum.nodividestudio.com/), for example. -But at [Torchbox](https://torchbox.com/) we mainly use Python and Django and -we find it hard to maintain layout on big projects in several places: -in a project's pattern library and in actual production code. This is our -attempt to solve this issue and reduce the amount of copy-pasted code. - -To learn more about how this package can be used, have a look at our Wagtail Space 2020 talk: [Reusable UI components: A journey from React to Wagtail](https://www.youtube.com/watch?v=isrOufI7TKc) - -[![Reusable UI components: A journey from React to Wagtail](https://raw.githubusercontent.com/torchbox/django-pattern-library/master/.github/pattern-library-talk-youtube.webp)](https://www.youtube.com/watch?v=isrOufI7TKc) - -## Concepts -To understand how `django-pattern-library` works, the following concepts are important. - -### Patterns -Any template that is displayed by the pattern library is referred to as a pattern. Patterns are divided into two categories: fragments and pages. - -### Fragments -A fragment is a pattern whose markup does not include all of the resources (typically CSS and Javascript) for it to be displayed correctly on its own. This is typical for reusable component templates which depend on global stylesheets or Javascript bundles to render and behave correctly. - -To enable them to be correctly displayed in the pattern library, `django-pattern-library` will inject the rendered markup of fragments into the **pattern base template** specified by `PATTERN_LIBRARY['PATTERN_BASE_TEMPLATE_NAME']`. - -This template should include references to any required static files. The rendered markup of fragments will be available in the `pattern_library_rendered_pattern` context variable (see the tests for [an example](https://github.com/torchbox/django-pattern-library/blob/master/tests/templates/patterns/base.html)). - -### Pages -In contrast to fragments, pages are patterns that include everything they need to be displayed correctly in their markup. Pages are defined by `PATTERN_LIBRARY['BASE_TEMPLATE_NAMES']`. - -Any template in that list — or that extends a template in that list — is considered a page and will be displayed as-is when rendered in the pattern library. - -It is common practice for page templates to extend the pattern base template to avoid duplicate references to stylesheets and Javascript bundles. Again, [an example](https://github.com/torchbox/django-pattern-library/blob/master/tests/templates/patterns/base_page.html) of this can be seen in the tests. +## Features -## How to install +This package automates the maintenance of UI pattern libraries or styleguides for Django projects, and allows developers to experiment with Django templates without having to create Django views and models. -First install the library: +- Create reusable patterns by creating Django templates files as usual. +- All patterns automatically show up in the pattern library’s interface. +- Define data as YAML files for the templates to render with the relevant Django context. +- Override Django templates tags as needed to mock the template’s dependencies. +- Document your patterns with Markdown. -```sh -pip install django-pattern-library -# ... or... -poetry add django-pattern-library -``` - - -Then, in your Django settings, add `pattern_library` into your `INSTALLED_APPS`, and `pattern_library.loader_tags` to `OPTIONS['builtins']` into the `TEMPLATES` setting. For example: - -```python -INSTALLED_APPS = [ - # ... - 'pattern_library', - # ... -] - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - 'builtins': ['pattern_library.loader_tags'], - }, - }, -] -``` - -Note that this module only supports the Django template backend. - -### Settings - -Next, set the `PATTERN_LIBRARY` setting. Here's an example showing the defaults: - -```python -PATTERN_LIBRARY = { - # PATTERN_BASE_TEMPLATE_NAME is the template that fragments will be wrapped with. - # It should include any required CSS and JS and output - # `pattern_library_rendered_pattern` from context. - 'PATTERN_BASE_TEMPLATE_NAME': 'patterns/base.html', - # Any template in BASE_TEMPLATE_NAMES or any template that extends a template in - # BASE_TEMPLATE_NAMES is a "page" and will be rendered as-is without being wrapped. - 'BASE_TEMPLATE_NAMES': ['patterns/base_page.html'], - 'TEMPLATE_SUFFIX': '.html', - # SECTIONS controls the groups of templates that appear in the navigation. The keys - # are the group titles and the values are lists of template name prefixes that will - # be searched to populate the groups. - 'SECTIONS': ( - ('atoms', ['patterns/atoms']), - ('molecules', ['patterns/molecules']), - ('organisms', ['patterns/organisms']), - ('templates', ['patterns/templates']), - ('pages', ['patterns/pages']), - ), -} - -``` - -Note that the templates in your `PATTERN_LIBRARY` settings must be available to your project's -[template loaders](https://docs.djangoproject.com/en/3.1/ref/templates/api/#loader-types). - -### URLs - -Include `pattern_library.urls` in your `urlpatterns`. Here's an example `urls.py`: +## Documentation -```python -from django.apps import apps -from django.conf.urls import url, include +Documentation is available at [torchbox.github.io/django-pattern-library/](https://torchbox.github.io/django-pattern-library/), with source files in the `docs` directory. -urlpatterns = [ - # ... Your URLs -] +- [Getting started](https://torchbox.github.io/django-pattern-library/getting-started/) +- Guides +- Reference -if apps.is_installed('pattern_library'): - urlpatterns += [ - url(r'^pattern-library/', include('pattern_library.urls')), - ] -``` +## Examples of usage -This package is not intended for production. It is **highly recommended** to only enable this package in testing environments for a restricted, trusted audience. One simple way to do this is to only expose its URLs if `apps.is_installed('pattern_library')`, as demonstrated above, and only have the app installed in environment-specific settings. +At [Torchbox](https://torchbox.com/), we use this package for all of the Wagtail websites we build, for example [rca.ac.uk](https://github.com/torchbox/rca-wagtail-2019). ## Contributing See anything you like in here? Anything missing? We welcome all support, whether on bug reports, feature requests, code, design, reviews, tests, documentation, and more. Please have a look at our [contribution guidelines](https://github.com/torchbox/django-pattern-library/blob/master/CONTRIBUTING.md). -If you just want to set up the project on your own computer, the contribution guidelines also contain all of the setup commands. +If you want to set up the project on your own computer, the contribution guidelines also contain all of the setup commands. ## Credits View the full list of [contributors](https://github.com/torchbox/django-pattern-library/graphs/contributors). [BSD](https://github.com/torchbox/django-pattern-library/blob/master/LICENSE) licensed. + +Project logo from [FxEmoji](https://github.com/mozilla/fxemoji). Documentation website built with [MkDocs](https://www.mkdocs.org/), and hosted in [GitHub Pages](https://pages.github.com/). diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..e35875f7 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,5 @@ +# Documentation + +This documentation is built as a website with [MkDocs](https://www.mkdocs.org/): [torchbox.github.io/django-pattern-library/](https://torchbox.github.io/django-pattern-library/). + +To build locally, view the project’s `CONTRIBUTING.md`. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 00000000..99772a24 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,170 @@ +# Getting started + +## Installation + +django-pattern-library is available [on PyPI](https://pypi.org/project/django-pattern-library/). First install it in your Django project: + +```sh +# With pip, +pip install django-pattern-library +# Alternatively, with Poetry, +poetry add --dev django-pattern-library +``` + +### Compatibility + +We support: + +- Django 2.2.x, 3.0.x, 3.1.x +- Python 3.6, 3.7, 3.8 +- Django Templates only, no Jinja support + +## Configuration + +### Django settings + +In your Django settings file, add `pattern_library` to `INSTALLED_APPS`: + +```python +INSTALLED_APPS = [ + # ... + "pattern_library", + # ... +] +``` + +Also add `pattern_library.loader_tags` to `OPTIONS["builtins"]` into the `TEMPLATES` setting: + +```python +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + "builtins": [ + "pattern_library.loader_tags" + ], + }, + }, +] +``` + +### Pattern library settings + +Still in Django settings, set the `PATTERN_LIBRARY` setting. Here is an example showing the defaults: + +```python +PATTERN_LIBRARY = { + # Groups of templates for the pattern library navigation. The keys + # are the group titles and the values are lists of template name prefixes that will + # be searched to populate the groups. + "SECTIONS": ( + ("components", ["patterns/components"]), + ("pages", ["patterns/pages"]), + ), + + # Configure which files to detect as templates. + "TEMPLATE_SUFFIX": ".html", + + # Set which template components should be rendered inside of, + # so they may use page-level component dependencies like CSS. + "PATTERN_BASE_TEMPLATE_NAME": "patterns/base.html", + + # Any template in BASE_TEMPLATE_NAMES or any template that extends a template in + # BASE_TEMPLATE_NAMES is a "page" and will be rendered as-is without being wrapped. + "BASE_TEMPLATE_NAMES": ["patterns/base_page.html"], +} +``` + +Note the templates in your `PATTERN_LIBRARY` settings must be available to [template loaders](https://docs.djangoproject.com/en/3.1/ref/templates/api/#loader-types). + +### URLs + +Include `pattern_library.urls` in your `urlpatterns`. Here is an example `urls.py`: + +```python +from django.apps import apps +from django.urls import include, path + +urlpatterns = [ + # … Your URLs +] + +if apps.is_installed("pattern_library"): + urlpatterns += [ + path("pattern-library/", include("pattern_library.urls")), + ] +``` + +!!! warning "Security" + + This package isn’t intended for production usage, and hasn’t received extensive security scrutiny. + +It is **highly recommended** to only enable this package in testing environments, for a restricted, trusted audience. One way to do this is to only expose its URLs if `apps.is_installed("pattern_library")`, as demonstrated above, and only have the app installed in environment-specific settings. + +--- + +Alright, now that we got this far, we can navigate to `http://localhost:8000/pattern-library/` to see our pattern library! But if we tried to do this now, we would likely get a `PatternLibraryEmpty` error – this is expected, as we haven’t added any patterns yet. + +![Screenshot of the PatternLibraryEmpty error message from Django](images/getting-started/PatternLibraryEmpty.png) + +Now let’s look at adding our first template! + +## First pattern + +Now we’ve done all of the configuration – let’s create a UI component. We’ll use `quote-block` as an example, and place it at `patterns/components/quote_block/quote_block.html` inside one of our Django apps: + +```html +
+
+

{{ quote }}

+ {% if attribution %} +

{{ attribution }}

+ {% endif %} +
+
+``` + +### Base template + +We additionally need to customize a base template, so the standalone component can be rendered within a page with CSS. This is what the `PATTERN_BASE_TEMPLATE_NAME` setting is for. As a separate template in `patterns/base.html`: + +```html + + + + + + My Base + + + {% block content %} + {# pattern_library_rendered_pattern is where the pattern library will inject the rendered pattern. #} + {{ pattern_library_rendered_pattern }} + {% endblock %} + + +``` + +`quote_block` should now appear in the pattern library UI menu! But the template doesn’t display anything – we additionally need to provide it with test data. + +### Component data + +We can provide context and tags overrides for our new component by creating a `quote_block.yaml` YAML file alongside the HTML, at `patterns/components/quote_block/quote_block.yaml` in our example. + +```yaml +context: + quote: What is love? + attribution: Haddaway +``` + +And that’s it! Our `quote_block` should finally appear in the pattern library, along with its rendering with this mock data. + +![Screenshot of the quote_block template](images/getting-started/getting-started-complete.png) diff --git a/docs/guides/customizing-template-rendering.md b/docs/guides/customizing-template-rendering.md new file mode 100644 index 00000000..c82000cf --- /dev/null +++ b/docs/guides/customizing-template-rendering.md @@ -0,0 +1,23 @@ +# Customizing template rendering + +## Customizing the patterns’ surroundings + +All patterns that are not pages are rendered within a base page template. The pattern library will render patterns inside the `content` block, which you can tweak to change how patterns are displayed. + +You can for example add a theme wrapper around the components: + +```html +{% block content %} + {% if pattern_library_rendered_pattern %} +
+ {{ pattern_library_rendered_pattern }} +
+ {% endif %} +{% endblock %} +``` + +`pattern_library_rendered_pattern` can also be used to do other modifications on the page for the pattern library only, for example adding an extra class to ``: + +```html + +``` diff --git a/docs/guides/defining-template-context.md b/docs/guides/defining-template-context.md new file mode 100644 index 00000000..3ae240fd --- /dev/null +++ b/docs/guides/defining-template-context.md @@ -0,0 +1,59 @@ +# Defining template context + +To define fake context you need to create a YAML file alongside your +template file. For example, for the template `big_red_button.html` you need to +create a file called `big_red_button.yaml`. + +Let's imagine that your `big_red_button.html` template looks like this: + +```django + + {{ button_text }} + +``` + +The `big_red_button.yaml` can be something like this: + +```yaml +context: + button_link: https://example.com/ + button_text: Example link +``` + +In the same way you can provide context in more complex templates. Here is +an example on how you can define fake context that pretends to be a `QuerySet`. + +Let's assume you have the following template: + +```django +{% if my_objects.exists %} + {{ items_title }} + +{% endif %} +``` + +You might define a `yaml` file similar to this to provide fake data: + +```yaml +name: My example pattern + +context: + items_title: Related pages + my_objects: + exists: true # simulate `QuerySet`'s `exists` method + all: # simulate `QuerySet`'s `all` method + - title: Page 1 + link: /page1 + - title: Page 2 + link: /page2 +``` + +You can define a list or a dict or anything that [`PyYAML`](http://pyyaml.org/wiki/PyYAMLDocumentation) allows you to create in `yaml` format without creating a custom objects. diff --git a/docs/overview.md b/docs/guides/overriding-template-tags.md similarity index 56% rename from docs/overview.md rename to docs/guides/overriding-template-tags.md index 617237cf..8a099ad9 100644 --- a/docs/overview.md +++ b/docs/guides/overriding-template-tags.md @@ -1,122 +1,4 @@ -# Overview - -The main idea of this package is -to allow you use template in both: your pattern library and -production code of your Django project. - -To achieve this the package must to provide a way to: - -* Define a fake context for templates -* Override template tags (default and custom ones) - -## Directory structure - -The initial structure of your pattern library should look like this: - -``` -. -├── templates -| └── patterns -| |── atoms -| |── molecules -| |── organisms -| |── templates -| └── pages -└── templatetags -``` - -- a `templates/patterns` directory with subfolders for the different levels of - pattern, following standard atomic design naming conventions. - - Your `PATTERN_LIBRARY_TEMPLATE_DIR` setting should point to the library's - root `templates` directory -- a `templatetags` directory for holding template tag overrides (see below) - - -## Defining fake context for templates - -To define fake context you need to create a `yaml` file alongside your -template file. For example, for the template `big_red_button.html` you need to -create a file called `big_red_button.yaml`. - -Let's imagine that your `big_red_button.html` template looks like this: - -```django - - {{ button_text }} - -``` - -The `big_red_button.yaml` can be something like this: - -```yaml -context: - button_link: https://example.com/ - button_text: Example link -``` - -In the same way you can provide context in more complex templates. Here is -an example on how you can define fake context that pretends to be a `QuerySet`. - -Let's assume you have the following template: - -```django -{% if my_objects.exists %} - {{ items_title }} - -{% endif %} -``` - -You might define a `yaml` file similar to this to provide fake data: - -```yaml -name: My example pattern - -context: - items_title: Related pages - my_objects: - exists: true # simulate `QuerySet`'s `exists` method - all: # simulate `QuerySet`'s `all` method - - title: Page 1 - link: /page1 - - title: Page 2 - link: /page2 -``` - -You can define a list or a dict or anything that -[`PyYAML`](http://pyyaml.org/wiki/PyYAMLDocumentation) -allows you to create in `yaml` format without creating a custom objects. - -## Customising the patterns’ surroundings - -All patterns that are not pages are rendered within a base page template, `pages/base.html` by default. The pattern library will render patterns inside the `content` block, which you can tweak to change how patterns are displayed. - -You can for example add a theme wrapper around the components: - -```html -{% block content %} - {% if pattern_library_rendered_pattern %} -
- {{ pattern_library_rendered_pattern }} -
- {% endif %} -{% endblock %} -``` - -`pattern_library_rendered_pattern` can also be used to do other modifications on the page for the pattern library only, for example adding an extra class to ``: - -```html - -``` - -## Override template tags +# Overriding template tags The package overrides the following Django tags: @@ -210,23 +92,20 @@ our production code. Assuming that you already have module installed in your project, to define a fake implementation we need to: -1. Create [a `templatetags` package](https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/#code-layout) - in one of your apps. Note that your app should be defined in - [`INSTALLED_APPS`](https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-INSTALLED_APPS) - and it should be defined after the package you are - overriding (`some_package` in our case). -2. Create `image_utils.py` in this package with the following code: +First, create a [`templatetags` package](https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/#code-layout) in one of your apps. Note that your app should be defined in [`INSTALLED_APPS`](https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-INSTALLED_APPS) and it should be defined after the package you are overriding (`some_package` in our case). - ```python - from some_package.templatetags.image_utils import register +Then, create `image_utils.py` in this package with the following code: - from pattern_library.monkey_utils import override_tag +```python +from some_package.templatetags.image_utils import register - # We are monkey patching here - # Note that `register` should be an instance of `django.template.Library` - # and it's important to the instance where the original tag is defined. - override_tag(register, name='image') - ``` +from pattern_library.monkey_utils import override_tag + +# We are monkey patching here +# Note that `register` should be an instance of `django.template.Library` +# and it's important to the instance where the original tag is defined. +override_tag(register, name='image') +``` **Note:** it's recommended to have a single app that contains all template tag overrides, so it's easy to exclude it from `INSTALLED_APPS` in production, @@ -376,115 +255,3 @@ tags: Note the `target_var` field. -## Some more examples - -### Looping over a template tag - -#### HTML - -``` -{% social_media_links as social_links %} - -``` - -#### Yaml - -``` -tags: - social_media_links: - as social_links: - raw: - - url: '#' - type: twitter - label: Twitter - - url: '#' - type: facebook - label: Facebook - - url: '#' - type: instagram - label: Instagram - - url: '#' - type: youtube - label: YouTube - - url: '#' - type: linkedin - label: LinkedIn -``` - -### Inclusion tags - -#### HTML - -``` - -``` - -#### YAML - -``` -tags: - footernav: - "": - template_name: "patterns/molecules/navigation/footernav.html" -``` - -### Image lazy load example - -#### HTML - -``` - {% image slide.image fill-100x71 as imageSmall %} - {% image slide.image fill-829x585 as imageLarge %} - - {% include "patterns/atoms/image/image--lazyload.html" with imageSmall=imageSmall width=829 height=585 imageLarge=imageLarge classList='slide__image' %} -``` - -#### YAML -``` -tags: - image: - slide.image fill-100x71 as imageSmall: - target_var: imageSmall - raw: - url: '//placekitten.com/100/71' - slide.image fill-829x585 as imageLarge: - target_var: imageLarge - raw: - url: '//placekitten.com/829/585' - width: '829' - height: '585' -``` - -##### Image inlude example - -#### HTML -``` -{{ imageLarge.alt }} -``` - -#### YAML -``` -context: - width: '829' - height: '585' - imageSmall: - url: '//placekitten.com/100/71' - imageLarge: - url: '//placekitten.com/829/585' -``` diff --git a/docs/images/favicon.svg b/docs/images/favicon.svg new file mode 100644 index 00000000..10a6f2a8 --- /dev/null +++ b/docs/images/favicon.svg @@ -0,0 +1,26 @@ + + + u267B-blackuniversalrecyclingsymbol + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/images/getting-started/PatternLibraryEmpty.png b/docs/images/getting-started/PatternLibraryEmpty.png new file mode 100644 index 0000000000000000000000000000000000000000..073349c38e7d364e1c7cbc4ebb3a563e25bc396d GIT binary patch literal 24235 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 literal 0 HcmV?d00001 diff --git a/docs/images/getting-started/getting-started-complete.png b/docs/images/getting-started/getting-started-complete.png new file mode 100644 index 0000000000000000000000000000000000000000..6b1178f211d22297a2fd1dc9a6f15acace20051f GIT binary patch literal 34606 zcmce-WmH^Ew=UX1kl^m_?$$_f3ogOkT^g4_aF<4c1PugtcS3Lv?%ue&!{vSVx9_;; z$3FMmKX>#fd#Y+y%{i;8*Lc>dNEIa+6huNq004j@Co8E20DJ%e0MK#>Fz?94{YKP# z;X*}0LyC)wi<5(q`qFLu8y|4nyk8-uBL{i zwiXEF2UJx8N{RvHWhzZg+AS^84Gro|O&HZx1Ox={!WkJE+g4YP@9y@lub0nG2e-Ep z#zrR(_6ufasumWx>+Ajc`){6}*3Zu`9v&>aI#Z{o=Z=qJ`Um{{d?zQz)znmBVG)Id z1mE7?o?l)co}XXe-ZnQkWu&F3Y3X)%w?#xgQBhIj;o;NM)64_JUnD)XGTXy#>U2ujEoEn^alk6+S}XD z%*^!m{<*xoyuQAzb+IYmVUc6RpF)g=!PcTJ7UlaqZH7bh0_Gcq!AVq%ifkx>iN z^QNZy_VyMwHa1pPRxvTr@$nH%Ol&45CL|ih`7FTt34g``7fp9l9@wBz(?d|0s z9c8Sp(#OV71_ZDb6nvnh^(!g;R$U#|(h}O#>;z7*4hb>!@``9}!<3h2N=rlG{Gv?M}R2D`0k4ehYhB)y|LzA9Nm*q0?haZV=uGbhN)(bLJ5vggIWwSl&fE~ zZaw+l>XT>Ez*8OZfJ?Gk@LkA2=-G>Y+DnV%pBrlZBWQYw01k1K2h6AD`HcIs>qWq6;xJA&UN2* z*D?=67RrqSHOhi@V5!6yJ6DRKFwxORM=UF8?K87eA#^hPVuaYV!sM)GXg{y|&Z2xx z=dn?NJ@{eV$f@3V-GtT2!Xzu7{fGnZuHC%M(%+T1EIzJ--glcx6dx0qs7-ktmmYteTFLmL8QT z>@$~Ye{TNxckcBs51-T!oPR=dDlM-srQx1DJ}Ilz8FBW~3T*WkJ2%ua-kBI=g@s++ z1cL<=0h6i1G}OeAS5R0KirU?n&16vK9a&d>*W1OWuW}|@!1J2_!~N^K4r$caF&x=8 zTk=@d3?NGJIQD^70Spjv(cy3&XE~omVfu@IfLuWlj9Hw6Z5gMhZ%LX}{Y3_+hgNm# zfiBTD?9k%4JvzEOrrnQFGxn-;$fg$TARqGnV+00D((y@~V(WRBZqb$F7~DW@T{@Dw zWYZvUEI~bfu%u!B$xCMf*`#bYxQ>DL{EN_M+i1+_Ot0c?lB$Z3)NDSxu;T_WU$N8( z^IlU`7uqO6#MRUM`ujprNF+3JX04AiLlF^IN+cWCOI1}>?aL60&s>cl&XQcZA;9t% z*+4ZbKQ;RqpI-ROy|3I(x6Hu)5>7J88r0m_eY85kd|@5Zlk@<(UNx3K_&@#yR661NXIg zOMyymy$(4R-CNLFfW*x6#a>QNs?%8!zt2Y?F0($_{_-6%aMWQuJui=k+-iD@i6R_U z@y8+vU7-Ivw#0*>?g+eBja?qo6Cf%j6t5^uS?iOvGIZo1hHJF<6n}rs2k>-@$?}J- zFN1mT>$IX@;43A&5&K`24WkNZA;uUUvnHaYKktLw^O!LIL19tBzk`9kSWK@{@75#S>?8+jl?{ zbwC3&$Av&jrE1_4cBu$hnAi10H>f zhLpY-f1}rmaUtZcJ}cJ?=~#QKBoJppc~{7{IJG*`FkBphteMNe*ulJRrd9X1c%)t= zajEd)=BDaDgF`~ct@#)tvBWE=;;=ECR|M>50v2_HHcNkJ}*qYtw=xOjx!ppEPz7wkz{mqD4-g_GKNBANaxcJf0-ltY?)!+*_4|ZP7vs)*x;R;1uu|K2gz~)1q7s?# z!DjVywQ|{Wsw>27Hp+H2y4PV1(^^(Xud@+ru_fFtc}*!F<%%1l9{vH+_`sw?6yJ!}=r#$^6Mq!o7kDEz@_yAXmDd z8K6c5YGmary2SD&0gW>-Rwc!bFe|a{6MNSDiZo#6&E{3I;UZM}gH^6B9g-Fp?a<6z z=C8_*1!aOa9$CdQRp(`OfH zK|ux4cl&b^=ZSPxbCM!*u#*=d_OLtLkyhhSgx_@RJ*FU)b!+jtJj->fG@$xsxhmJz zc<7q8#@CEtfTvBeTBo2~2Q|n9gpT#DM{E{9b&8B&2n?~PEqnlafH7`L^KT32q|P^_ zBPk#u4S(8R0#WA-1@KK2DmM=P9)+Hm(H_8{iHkJ4hX@Q{Zdf(N$4;omj87$qc^3e( z6JY)b-C{HT)FMgby5{+G`aIP^?4->1DEi<|kat)Yi*g5gJSEd>UFI*W4x9bSSGkk$ zoD*Of4!N@e{#i8)>qb@+S@!FWruW%?>hx{rW~G8GJUJYVlP2Cx^q2;5^dEd%>R0Jx z5MbSGJc18*@%TDnc5nwm#UrkLJECa0_}bDD5~iFG%;&Frb0`5qHVELjHALMLsEom9 z$|2g!|GZ2@DdTebLmgXtOHp0>8p);w(JSElHWi(wq2ZP$>a-3jz`pMU7uJwzeDw3@ zhVMM6EaIr7*-ha-?TGkVVJ?94vAl*KEoJ1TG3fZNS*%h`L=b>bcL9_0992r3>($lZ z1&1Eo^ej1Tqx+(H`*!I`eE;eG zm$)uqfzjX5gxZBhR2_2_6B^TTZ(YUWv+FK7eGh}$hHYUxnrkHNd^Rgf4{k5=6l1?1 zHGow^DcUp;IcQ@m1=>*LGn7@E7jrxlp#TO2Pe2GgUPCq|yr!c=iPW~ieqpq!e>AbJ z$lGt2=}MxF&xh=*<(RZpzk)FO2DLG+V|aoRg1anIvqo?s%(|cEyah)lJuB693pOlL89*xz^Pl4UfQ_@;mUXMC4Ou3}_5rc8R{Ex^5~$M` z?qHZB64r}d&roZkrSFQhG7hs?pud9ozQENv*y#FOSct}JLkHK79k;PYn{$cM&AR1* z(LSjk)p#T+9qIsq@uK(rmyBQi6ML-`mu30=Y3tePc@yXD>+{`fWQx*+yFsc2Hm{qv z;;w_1A&9jqC%PBL8han{04AO%WoHZ>{~sIgbe+?Kq{rCifKX}WZAyfp5d)HG_H zN-d(JPP{(>&i670ODv)to@D!GvFcf>_NKKRAmLt%3ZD!=`Au-B z?`wQ&S+2;Ci_4Q*zBr3Q+^lO$LrUGHLo2)f@SB_4 zHl$9s1CAXfmb823kmj3>^|923Av8IV8vdUE7@`3F8)Q+3p#BM>pxyseyk})m{|SIZ z3DCbGffzs?4gpXP=ozpa$tf9#g8crfeqlV^*vlX$&!WuvJfJs{(1QS!P2BDY?*n}) z+1ZjfzfwR=nA>S~$5am8uCU-=j{%WVYsDGXk3$#<=2^M5P%Rl#Dv764sI&g*-u_TS zusJ}Bl$gIg)lvRkPg)VgJ`YLu=PzfY`B=1dF~@1oTcagzTtgFKba-AjAM{FTR^AN+MjfPn+= zqHqH{-8vX+rdf3mpkbE^%|E#6{oQaRIXHl`r-6{ms`3wVL{NJ4*Ja zm906YEsvgG+yi~s?rtaXUu{8k=r3{hO{o*y9ugfG2I`gB&lehxzk_DlhFz39ay`En zZiqz>_Xd=d@YNGh`e@{_GaOn>lMJTgljY}B#briD!pCNw)e*yvFx7M|lH`&*QlE#T z4rl(^^%cjE+3Cixa@KHa zTy;F*@ltG}va+{*UhG|3=v~FeKnv`PRymbM{kj0j9&{lC`ZgAzDs@rqh0#adtFB-b zUOT0D9?j{$7eH)-=i34pqRam>a^9{Jb?Ou znHf{5wHLLQUYfxN&%mE_XjfUf(kzNhQ?xO+@sEOhY`M%RmFD+_l1e|*I5HJ)fA8{3 z#D`nyz1@oEQk_3iU!DmTNRgq5IK2%(Y1sU1g|~TuTt7_LIsAUR`XRb;9S%g!nCbeK znNF519Tou0ChbB3Chhg~#OHhQ2tE5)A1XmU=qZoWorPxhjhivvl{l%6M)wyE%m&Lm zC4uWO4qixrd^EVhqKZ_ zm^Coef-R1*)Y#SS&2Qkf@7U8%eWF%$*TGDXd^IyjF7nmwPiCZK)i)tKI;iH4!DD#lpVfNO`VBZHR||1db3|#v1{C8?XloRHQ6B8kfKBOgh-v$1+<--yEVd)*3_S$tskcy#$kS;+6w3(jPJcez%> zmgAkaZyIcquEoU#9Q0!nhGpaCxO(vvENtbm+Zpe9`0=8QEeTD8T%@xr1>JXWt*_&U_%sz{Sb@BD|hE zaixv5L145}81K8U(nrnIB21lehu@rxqJKDeF?otg^8e09amXGa zI;ng|WV4mMOqn&$(;MUJMKP2s;E$KE!Nc7y0B&=M(O{=%-p!7SFWiv;EC zLQ4!sP>g#=G5ZQf;}8ivB4KoBer1CLOSP{s(IROLYrze2_|!tfT5v=Ngc=yS52No7 z(q8^N6v89t*gD-dU{QC#{T_=GNStnTHrpYltC`ndH^hyzpvYRK(mhNA8Vt2{qi>TO z51VG9k@@X06BXUxk57Kf>RfMjq?2_?Rl&qK#@qRx zSXQy#0($wbIO^(Xe&4qtP}p9nHYF% zNS7l(Um_ms$BmX}B;}U#D0v3V`J+akX9S*dzE)$cA5T=Q2fQuFpd!iO-syh8^W?{WoQ86Co)Zv$ znO-ZNFL;H8khi*D><0MdO}61__SCgGc`S2rRdw-#biuNM!*Tl;>eplQVsTLo!Ojp< z57b~QLoJ+!0C6v1I*j^iWAWLdq!q%zz;AcKi3c(A@S?onNW5=0qvIBC-dBgRS3*=U zJz-(>HK<8NxrZT{AKaIu!;qsQ@ss%G;hJDc0B|ES3WhLlE@+ghisD6Pk#QqRk?kYK zl(Lu|+0wB-W__lylomh9X`>ul1s;+uJXP}tiHtRD7w1-C<)JhsTJ@M>%Le5lxA4Co z^x3+(S3N6!t**wzIgr&v(m)T4PdsG2j3d~tMI5UK^9?h_^5f^u93krvqy+g69+6qhiUqdvWX*c{eIur9X-_bS%nV7wG-Cs%_-n!*@Mp2?dFyDm&C>K1``nCpQqV9D zeL^Xm@y!v7lh1^2mU=zq22FizSK+vNH2u)s@#Y$LuEQVFw8cNmBL4147LB{e*wuhCxh;rVOg8yawe4 z^>j=y-E{Mno|~CPih7pAJebI5B|Uj)g%W4g^keW&mAH!>X`2R9yH|=s%LWOMYqgH4 z!cog8aZ=se28X7jn+Gw0&~q{TJzTo;5iRqjy{_%nf!$?F{at=|YC*=IV~-=*bEM*^ zQMGA%NO4`HUwthVEZ9iLO!kbzemmboXd3-R=p3Hb_qrj@Xs_)@V=m7s@@Zt|!LP{3C-ObT3C}so;S#Y zmKf@sK--qr6owpK(AO=6u>Pl`q7C{KB3;VhXyAtd*1d=dbZG+1#%iy=IF_)vL0?RH zEIaku#}fF!Od-CCuxZ7V5x#`FT76bFw$!AM%uBjKF+X5W)=!qE!67@dZ<(>LtM@<% z^$O9a{-aT;I1nr_*liw6wY3!bTxyo4!YO4yF4OAy5!h!pXhI5VzeGa5{{STV3V|F$ zfoxL;OZdkqi>^;`TVi~$fJ8au14DGFBi9oaLC_yLLK$7a60ZvNmi4VBXbI}0{N52< zh3u+RR^XnK<%Fce3753AIAL-V?fdR{hUz&|+}9VJId%`9n5;Tv-{FnoiFPNodP8+g6|06`+LmW6_gaFqaN}Q=5k9JZfmZD{&@A*O*)>i;)*eV!BB5q-P>X z3+XH{JX22lrko&Aaw}grF8<3WREYs;`a>Ma=@x==P{kz~YmCT6*aSqDEde*5i zy))?i!tdV8{M6o)am624>xv6|M76hgAV>@w_k5)+a(&YHb{6+GyVhsEPkCwT&+3fY z9uX5@2bpmrA3C~ZKuV6H0@oq`9(UmUyQ-I$ucPp_w))WF=L`$rvPPkzXha*9AnyZ* z)VJ)Si1~yb`H&RDm9JhuJZp3Bd(k`IJr#0HzRClE3>Bqg3JXy+4W=Kll9Ev=qg2ml zLAzHhi~7yVVu1!-Rs829l)8f8;_nFduXE;knav^9bxLY0^D~PL5L}>Zeg+(y$`>4) zY4gV&@+4Dq8id~kf!h9DylFcSekpVBhkJfl-#+%C+yiQQyM9bkgBG(CLkyVeL>qk> zR3I9zCMGa;Vid%U=ZuC7WL(Q`+%uBuc_W04d)?l-72lvlaKF9jzU|&lQa#kVI5aXc z-JHyvq5C63HgPg>7S4WmH0R-VmP-E}PO-@$5h*dIL->wOacvt34Xb-&LDJdA5A1)+ zO^OB=939===j*rG9m~fdu!*J!AG|%WpP7B)w~0rZe3c%(dHbDgxo7#e+aB!h1k|V_ z#iJVn|E=n1Xt3NmvU}q$8@O)fF@j*7{+v_XL)BQ4+`p0pxzP~XEQ>_p$xytHPf97S z3Xd@}wfFWjqYj^!x=iLCBh5ZaHXHezbH-5AW*U7iV&hdQmfYE;j0!oFxm7 z^Gi3gV*%orE#KUFi^m=uC^%v-#$xHP5@6Fagm-CbCaYIsL^4(}k(390fhjeW1w5fmxhX!?&zW$0Q_Snh) zrf{$~#Wzp3R22WRNoS=@8APAd=4AMWjT&fTvwLxrbBCKZOoy|{p>QGv*#nI)XWcq) zT)ShZp$Rk2N6?UA>$@mQGW5KB<0L5|aGXCj$8vc#u9-skx6vW{hfF)g?(; zq!!tdiTmj_gxU{t0jAODbaEJRQeDKLm5M#ZwsQgg$04(*J9yeZyn;Z%WS?8sqaXy; z(l1NN8Z0n}AbBjrei;i^XO5caa+e2;R(7}TJ`sM4{n;#0EF;8SQgNxzrSu{V_06vp z%6c^;$g6)tnieGqDFCFs=k`Neyv(&zEwVccj6enp+d~aW{^;D1j7n~Rb&8!n%Ybj* zm8#?gef+iVEw7E%GzCYeWQ7v%My*jguf< zJWgkqww_TKQND6Np3VuT1dgPhP&paM@Fxt@d_*b8z7@M`W|V%7e5ig3-F}H)&4>U- zj%X;PUA&;uF=EAl!$VP0;Ra$%J%gR>%99+EtcR&B+_U0I(e`<*7#%)u=1}1~Zvx@^ zivD`?6r=&Z$)fLhnJQncr_NO*n6ZAN#i$WrZxfqb@=MM}ST2X3R+S=VBFDg~l?1pY zxQ>MeN!8M4g2*T2o0{*kkZ|0&nHP76h={zo5D6;Z`4jncN|}E&Iefry$*8C>7>%|X zoCmv7gX^$q1nrvy+a%a@yN!J#bY-BI*?JE7WKgfYg!;J^%`^oZYtp1jN#zIjtrvND z>};kB^DQGQyOBsrCJR}!EWCB+%|n`Pj(kME&lEIx&ZsymG#hY&bS3B72l=M@OjB?6 zbw}%2veJbcRv`53@>fkbPI#@+ z?x-A6`6I!4!z^sssx?GJ-Lhb#>N~2S*GlTCo{pWfxP~4Y=o<*91K(4Cw|uDtfA(i% zYAC-9T-`k{@KkFkFX5fLPTf_qdQ$Yt_0~y@v)2{y{s9u|L{1!FK?JQ44G_`<>Bo-y zsy>5Hdd3V0)`@GYrYttzgT@4Mb@J5OO*2~|AlrNNH=UpG4XGYluFZ$GPngdgo&9?4 zd@v;aadY$N!!@m6RCjZ?wcjZ#*i<&6wci{2x4pC?sbO!4nHHi>(lp}k8Qh}DH)T*J zb6s9_e%ksYU+<6QglW2?dIRb#o#X)DJ_K?1A5!`XT<_25aPf4d6i= zkM$%T0(~Otn~@9)BVns%N=C{7cfORpO$&Fs1OkTMG1U<@I2o<{H)wXm-*7?U(!_Vu zwoP^_j+^nHFDG7^TI1G;(WC&YI$;e_&O&|`3EE1M z)77$puP!t`eaa?S`zrW${S86VW1zx%KbPc!E!+QtI!D0LM-y|)4-;`ewU@Kra0hjU z4o0W+DR$5z96ganqtEe7by^o<;M~7PYS#OQPVA*$g~; z>E4W~Zf<%?%Be(u3Lm7aAbIl%UlJPw{M?%TdIcHSHm?bm^I z0Pu53?|I+P?KM+6(|d7~<3qsc)JvT_{0&;bWI{j?_Eb=K zJ~_b^n<|r85~J)KYVeF8aOZ1tyugfHm|S64_Kk9$>7fHcTJ}#(0g5Sf?*+nx>)?ix zuIY8e;)t9o`=^^|;=#B0X)D_|Cdeb}`di#=(O_Vy;ZK>4%hI*Gz*L`U1u19i5xy7i zqOMP%N+p8V!k_FrcJR-F6)$amZ+2NZV&iXSXK|`}@Ie6N^?jb@7M?`uDc#zQ2TWf~ zqwXd}yhe8Few{ywXJrKzhdBp@m>6IDOWz)EN+Rgi!KZI;tLn|?0LMhFAsf2SN>z!( zgGMDX0nYqiby-J{SmhsQAv0T5H1N(Eg~FmBLwMOZ$Tx^ADmn0+gD{w=J~D7&4aXnH z3`_wbw{Sy4Ht9L<`-97yl=)n?WNGM2{*(9xB3mc40ZZnqpBFv(DAtKfB@ z_93XYyW44iW8_3Rud1*k;xX5(#ZT?vzMm!c43EDUvbK!0_rY@726dZ0{7%WA$(Q`K znp(eIi<`1XEvG<_^=0=dY9Z9K#B0X#4_evqFT?Sbz zxwB_Y3iDWBP^tSh`bbm*HQ^EVa-0a=V$UtH${qiHb>#v5{tZ7P?Urnyf^{qgG*xe@ zQo)0D_cKqv=3R~{q)+d_OsLlWWWGb+HFxkq03#ArAWIjLtwyDo7;>lS)D+|NrW7_T zfa%cMN%6D$r4pUIs4oWDA=TAMfyRBOEoW-ONs>F~UdTFWQ;I85?=Ta*Nlf*4cqVD) z7C*@DS?}|6*rd6LgLk@0!1|7&=fNe2BQz*ukm2_sf}nRq6_K;TNuzffsIa$N$9h4yMX9yeIcWJ^TGay+e>MNj)(Kqz~zr%?U)_W4l@>5Bb&eqjm@tU>_ zebU8p>J#WOQPZ=K{co$1n`V8a^r(TKbdiOG!IPEb^efFIrk;=D_9Tv~gHG4a+v7FC z!Y?lV3M3b9MZ_0FUp^^o%?O|XJX#a?lf-pL9E-!oBG-6A$#{Op7n7#>6=mW(vFb#8 zTHVUd*epTpsvJF>7DJ)fvRfd4$9*zxaE)6bkSY4|)3!7u(6y4a1nZkcVgJ%~f1t0N z7HT32N&=PvQiPpPLpFY%^|{?6^*L@mV%mBxkPMS2&{#>`K5`3vwJAAxqC~XWPw|T} zzwq7tKmII~q{5aoOA0T_!v6YMrM>wibr|?rHPL?&K+EKFBKnDbZv>cRt%~&eCkKP@rmG3Hpu+<{GB^-LRZcz$FKcv$vC&UcF7GPpgvINhpt;gnQh^hzTB>{G=|j-z#?M?fw;bG6M|WVM;zAKsd>JJ95y7#TEb4|y7e^jdXpsRP zS%MP2TfUXY+pA8_MaJeJ85edx(=?n{Jyu_OEyS*b3(#30k}M(o@dY-Zc%wguXO z*GhgGNUkGgvc|}o)?2#3`zn(v+`S6C*(BVNRnAG;bi=(rEMbI~jps|iPvR8By;7sF ziMAY%0-ZhPBryhuJX;a1qEsIOj0JbNZ9ZrW$HUcgK1C4r+{eOvh|T+2U8}v*?y6)% zTyxbg>+{EawYl**DkD#IoF;aGXY5ZF$n7#WQ+dvROZiitW?8-R*1VxgyH?x^(_Zw| z$=oyBpH5ZOy^dfIW;Ux#igF~Q{`<)!t1ton>2Ar{>$Jwrwrdm{1T?VJir)B+t0-H1 zyo2{;ugt|`Tl8}|(YgllwaAY-Ek|$1UW9HUNwzRrf+-+AGj17ip0*ND-Quw6ptL_E zVcdvbosC{1QOQt+Ohf~%LuwR!K((=bCW=6pCJoFtyE zu))qXX5B;OV!_^1B>IvB_s7ZTZw*KihtezZFH0&C&>PuG*c&{l3px1)`huIM_yyJ4 zPEIqs($Dq5>f`hx1BrFo(3h-Cx5vjV>pDZGQFQ-F2$*zg~ zPBzCr4T!TB+CW#rW}D|jOMHr=s?4G?;z5aLF@x7!=C0J?90A>T$J0zoX!uB9&p)7G zWF1-CT#mp_GULQ2_uQ(FuSe|WVBIu~M}F$$9-wuNUsnXO{=mqx@1!O)mA_I0?r`Ba zp-(O{BZ2c${L z?aX)vuHp1a1-eZjkGVn-%d}U+O=y&9(xjrgtEUpjm9|1^C0i}**;QRJQJD^z&KF(rU*h^qQ`80E6`t3P-+O8AAf!-h z0&$~vN?uh7%81|p=LZ-EOyBYM-+RY@s{R8F*9_OZuYqz9Gj8;kxPRrC{UhPOpfX~A zgtMN992MZ3cf`B6cqTgFG0o@pa5a&{C$Z5Hn0T1Cu945h)sF_HulB>>aO)cF*Jcnu zlLZBSfY;ggDuw=aMP@Lsr|Rb6B_pQq&zlGta7oM@81t$2vf*s#XCLOKi2BYxw*rqi zNm#}vMyG&ebl)X>O3!NHC6*D70c%JUCf(m`Ss~YLnxLnKjPoxlo-3EOm>2Ln=00lM zpC;xy*=%Xm#o^Vur1e za>)=T&Maa6<7&kE*;7yWNnjuRnkyI*e!tDG&D@1pM%?F~#adF?qISTZ6p31y-vP=n&29c% z)L@NUy(wGaJ5s`i?C)IIj1I$x6jXw{a(&Y_6|GOYK9->%thEEdhLJU5rp(QV4Kr4G zDbE!o-eM>E0^WV``l@vuY$%dyZIKRBlrw-3B^1-)T*KDc>C%He*_8dl`h~gaVM@|yd0<^}G|L`aw4b4TeJHl_=SBO%N@$1L|16)4d!yefIha#c zs!yUF<|+fF6o%`tMY7|`P83cm)U+C9=!uv5{V>Wjq6R)Mrb+XzDy`NRJ6TOLx*knW zdTkv&9ogDeTHZ7u6Ch!jFP;i&?j5-alb&>%rSP2|a%@==;_m4RA64!6wMVG55hxV@ zO@{H{#U(4k z@y%`!@8boi8J(1+f_W+UQP5S8m^-|A_Wm^2fM=ZI?KPu2B%VrkDh`b7FQZQ}Cv!X8MkMEUaKJWGk^+S$#=z@)< zjpCM)pB!{G;+RkaB48)o6sSZE;5L2$63l$+PE>Hv$$~zifs> z>t4QaMxW<6Vgo~-(WK&@Ez;-IzRpL69K%%N7{O_XJifWJW>(CWX;kfT8Xy)Kx?p9%Xu9`~^U-QhF3%7xvmnjBbcw=?*MA zy4;Ocg-C{|KxB=-Lt#br!Kw$X2Yn%5bm`inoUL+@kLLy8O(0twb43W7^q^Ft7L!*E z4#`&|O5P8Pa^x{fSQ5x2O(#nrNGVa*2sG%S;uFK{#8`Fs+s{_mG*O9GELQ*?qB6fa z+`varUDeJ#n2A^CJN^LFs0*Ds|4P-1pThy!Ph{O3RsQHcsJPtirUxP94Ps#aS;YSJ zsoRf3<>c7T0N0N6k19O74`PS9kVARflI3cM%&sEef)rkJo2{)Df6VMitUQ#xVCkO< z<11g4GS(t>wAnco2)Z~pi9WMlD#wPH6j{TudQ1q;;L&PVs&2`2pE22H*oD}nylRjD zFaC1mgRpn~uO;6mK&p6Q9kG+r$D#|1oubFv<{MAuv zi!9}8JRLn^-owasfh9Bud$Meaa$L6po^HP|7Y%DUr=K-Q)yWph_detm=&?BA{`SH9 zGy*nE44?{G({^al(2}I zW3jHVWtI-hQCx%=Gu#9Fh2&ywy&dRxKrMwzfelnO&Qr4($FGx$*y@PtxVJLmsp90C zSj5%~wV-&Yq4Q8;;`$P`0#K0%#(rBLbdSS`<0w+uE&-4P1`@AA(Eq4)Nnd} zZy#TACp<6e{@GkmxTI1<6=lctNcE@aC@P7 zU*7bfPPrR2LCi>Yy`fn}&CCvU6{l7*2+r=m9)=E#n1Jt-N;KPfpN4E)%6sSZc>HLcc$kDs>2*BQCL zhJyWyHNf5MQijN;Zs~_rP4ee2?q3>O3;Y-f`;JqyL8pT)R0H~HNH2akvZSx8gC=5x-Fv*oZYVn zxPj;!ANP-wd%R|NKX&d1qLalu%*VD~@HgX8%73V%Zr@hO8D&uK71kZ@jbV){e)eLOW;GK^&cz z+Ct~~F0mi#DE!9Co+}M>80pFFy-E0>s(_4@z=u;VWT>_8s&MntL@?jd85ut#VvJ3i z`+OwdUo(h1mt;}(YNBci-$u`cr59_%0CpNe_3MBoo4D$5qpT$-1jD&v;C3w22#P;7 zxvWZ!lK>0wC_&$4!=9UkcSG=t{6dFKE6UkL`ijd*xk0jiOPv)cwM)9o>coUcC%7!a zJqgbtCLbp2{d;)6u=J~k)BTu;=>zQ#S#z4AU!$nV+TPZ+$2i$s7Lzm57Td74!%$%3J!^=$#4(Q5#Np^; z?>w#u2|7>|E1d@0tzOOVjbYZs6XJ#ol#SL#V`my!(w#$ux<2XBJGvP2sVnzuX&m40 z2f(x>n++}sB;Mq^uk}Y)xc5s17ZcDm`3rUy3&m)&RBhBY~i zR#L#*fRF8snagZ-6zP>c^II!xKSg2p=92zYQ->sy(9F^kovKXIM9sDY2iv2Yr*nUAA{7d(P11dByZQX@PT6jg_k$eKcBC> zs@%da;#VlTEh;7_LQ@XlEmYX=MZpo#v9*`$x7by6lS`Q$EB%O2x@|4fmpy(YO{q=P z;b;t81y8#`u4A0^sJO@DEuT+lq}g)^!JKw|$@e>aJ(6Y(V?E9eeHnDcx!NX`kvvNK zJaIDh%Cu^`#D?HH;ryCd-wr+|I{aj|@*`fj16mODYsmS=SZB=e*FPP|EFxKmPHMz} zL`K!YKyI8BSFVkN2p7$^uVfWG*n8VhXTNj;~%80}ofT2=)+AMZR zId9ev(7Ed11qIOpvD&euum`_85;oP)53zMPwhBU4yWC5FsyYcJhZ5!KvHXSyIOx!| zZsNnjiwoATS*#D?r7_iYpSmOiFZmub1%fIFfB z)eOHJQGQWM22n0yC*RPFb`#0HbVmi}HkJ#^$^bO**Bqmg8}z+jF0_7-WBj{+Ba*@P|Hal@2gUUS?ZQBCx8UyX1ozD=+)4RnXH=%zI z1gY2uX(ceN+0QH~?m4B!MKrEdITe7sjtJnfBSAWdQDboQj~BLg(5lABYu#vK__veXptamL59Z?C{v0zAuKeSIeYG;kHKs$?4U4O#F}XBU|-<@@pF$9nC(--qg!<%W%&8QweUV~=bQt<8crZJ$Zr4!36B*CF_^SM z>I{>6-&YiD>lVpY-KmZ`BX+>GOw~`5DdL#48_fS5LN`TkQD*IITvc5u21L7|iQIK( zq0Z-W#$ZaOueucLwJaJ5NN!tjITQhGBo*b?eL#o>+C9*Kil5$zj_oxNHGOjaGoSpv z9?LibZN-?)XO=;pm?~#;YfwN|d-DizQRl;Rf&z@3{K1ZEu76qJ4qxeZYEdXgp96_J zWQxXCew?t3)_hUji^B#!p-1VA6h)F){eBKQl4p%Juzk{{#)zoOZ&DePEuoleo`5Ve zrEkk~`{-u5_`XPnh^}eOx)dbNK|;MCMqbJU{RK*Z8desYxZ;p5Ix9vBEOs}N9yYiS z(P@K=&+4)4#=v6|f~s5WvG)SC1t`a`u+Fd0p%lD(7IYE#)9oqwV-a z`8{gI78K9|m=veAAQTCx(murD*JBEN zRZ?~QQj~*bfB$8W{!rz@rhl@+PNn$gxDf+XiH*jJi&|`G84{CvD#)M|#7=SOA+%ps z)d9MVDNa_8XS_-w6x_FHD*er{_v=r}yaJM&r#1w|VY^)3_if>*A*&P>cKYw7O=@(; zK~D8NI_y@3776;*9w;WIc|^ro2+}_lqi0TpKfnq*I?dv@594*C9wi+HxBDQB& zf{o{KQiw+rn_iOzl!}a+R@m4t?i}z#c`&xQiTQ?Yt?L7t5Pzxy;k+khRL~%&O+~X4 zh&C4Dr3BBfg!@z|rQC+Gz`v*@%vpe3ohOXl!ZE)aU-|rTss$T?%|?93Y@DLi&ZBkp zBV|V>oY~jg2vOngU^*I8JGBo+n+wt_Vpei^$6Rb zZ!6$yt6dn!SW+a+Jg!+vUlXSJa=$-;tWRm$8lU48)d218FwZ|w6QU18l#i=r@x5{5D2A?opzb}N56`igBXR;A$w5D%fcv5sA?%5b4LF+AL2Jn%-4_R>+y3lsNJ z;dt+8u-IsMaNQI4iJPQ(oE3%>%R3Yim4QW^+K@ty>S51phYcK9w6pp!*xL+bdsftm zyP9LQeyr=(rCc9MsDY4JRlhy@F4s3{SveqU4x0!XtPnldM*3y|LGWGjki+G}b72_K`xFj})0)j$KYrcTn5rMm?_ z-o;-x2Z8t-g{BzizM;PYxYtCLl690kr03Q|a?f&R9d^dDMo%Y}diF0oASAXR@Z=vV zYLB3;a%qAfAQFw}5|fhpU}Ghyp*N3_TeeK?6)b6P`H~wOSO07fzZWMR%U!kiBt0!P zOuo_XaK1gh7s2zhzNqpg0Tmc2D0X_$I7#Vg)s((|Ee6T(VPGTyDfYj9{rV!JtyE03 z){uD@fNk*}Gkba!Df4j)^aR%SHTjDx%&2<*6X)A6Y^d#!ivXqYagJx@+|aA_$A|3c zh`gGgIZvh%!H?pjb6P^~@TQ`|I|CzFaqN2XxWM>xq2 zRVx2hi1}Uqs{`^*TG57$QIH_*YNp06%J^HFeb!W6=KCzy*wYY!M&e}8TVKoPUL`j)D zr@mxlYOht3q^`7A_~~@W&M-Yz`n5$C znNXJ2W0WK`#{r{d-r6AD9W%zB%01JfXKFD4XB0Zbp`N_u(qEPGQ-?#BJ5~gYXN(T3 z3_*+0Sf5KQ0@p6-;S?2D;NGZ#iy@37mY?YaA)kol_W-4!EZHf9BH%Hs;OF4vf%^nV z3z`LwN9~-%jXG~2iQA3u95SEs+6hjU0YXUt4rAbmhJA4RkaN1yD-KoPQ}?|2GJRuB zKI^gr2h$hY8(cZ_Z<`cSAJ3taqUq*7IqYW5wGDNvfT6Tg&P6+wA*cb#TTg8`>(Yd}L^ z=-Mh>1zj50R*Y@qL&pa%!vEtpL&%F;>2d5d6jO|TyvmLQbvk`)5{r?vtA0*&-7yPb zZ7eaWti+8oS-tow)=mLY$!q&F$zR%8Vv`E$Cz)HyZJGU;rVNHhdjILO^S{fvw}z{Y*|`Q%iNy5OWFS1@mnYsJ`?LwVCA#qx4bIC-|fN8LpVCASxQe}qvB&4^b1h@J2UF` zZmYZQILt^MR=D)j+fP+$){R!jKGHbZRr@-oZKg>IF~ITgKN-j+yh`}QE@7Yd2qNP~ z5LV5|k%eWOhG;9(u9Nz-A1q-$s57%?Vi*W0UJz^d_aJhrigRY92^F-vuTH|fcWy!` zN3iftj{cf=(8pJbFXEHTCo=VMVO=6OwefPN{E6yCjW_RjxB@p@Yh?uZ(jg{MV z!}V;FxM&L>xUoB3_vhSpl71orHui1r?lUA|KwMP+t8tJ#%fZ3&!6H!hc&rbuUaV)BL>WXvj8b zo%3>vY7|EoShp_eG*9!1DjcseFLoZ*OqSnPKjDT1B6CuoopWEn4m}ZKeXRF*=6yM( z9faRAs}j<$I0;tNb>ih{gzCx)RDCmc!55@LgjU|B`K3SVYM>S{9R{sAr}kY?s7%zlp$7FjEuax^tBt|p6na8E@;u{I$-N&@Fdmc)o;774c@muyf|W??T@7#0yRV-lO4l7QG-^lpzix<7%>jIp-KqoY1z;v}vly&( zw0DsVu<8|*r%UUqLN3cTDK4EW6L3KtzZEdocR7Ok=l+aR?te@LmMU24Lk(Z#6iU-x z*aR&oDizehU+S0$fIdBczSNUDv!leC=SVjsx}4NO@PyZ#mO!A#-)`L^-Zi`e;Q8w@ zzfq$g4Yw0r5BHBufj~e_Gc6e6WZuLJ*seqW`wPiADMGd#5}~V2+Y;Rbi>g1J<$^UC zY*Y8hpu`1X&m+f!fn)wr);A_SifyS~_akrmzWa_CrUTiol9{X&ervY-P9T-WGnsLz zJT5Dpr}C+Yd-R5U_1lJrlAghZ-=cLlaLd7qnJx&pv)Q&#o?kEwwT~BQrvo@=Km3Gd zx-lT`16k|VuLCK?lBVg(mC&OUZO!y zdR~9h0_08p`u9}VzNc9g&ut@U;xlN7&hMEfEumS{Tn!3S^H!OGweAL%_`HWH^}HS~ z6*dPJS1Tbb%4fkkORoZnw~@FLCBG-)$b9z-jPQ(*ZK40!t@``9wR&0cDrq@=71r_} zd6k^`!T_f53G&F!XI@dt-4C6A8E2mO!*-BmAacr&LRDFFV55T}B<=D%xKVRprQ}z3 z(M{7s+E5H~Yj_6ZsZaAMcI}w!e&4Hni)iUteV$j|65Vtp?W}Q6n3cVmq{&T9-aT(z zt#Eb~P?8_mP(?aNa(T&|?A8>J`1ZzC4a>Q0?PH1ZNbF#@3Ufqf@MYe0HwrnV17;lp@v`VzW^f!<-S4Wfw%N zt}dJK$`7_6SHV(^5Ht5d(}k|lXASD)`#rP7uP!O!z;;jg8G0Ezg-&hoBPNr%UQ7w8Pp)%oiC)FHlm zpn(x{8mWi=gIb5d))rraL-~vqwuVizyZTrXpg1rG)6|Sf8kvM}lH32(>5I7c%g$6Q z9&jku*_r-ja846zUB=%VEDFRoL8y;2bYoFjVWqKWvTp2Otjm1s8ssz==_c(j_;#&i z8b#B3p!a@7mv5*vip0&tF=`v`lb5?xy%(P0zuC0aMUw;gaM_(!LsBste zBqwFY@g^`^$p5J81@!}$!Nb~dlKH-DwG2=qg=c<_o!`j+W#eX8E~~g^)T?Y7Zv1wG zOLCSk6G;3rYt&-BGV-GWWODC4|C1Wu(NK!-3 zx{&1M-j%n92&EOD((T*44|HhA!EL5ytNqP-95vk}>%#U~8Zb?2($8N!+G#_X!&CK+ z_%VqJFA@9K{obMv4t+6?FS8NyEY<7YXzTDB-#6SQFO!7xz{N(JhK^(uY9H8qwyUnqL12enJaL`w_ZxzQ%mDrdQ$rj zf5wZkl4Eo*1Mv9<-)^_=mEWIwr1^bG!meBphkIiOj53}4^0Ydm2aGuC;gj&Wt0F(J z|2tBOM7t&Qn#}IKIOygmC(^ZtZ~%F}MVv(pBRtojg+y)c5f+h~bx_Kalcs9}cpzQ; zNoebd?Y)~0IoNT5gLln4VF11~VqPI@oEdill%?(c*q()-`v)`3SiZ@yDpeF?x9lz^ z>VKeppc;+>YWtc5KE0r8q}j*GVCg($uCGw=Vm^2YE$3E zGMej#DP_Ej>Fo1IsI4KAO1jEW=D#Q?7Gf~1anUW7be28@E7f@#+_UN;e(SJ#e&UZACiV$F;;#I{J&SCe4>4r=;)V1=dW>3d z+U=sCEo5`nKG=8pqbyogxa&QD!8N}5m>Mxeev|Hsc3G|QVqJ^uo#F87KV&y}=5?G{ z20Nlq7OLjLJ^4>kfAV&pR4Do>QNM9(=iG=zsl8W5|VS=(n2#NP-!}+ zVy7bIDT{q^Yu+vwaj~s!G{*}QQ=Edd$rLpYu(F>rJOKR{m)?f2u>xg;y@ia<(vrk^ zCdUQOyPf;Jpgh{2p8S>k+aS|#Q#lV?21C=_PN32ZMCyrx&vTxhjKeHW9oqF4)i9}X zq}gst`jR={m=Jf*L6oW^J)wg9^2Jd5dn&y!35BJ+Xmu(elPbSys9#TkeFfL`S;Dme zeh|1)##2#D=*j3Sxgo5%>jr1(^t2z#)75yGNpO*?x<$@M-)L_{Y-Sov&MA8;WG1$9 zF;O3wL2p_XN0#qTP`D6bV9m02PO{##Tti}WR3k6^=O#Bz@~6Al3dYAW7R{hkl`2F; z$oXQYU_%z5feOiS1L~a8!szVr^VMVy7vET!tc4|&G7jzh)1m>eT9rWURpb3|#QbJS-b^MPF
KlZik_L);kpdxJh8gjD!DTje& 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 Ce+=UQ literal 0 HcmV?d00001 diff --git a/docs/images/pattern-library-screenshot.webp b/docs/images/pattern-library-screenshot.webp new file mode 100644 index 0000000000000000000000000000000000000000..dcdbc0a8d4848f55d747b03d0f432f60b4d2520b GIT binary patch literal 57682 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 VxDwdQ($d)gc%TG}KmY&$001{uJ5&Gw literal 0 HcmV?d00001 diff --git a/.github/pattern-library-talk-youtube.webp b/docs/images/pattern-library-talk-youtube.webp similarity index 100% rename from .github/pattern-library-talk-youtube.webp rename to docs/images/pattern-library-talk-youtube.webp diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..a6ee57a3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,31 @@ +# Django pattern library + +> UI pattern libraries for Django templates + +## Features + +This package automates the maintenance of UI pattern libraries or styleguides for Django projects, and allows developers to experiment with Django templates without having to create Django views and models. + +- Create reusable patterns by creating Django templates files as usual. +- All patterns automatically show up in the pattern library’s interface. +- Define data as YAML files for the templates to render with the relevant Django context. +- Override Django templates tags as needed to mock the template’s dependencies. +- Document your patterns with Markdown. + +Here is a screenshot of the pattern library in action: + +[![Screenshot of the pattern library UI, with navigation, pattern rendering, and configuration](images/pattern-library-screenshot.webp)](images/pattern-library-screenshot.webp) + +## Why this exists + +We want to make it possible for developers to maintain large pattern libraries with minimal fuss – no copy-pasting of templates between a static library and the “production” templates. + +There are a lot of alternative solutions for building pattern libraries, or to have [UI development playgrounds](https://www.componentdriven.org/). At [Torchbox](https://torchbox.com/) we mainly use Django and Wagtail, and we found it hard to maintain large libraries with those tools that have no awareness of Django Templates. +This is our attempt to solve this issue, bringing [Pattern Lab](http://patternlab.io/) to the Django world. + +To learn more about how this package can be used, have a look at our talk: + +[Reusable UI components: A journey from React to Wagtail](https://www.youtube.com/watch?v=isrOufI7TKc) + +[![Reusable UI components: A journey from React to Wagtail](images/pattern-library-talk-youtube.webp)](https://www.youtube.com/watch?v=isrOufI7TKc) + diff --git a/docs/reference/concepts.md b/docs/reference/concepts.md new file mode 100644 index 00000000..b7304c84 --- /dev/null +++ b/docs/reference/concepts.md @@ -0,0 +1,24 @@ +# Concepts + +To understand how `django-pattern-library` works, the following concepts are important. + +## Patterns + +Any template that is displayed by the pattern library is referred to as a pattern. Patterns are divided into two categories: fragments and pages. + +## Fragments + +A fragment is a pattern whose markup does not include all of the resources (typically CSS and Javascript) for it to be displayed correctly on its own. This is typical for reusable component templates which depend on global stylesheets or Javascript bundles to render and behave correctly. + +To enable them to be correctly displayed in the pattern library, `django-pattern-library` will inject the rendered markup of fragments into the **pattern base template** specified by `PATTERN_LIBRARY['PATTERN_BASE_TEMPLATE_NAME']`. + +This template should include references to any required static files. The rendered markup of fragments will be available in the `pattern_library_rendered_pattern` context variable (see the tests for [an example](https://github.com/torchbox/django-pattern-library/blob/master/tests/templates/patterns/base.html)). + +## Pages + +In contrast to fragments, pages are patterns that include everything they need to be displayed correctly in their markup. Pages are defined by `PATTERN_LIBRARY['BASE_TEMPLATE_NAMES']`. + +Any template in that list — or that extends a template in that list — is considered a page and will be displayed as-is when rendered in the pattern library. + +It is common practice for page templates to extend the pattern base template to avoid duplicate references to stylesheets and Javascript bundles. Again, [an example](https://github.com/torchbox/django-pattern-library/blob/master/tests/templates/patterns/base_page.html) of this can be seen in the tests. + diff --git a/docs/reference/examples.md b/docs/reference/examples.md new file mode 100644 index 00000000..254b24f5 --- /dev/null +++ b/docs/reference/examples.md @@ -0,0 +1,101 @@ +# Examples + +## Looping over a template tag + +```jinja2 +{% social_media_links as social_links %} + +``` + +```yaml +tags: + social_media_links: + as social_links: + raw: + - url: '#' + type: twitter + label: Twitter + - url: '#' + type: facebook + label: Facebook + - url: '#' + type: instagram + label: Instagram + - url: '#' + type: youtube + label: YouTube + - url: '#' + type: linkedin + label: LinkedIn +``` + +## Inclusion tags + +```html + +``` + +```yaml +tags: + footernav: + "": + template_name: "patterns/molecules/navigation/footernav.html" +``` + +## Image lazy load + +```html + {% image slide.image fill-100x71 as imageSmall %} + {% image slide.image fill-829x585 as imageLarge %} + + {% include "patterns/atoms/image/image--lazyload.html" with imageSmall=imageSmall width=829 height=585 imageLarge=imageLarge classList='slide__image' %} +``` + +```yaml +tags: + image: + slide.image fill-100x71 as imageSmall: + target_var: imageSmall + raw: + url: '//placekitten.com/100/71' + slide.image fill-829x585 as imageLarge: + target_var: imageLarge + raw: + url: '//placekitten.com/829/585' + width: '829' + height: '585' +``` + +## Image include + +```html +{{ imageLarge.alt }} +``` + +YAML: + +```yaml +context: + width: '829' + height: '585' + imageSmall: + url: '//placekitten.com/100/71' + imageLarge: + url: '//placekitten.com/829/585' +``` diff --git a/docs/reference/related-projects.md b/docs/reference/related-projects.md new file mode 100644 index 00000000..6a2ac43e --- /dev/null +++ b/docs/reference/related-projects.md @@ -0,0 +1,13 @@ +# Related projects + +Here are other projects that are similar to django-pattern-library, and may be relevant if you’re looking to go further, or wanting to try out alternatives: + +- [storybook-django](https://github.com/torchbox/storybook-django) – attempting to bridge the gap between React and Django, by bringing django-pattern-library patterns into Storybook stories. +- [django-reusable-components](https://github.com/EmilStenstrom/django-components/) – Reusable UI components for Django, going further than template partials. + +## Alternatives + +- [Storybook](https://storybook.js.org/), and in particular [Storybook for Server](https://github.com/storybookjs/storybook/tree/master/app/server) – Storybook integration with server-rendered UI components. +- [Pattern Lab](http://patternlab.io/) – PHP or Node pattern library, from which this project is heavily inspired. +- [Astrum](http://astrum.nodividestudio.com/) – Similar to Pattern Lab, Node based. +- [rikki-patterns](https://github.com/springload/rikki-patterns) – Experimental Django-friendly pattern library generator, for Jinja2 and Nunjucks templates diff --git a/mkdocs.yml b/mkdocs.yml index cad8f547..ba4fa822 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,6 +28,16 @@ plugins: - search - git-revision-date nav: - # Auto-magically copied from the root of the project in the CI build. - - 'Home': 'README.md' - - 'Overview': 'overview.md' + - 'Home': 'index.md' + - 'Getting started': 'getting-started.md' + # Ideally these should be in a "Guides" section, however it’s not currently possible to + # display those sections expanded by default. + # See https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/. + # Choose the order carefully from fundamental topics to advanced ones. + - 'Defining template context': 'guides/defining-template-context.md' + - 'Overriding template tags': 'guides/overriding-template-tags.md' + - 'Customizing template rendering': 'guides/customizing-template-rendering.md' + - 'Reference': + - 'Concepts': 'reference/concepts.md' + - 'Examples': 'reference/examples.md' + - 'Related projects': 'reference/related-projects.md' From 8c92ae502ad484bddfcc9578142e383e0d38265d Mon Sep 17 00:00:00 2001 From: Thibaud Colas Date: Thu, 29 Oct 2020 12:38:46 +0000 Subject: [PATCH 4/6] Improve syntax highlighting --- docs/README.md | 2 +- docs/getting-started.md | 6 +++--- docs/guides/customizing-template-rendering.md | 4 ++-- docs/reference/examples.md | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/README.md b/docs/README.md index e35875f7..aa456244 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@ # Documentation -This documentation is built as a website with [MkDocs](https://www.mkdocs.org/): [torchbox.github.io/django-pattern-library/](https://torchbox.github.io/django-pattern-library/). +This documentation is built as a website with [MkDocs](https://www.mkdocs.org/): [torchbox.github.io/django-pattern-library/](https://torchbox.github.io/django-pattern-library/). The documentation uses non-standard Markdown extensions, and will be best viewed on the website rather than GitHub. To build locally, view the project’s `CONTRIBUTING.md`. diff --git a/docs/getting-started.md b/docs/getting-started.md index 99772a24..f6220262 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -35,7 +35,7 @@ INSTALLED_APPS = [ Also add `pattern_library.loader_tags` to `OPTIONS["builtins"]` into the `TEMPLATES` setting: -```python +```python hl_lines="13 14 15" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", @@ -121,7 +121,7 @@ Now let’s look at adding our first template! Now we’ve done all of the configuration – let’s create a UI component. We’ll use `quote-block` as an example, and place it at `patterns/components/quote_block/quote_block.html` inside one of our Django apps: -```html +```jinja2

{{ quote }}

@@ -136,7 +136,7 @@ Now we’ve done all of the configuration – let’s create a UI component. We We additionally need to customize a base template, so the standalone component can be rendered within a page with CSS. This is what the `PATTERN_BASE_TEMPLATE_NAME` setting is for. As a separate template in `patterns/base.html`: -```html +```jinja2 hl_lines="11" diff --git a/docs/guides/customizing-template-rendering.md b/docs/guides/customizing-template-rendering.md index c82000cf..a8f30918 100644 --- a/docs/guides/customizing-template-rendering.md +++ b/docs/guides/customizing-template-rendering.md @@ -6,7 +6,7 @@ All patterns that are not pages are rendered within a base page template. The pa You can for example add a theme wrapper around the components: -```html +```jinja2 {% block content %} {% if pattern_library_rendered_pattern %}
@@ -18,6 +18,6 @@ You can for example add a theme wrapper around the components: `pattern_library_rendered_pattern` can also be used to do other modifications on the page for the pattern library only, for example adding an extra class to ``: -```html +```jinja2 ``` diff --git a/docs/reference/examples.md b/docs/reference/examples.md index 254b24f5..7726ef04 100644 --- a/docs/reference/examples.md +++ b/docs/reference/examples.md @@ -45,7 +45,7 @@ tags: ## Inclusion tags -```html +```jinja2 @@ -60,11 +60,11 @@ tags: ## Image lazy load -```html - {% image slide.image fill-100x71 as imageSmall %} - {% image slide.image fill-829x585 as imageLarge %} +```jinja2 +{% image slide.image fill-100x71 as imageSmall %} +{% image slide.image fill-829x585 as imageLarge %} - {% include "patterns/atoms/image/image--lazyload.html" with imageSmall=imageSmall width=829 height=585 imageLarge=imageLarge classList='slide__image' %} +{% include "patterns/atoms/image/image--lazyload.html" with imageSmall=imageSmall width=829 height=585 imageLarge=imageLarge classList='slide__image' %} ``` ```yaml @@ -84,7 +84,7 @@ tags: ## Image include -```html +```jinja2 {{ imageLarge.alt }} ``` From 9e24f6803ec21f51b241cff2e8ec003c8020c076 Mon Sep 17 00:00:00 2001 From: Thibaud Colas Date: Fri, 30 Oct 2020 08:19:34 +0000 Subject: [PATCH 5/6] Add more documentation of patterns known to work --- README.md | 13 +- docs/getting-started.md | 13 +- docs/guides/customizing-template-rendering.md | 27 +++- docs/guides/overriding-template-tags.md | 22 ++-- docs/guides/reuse-across-projects.md | 18 +++ docs/guides/workflows-that-work.md | 42 +++++++ docs/index.md | 14 ++- docs/recipes/image-include.md | 17 +++ docs/recipes/image-lazyload.md | 23 ++++ docs/recipes/inclusion-tags.md | 14 +++ docs/recipes/looping-for-tags.md | 45 +++++++ docs/recipes/multiple-variants.md | 62 +++++++++ docs/reference/api.md | 118 ++++++++++++++++++ docs/reference/examples.md | 101 --------------- docs/reference/known-issues.md | 105 ++++++++++++++++ docs/reference/related-projects.md | 14 +++ mkdocs.yml | 17 ++- 17 files changed, 539 insertions(+), 126 deletions(-) create mode 100644 docs/guides/reuse-across-projects.md create mode 100644 docs/guides/workflows-that-work.md create mode 100644 docs/recipes/image-include.md create mode 100644 docs/recipes/image-lazyload.md create mode 100644 docs/recipes/inclusion-tags.md create mode 100644 docs/recipes/looping-for-tags.md create mode 100644 docs/recipes/multiple-variants.md create mode 100644 docs/reference/api.md delete mode 100644 docs/reference/examples.md create mode 100644 docs/reference/known-issues.md diff --git a/README.md b/README.md index b874daeb..95ad70ed 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,15 @@ This package automates the maintenance of UI pattern libraries or styleguides fo - Override Django templates tags as needed to mock the template’s dependencies. - Document your patterns with Markdown. +## Why you need this + +Pattern libraries will change your workflow for the better: + +- They help separate concerns, both in code, and between members of a development team. +- If needed, they make it possible for UI development to happen before models and views are created. +- They encourage code reuse – defining independent UI components, that can be reused across apps, or ported to other projects. +- It makes it much simpler to test UI components – no need to figure out where they’re used across a site or app. + ## Documentation Documentation is available at [torchbox.github.io/django-pattern-library/](https://torchbox.github.io/django-pattern-library/), with source files in the `docs` directory. @@ -24,10 +33,6 @@ Documentation is available at [torchbox.github.io/django-pattern-library/](https - Guides - Reference -## Examples of usage - -At [Torchbox](https://torchbox.com/), we use this package for all of the Wagtail websites we build, for example [rca.ac.uk](https://github.com/torchbox/rca-wagtail-2019). - ## Contributing See anything you like in here? Anything missing? We welcome all support, whether on bug reports, feature requests, code, design, reviews, tests, documentation, and more. Please have a look at our [contribution guidelines](https://github.com/torchbox/django-pattern-library/blob/master/CONTRIBUTING.md). diff --git a/docs/getting-started.md b/docs/getting-started.md index f6220262..351093bc 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -58,7 +58,7 @@ TEMPLATES = [ ### Pattern library settings -Still in Django settings, set the `PATTERN_LIBRARY` setting. Here is an example showing the defaults: +Still in Django settings, set the [`PATTERN_LIBRARY`](./reference/api.md#pattern_library) setting. Here is an example showing the defaults: ```python PATTERN_LIBRARY = { @@ -83,7 +83,7 @@ PATTERN_LIBRARY = { } ``` -Note the templates in your `PATTERN_LIBRARY` settings must be available to [template loaders](https://docs.djangoproject.com/en/3.1/ref/templates/api/#loader-types). +Note the templates in your [`PATTERN_LIBRARY`](./reference/api.md#pattern_library) settings must be available to [template loaders](https://docs.djangoproject.com/en/3.1/ref/templates/api/#loader-types). ### URLs @@ -165,6 +165,15 @@ context: attribution: Haddaway ``` +We could also provide it with a custom name: + +```yaml +name: Quote Block +context: + quote: What is love? + attribution: Haddaway +``` + And that’s it! Our `quote_block` should finally appear in the pattern library, along with its rendering with this mock data. ![Screenshot of the quote_block template](images/getting-started/getting-started-complete.png) diff --git a/docs/guides/customizing-template-rendering.md b/docs/guides/customizing-template-rendering.md index a8f30918..7308779b 100644 --- a/docs/guides/customizing-template-rendering.md +++ b/docs/guides/customizing-template-rendering.md @@ -1,6 +1,6 @@ # Customizing template rendering -## Customizing the patterns’ surroundings +## Customizing all patterns’ surroundings All patterns that are not pages are rendered within a base page template. The pattern library will render patterns inside the `content` block, which you can tweak to change how patterns are displayed. @@ -21,3 +21,28 @@ You can for example add a theme wrapper around the components: ```jinja2 ``` + +## Customizing a single pattern’s rendering + +There is no API to customize a single pattern’s rendering, but it can be done by using pattern-library-only templates. For example, with our `quote_block.html` component: + +```django +
+
+

{{ quote }}

+ {% if attribution %} +

{{ attribution }}

+ {% endif %} +
+
+``` + +We could create another template next to it called `quote_block_example.html`, + +```django +
+ {% include "patterns/components/quote_block/quote_block.html" with attribution=attribution quote=quote %} +
+``` + +This is a fair amount of boilerplate, but neatly solves the problem per pattern. diff --git a/docs/guides/overriding-template-tags.md b/docs/guides/overriding-template-tags.md index 8a099ad9..3d609f45 100644 --- a/docs/guides/overriding-template-tags.md +++ b/docs/guides/overriding-template-tags.md @@ -2,19 +2,16 @@ The package overrides the following Django tags: -* `{% extends %}` -* `{% include %}` +- `{% extends %}` +- `{% include %}` -It's required to allow us to define fake template context -and override other template tags in `yaml` files. -This package uses custom behaviour for these tags only when -rendering pattern library and falls back to Django's standard behaviour -on all other cases. +It's required to allow us to define fake template context and override other template tags in YAML files. +This package uses custom behaviour for these tags only when rendering pattern library and falls back to Django's standard behaviour on all other cases. The override process has two parts: -1. Override your template tag with a mock implementation -2. Define fake result for your tag in a `yaml` file +1. Override your template tag with a mock implementation +2. Define fake result for your tag in a YAML file ### Providing a default value for template tags @@ -34,9 +31,10 @@ Currently this feature only supports providing a default for the output of the t Ideally your pattern library should be independent, so it doesn't fail when you run it with a project that has no entries in DB or on a local machine -without internet connection. This means that you need to override -a template tag when it hits DB or any other resource -(cache, or requests URL, for example). +without internet connection. +This means that you need to override a template tag when it hits DB or any other resource (cache, or requests URL, for example). + +You amy also need to override template tags in other cases, when data provided by the pattern library’s context mocking is of a different type to what Django would expect – this is because the pattern library only uses data types that are de-serializable from YAML. ### Override modes diff --git a/docs/guides/reuse-across-projects.md b/docs/guides/reuse-across-projects.md new file mode 100644 index 00000000..6a08b8e0 --- /dev/null +++ b/docs/guides/reuse-across-projects.md @@ -0,0 +1,18 @@ +# Reuse across projects + +django-pattern-library is designed to be useful for component reuse within a single project, but it can also be set up to create a component library reusable between multiple projects. Reusing pattern library components is a matter of packaging and publishing a Django app (that happens to contain a lot of templates, CSS, and JS). + +Here are the rough steps: + +- **Decide where to store the shared pattern library**. Whether it has its own repository, whether it’s published on PyPI, or in another way. +- **Choose a versioning and release methodology**. With multiple projects reusing the code, it’s important for them to be able to pin specific versions, and have a clear sense of how to do updates. +- **Provide a pattern library development environment**. Developers will need a way to iterate on pattern library components in isolation from the projects the UI components are reused in. + +## Static files + +As part of your pattern library’s build process, make sure that the static files (CSS, JS, etc.) of each component can be reused individually of each-other. Different projects likely will reuse different components, and you don’t want to be paying the performance cost of loading components you don’t need. + +## Useful resources + +- Django’s official [How to write reusable apps](https://docs.djangoproject.com/en/3.1/intro/reusable-apps/) +- InVision’s [Guide to Design Systems](https://www.invisionapp.com/inside-design/guide-to-design-systems/) diff --git a/docs/guides/workflows-that-work.md b/docs/guides/workflows-that-work.md new file mode 100644 index 00000000..9b653620 --- /dev/null +++ b/docs/guides/workflows-that-work.md @@ -0,0 +1,42 @@ +# Workflows that work + +The workflow of developing UI components in a pattern library can be quite different from one-off templates that are only rendered where they are used. Here are tips to make the most of it. + +## Keep the pattern library in sync + +One of the upsides of having the pattern library built with Django is that the HTML templates can never go out of sync – but the data can! Make sure your template context and tag overrides keep in sync with your actual templates. This can for example be part of a code review checklist. + +## Document your patterns + +Patterns support defining a custom `name` in YAML, as well as rendering fully-fledged documentation in Markdown. Create a file next to the template to document it: + +```markdown +This template can be used in different places. In streamfield block +or directly in a page template. To use this template pass `call_to_action` into context. + +Example: + +{% include "patterns/molecules/cta/call_to_action.html" with call_to_action=call_to_action %} +``` + +## Back-end first + +Traditionally, Django development starts from models and everything else is derived from it. This is very natural from a back-end perspective – first define your data model, then the view(s) that reuse it, and finally templates. + +We generally recommend this approach, but keep in mind that: + +- With this workflow it’s natural to write templates that are heavily tied to the database structure, and as such not very reusable, and may be out of touch with visual design (which generally uses basic data structures like lists and mappings) +- There will be work to do to reconcile the data structure as defined by the back-end, with what is mandated by the designs. + +To mitigate this effort, and overall make templates more reusable, take the time to massage data into simple structures that map better to visual representations. + +## Front-end first + +Alternatively, the pattern library makes it possible to write templates without models and views. This can be very convenient if your project’s schedule requires this kind of progression. + +With this approach, keep in mind that: + +- When creating the template from UI principles, there will be assumptions made about the underlying data structures to be provided by Django. Templates will be heavily tied to their visual design (which generally uses basic data structures like lists and mappings), and may be out of touch with the models once they are created. +- There will be work to do to reconcile the data structure as defined in the UI components, with what is mandated by the models. + +To mitigate this effort, and overall make templates more reusable, take the time to massage data into simple structures that map better to visual representations. diff --git a/docs/index.md b/docs/index.md index a6ee57a3..a732f1c5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,12 +16,22 @@ Here is a screenshot of the pattern library in action: [![Screenshot of the pattern library UI, with navigation, pattern rendering, and configuration](images/pattern-library-screenshot.webp)](images/pattern-library-screenshot.webp) +## Why you need this + +Pattern libraries will change your workflow for the better: + +- They help separate concerns, both in code, and between members of a development team. +- If needed, they make it possible for UI development to happen before models and views are created. +- They encourage code reuse – defining independent UI components, that can be reused across apps, or ported to other projects. +- It makes it much simpler to test UI components – no need to figure out where they’re used across a site or app. + ## Why this exists We want to make it possible for developers to maintain large pattern libraries with minimal fuss – no copy-pasting of templates between a static library and the “production” templates. -There are a lot of alternative solutions for building pattern libraries, or to have [UI development playgrounds](https://www.componentdriven.org/). At [Torchbox](https://torchbox.com/) we mainly use Django and Wagtail, and we found it hard to maintain large libraries with those tools that have no awareness of Django Templates. -This is our attempt to solve this issue, bringing [Pattern Lab](http://patternlab.io/) to the Django world. +There are a lot of alternative solutions for building pattern libraries, or to have [UI development playgrounds](https://www.componentdriven.org/). +At [Torchbox](https://torchbox.com/) we mainly use Django and Wagtail, and we found it hard to maintain large libraries with those tools that have no awareness of Django Templates. +This is our attempt to solve this issue – [Pattern Lab](http://patternlab.io/) goes Django! To learn more about how this package can be used, have a look at our talk: diff --git a/docs/recipes/image-include.md b/docs/recipes/image-include.md new file mode 100644 index 00000000..e498f5bc --- /dev/null +++ b/docs/recipes/image-include.md @@ -0,0 +1,17 @@ +## Image include + +```jinja2 +{{ imageLarge.alt }} +``` + +YAML: + +```yaml +context: + width: '720' + height: '400' + imageSmall: + url: https://source.unsplash.com/pZ-XFIrJMtE/360x200 + imageLarge: + url: https://source.unsplash.com/pZ-XFIrJMtE/720x400 +``` diff --git a/docs/recipes/image-lazyload.md b/docs/recipes/image-lazyload.md new file mode 100644 index 00000000..f1260d9b --- /dev/null +++ b/docs/recipes/image-lazyload.md @@ -0,0 +1,23 @@ +## Image lazy load + +```jinja2 +{% image slide.image fill-100x71 as imageSmall %} +{% image slide.image fill-829x585 as imageLarge %} + +{% include "patterns/atoms/image/image--lazyload.html" with imageSmall=imageSmall width=829 height=585 imageLarge=imageLarge classList='slide__image' %} +``` + +```yaml +tags: + image: + slide.image fill-100x71 as imageSmall: + target_var: imageSmall + raw: + url: '//placekitten.com/100/71' + slide.image fill-829x585 as imageLarge: + target_var: imageLarge + raw: + url: '//placekitten.com/829/585' + width: '829' + height: '585' +``` diff --git a/docs/recipes/inclusion-tags.md b/docs/recipes/inclusion-tags.md new file mode 100644 index 00000000..ff885187 --- /dev/null +++ b/docs/recipes/inclusion-tags.md @@ -0,0 +1,14 @@ +## Inclusion tags + +```jinja2 + +``` + +```yaml +tags: + footernav: + "": + template_name: "patterns/molecules/navigation/footernav.html" +``` diff --git a/docs/recipes/looping-for-tags.md b/docs/recipes/looping-for-tags.md new file mode 100644 index 00000000..8407d548 --- /dev/null +++ b/docs/recipes/looping-for-tags.md @@ -0,0 +1,45 @@ +## Looping over a template tag + +For a template such as: + +```jinja2 +{% social_media_links as social_links %} + +``` + +You can use the following syntax to mock the tag’s output: + +```yaml +tags: + social_media_links: + as social_links: + raw: + - url: '#' + type: twitter + label: Twitter + - url: '#' + type: facebook + label: Facebook + - url: '#' + type: instagram + label: Instagram + - url: '#' + type: youtube + label: YouTube + - url: '#' + type: linkedin + label: LinkedIn +``` diff --git a/docs/recipes/multiple-variants.md b/docs/recipes/multiple-variants.md new file mode 100644 index 00000000..0ed01e55 --- /dev/null +++ b/docs/recipes/multiple-variants.md @@ -0,0 +1,62 @@ +# Multiple template variants + +See [#87](https://github.com/torchbox/django-pattern-library/issues/87). There is currently no support for trying out a single component with different variations in context or tag overrides, but this can worked around by creating pattern-library-only templates. + +For example, for this `call_to_action` template: + +```django +
+ +

{{ call_to_action.title }}

+ {% include "patterns/atoms/link/link.html" with type="primary" classes="call-to-action__link" href=call_to_action.get_link_url text=call_to_action.get_link_text %} +
+``` + +We can try it out once with the following YAML: + +```yaml +context: + call_to_action: + title: Will you help us protect these magnificant creatures in the UK waters? + illustration: + url: /static/images/illustrations/sharks.svg + get_link_text: Sign up for our appeal + get_link_url: '#' +``` + +If we want to try multiple variants, simply create a custom template for pattern library usage only, that renders `call_to_action` multiple times: + +```django +
+

Call to action

+ {% for call_to_action in ctas %} +
+

{{ call_to_action.type }}

+ {% include "patterns/molecules/cta/call_to_action.html" with call_to_action=call_to_action %} +
+ {% endfor %} +
+``` + +```yaml +context: + ctas: + - type: Call to action + title: Will you help us protect these magnificant creatures in the UK waters? + illustration: + url: /static/images/illustrations/sharks.svg + get_link_text: Sign up for our appeal + get_link_url: '#' + - type: Call to action with short title + title: Will you help us? + illustration: + url: /static/images/illustrations/sharks.svg + get_link_text: Sign up for our appeal + get_link_url: '#' + - type: Call to action with long title + title: Will you help us protect these magnificant and learn how to make environmentally responsible choices when buying seafood? + illustration: + url: /static/images/illustrations/sharks.svg + get_link_text: Sign up for our appeal + get_link_url: '#' +``` diff --git a/docs/reference/api.md b/docs/reference/api.md new file mode 100644 index 00000000..b6254d25 --- /dev/null +++ b/docs/reference/api.md @@ -0,0 +1,118 @@ +# API and settings + +## YAML structure + +YAML isn’t everyone’s favorite markup language, but it has the advantage of being very lean, mapping well to both JSON and Python data structures, and supporting comments. + +Here is what you need to know: + +- Always use `.yaml` for patterns configuration. +- Use Mappings in place of Python Dictionaries. +- Use Sequences in place of Python lists (or iterables like QuerySets). +- The pattern library uses [PyYAML](https://pyyaml.org/wiki/PyYAMLDocumentation) in particular + +Here is an example in practice: + +```yaml +name: Test +# Nested structure, dependent on template. +context: + quote: What is love? + attribution: Haddaway + my_list: + - 1 + - 2 + - 3 +# Mapping from tag names to tag overrides. +tags: + error_tag: + include: + template_name: 'non-patterns/include.html' +``` + +## Templates + +### `pattern_library_rendered_pattern` + +`pattern_library_rendered_pattern` is required in the template defined as [`PATTERN_BASE_TEMPLATE_NAME`](#pattern_base_template_name). This gets replaced by the rendered pattern’s HTML when displaying patterns in the library. + +```django + + {% block content %}{{ pattern_library_rendered_pattern }}{% endblock %} + +``` + +## Settings + +See [Getting started](../getting-started.md) for more guided information. + +### `PATTERN_LIBRARY` + +All settings should be set as keys of the `PATTERN_LIBRARY` object. + +```python +PATTERN_LIBRARY = { + # […] +} +``` + +### `SECTIONS` + +`SECTIONS` controls the groups of templates that appear in the navigation. +The keys are the group titles and the values are lists of template name prefixes that will be searched to populate the groups. + +You can use this to create basic two-folder "includes and pages" hierarchies: + +```python +PATTERN_LIBRARY = { + "SECTIONS": ( + ("components", ["patterns/components"]), + ("pages", ["patterns/pages"]), + ), +} +``` + +Or more detailed structures following [Atomic Design](https://atomicdesign.bradfrost.com/): + +```python +PATTERN_LIBRARY = { + "SECTIONS": ( + ("atoms", ["patterns/atoms"]), + ("molecules", ["patterns/molecules"]), + ("organisms", ["patterns/organisms"]), + ("templates", ["patterns/templates"]), + ("pages", ["patterns/pages"]), + ), +} +``` + +### `TEMPLATE_SUFFIX` + +Defaults to `.html`. Only set this if your templates use another file extension. + +```python +PATTERN_LIBRARY = { + "TEMPLATE_SUFFIX": ".dj", +} +``` + +### `PATTERN_BASE_TEMPLATE_NAME` + +`PATTERN_BASE_TEMPLATE_NAME` is the template that fragments will be wrapped with. +It should include any required CSS and JS, and output [`pattern_library_rendered_pattern`](#pattern_library_rendered_pattern) from context. + +```python +PATTERN_LIBRARY = { + "PATTERN_BASE_TEMPLATE_NAME": "patterns/base.html", +} +``` + +### `BASE_TEMPLATE_NAMES` + +Any template in `BASE_TEMPLATE_NAMES` or any template that extends a template in `BASE_TEMPLATE_NAMES` is a "page" and will be rendered as-is without being wrapped. + +```python +PATTERN_LIBRARY = { + "BASE_TEMPLATE_NAMES": ["patterns/base_page.html"], +} +``` diff --git a/docs/reference/examples.md b/docs/reference/examples.md deleted file mode 100644 index 7726ef04..00000000 --- a/docs/reference/examples.md +++ /dev/null @@ -1,101 +0,0 @@ -# Examples - -## Looping over a template tag - -```jinja2 -{% social_media_links as social_links %} - -``` - -```yaml -tags: - social_media_links: - as social_links: - raw: - - url: '#' - type: twitter - label: Twitter - - url: '#' - type: facebook - label: Facebook - - url: '#' - type: instagram - label: Instagram - - url: '#' - type: youtube - label: YouTube - - url: '#' - type: linkedin - label: LinkedIn -``` - -## Inclusion tags - -```jinja2 - -``` - -```yaml -tags: - footernav: - "": - template_name: "patterns/molecules/navigation/footernav.html" -``` - -## Image lazy load - -```jinja2 -{% image slide.image fill-100x71 as imageSmall %} -{% image slide.image fill-829x585 as imageLarge %} - -{% include "patterns/atoms/image/image--lazyload.html" with imageSmall=imageSmall width=829 height=585 imageLarge=imageLarge classList='slide__image' %} -``` - -```yaml -tags: - image: - slide.image fill-100x71 as imageSmall: - target_var: imageSmall - raw: - url: '//placekitten.com/100/71' - slide.image fill-829x585 as imageLarge: - target_var: imageLarge - raw: - url: '//placekitten.com/829/585' - width: '829' - height: '585' -``` - -## Image include - -```jinja2 -{{ imageLarge.alt }} -``` - -YAML: - -```yaml -context: - width: '829' - height: '585' - imageSmall: - url: '//placekitten.com/100/71' - imageLarge: - url: '//placekitten.com/829/585' -``` diff --git a/docs/reference/known-issues.md b/docs/reference/known-issues.md new file mode 100644 index 00000000..ca34c41a --- /dev/null +++ b/docs/reference/known-issues.md @@ -0,0 +1,105 @@ +# Known issues + +django-pattern-library has a few known limitations due to its design, which are worth knowing about when authoring templates or attempting to document them in the pattern library. + +## No way to specify objects that have attributes and support iteration + +See [#10](https://github.com/torchbox/django-pattern-library/issues/10). It’s impossible to mock the context when a variable needs to support iteration _and_ attributes. Here is an example of this impossible case: + +```django +{% for result in search_results %} +{# […] #} +{% if search_results.paginator.count %} +``` + +## Overriding filters is not supported + +See [#114](https://github.com/torchbox/django-pattern-library/issues/114). PRs welcome! + +## Django form fields are not well supported + +See [#113](https://github.com/torchbox/django-pattern-library/issues/113). If a template contains `{% for field in form %}` or even `{% if form %}`, then it's easy enough to render in django-pattern-library so long as we force the form to be null in the YAML context, and are happy not to have the form. + +If the form is rendered explicitly by field names, then it requires a lot more work, which can quickly become too much of a maintenance burden – for example creating deeply nested structures for form fields: + +```yaml + form: + email: + bound_field: + field: + widget: + class: + __name__: char_field +``` + +While this is in theory possible, it’s not a very desirable prospect. + +## Can’t override context in a child template + +See [#8](https://github.com/torchbox/django-pattern-library/issues/8). + +If you have a `some_page.html`, `some_page.yaml`, and `include_me.html`, `include_me.html`, and `some_page.html` includes `include_me.html`. + +`some_page.yaml` with something like: + +```yaml +context: + page: + pk: 1 + title: "my title" +``` + +and `include_me.yaml` with something like: + +```yaml +context: + page: + title: "Title from include" +``` + +`Title from include` will appear on both patterns. It's impossible to override single key in `some_page.html` + +## No support for pattern variations + +See [#87](https://github.com/torchbox/django-pattern-library/issues/87). There is currently no support for trying out a single component with different variations in context or tag overrides. + +This can be worked around by creating pattern-library-only templates, see [Multiple template variants](../recipes/multiple-variants.md) + +## Can’t mock each use of a template tag with different attributes + +For example, with a template that uses the same tag many times like: + +```django +{% load wagtailcore_tags %} + {% for link in primarynav %} + {% with children=link.value.page.get_children.live.public.in_menu %} +
+ {% include_block link with has_children=children.exists nav_type="primary-nav" %} + +
+ {% endwith %} + {% endfor %} +``` + +This can’t be mocked for all usage of `include_block`. + +## Can’t mock objects comparison by reference + +With instances of models, the following works fine in vanilla Django, due to `item` and `page` being the same object: + +```django +{% if item == page %} +``` + +This can’t be mocked with the pattern library’s context mocking support. As a workaround, you can switch equality checks to using literals: + +```django +{% if item.id == page.id %} +``` diff --git a/docs/reference/related-projects.md b/docs/reference/related-projects.md index 6a2ac43e..23913288 100644 --- a/docs/reference/related-projects.md +++ b/docs/reference/related-projects.md @@ -11,3 +11,17 @@ Here are other projects that are similar to django-pattern-library, and may be r - [Pattern Lab](http://patternlab.io/) – PHP or Node pattern library, from which this project is heavily inspired. - [Astrum](http://astrum.nodividestudio.com/) – Similar to Pattern Lab, Node based. - [rikki-patterns](https://github.com/springload/rikki-patterns) – Experimental Django-friendly pattern library generator, for Jinja2 and Nunjucks templates + +## Pattern libraries based on Django + +Here are open-source projects that maintain pattern libraries for Django. + +With `django-pattern-library`: + +- [rca.ac.uk](https://github.com/torchbox/rca-wagtail-2019) +- [Department for Transport: Road Freight Survey - Alpha project](https://github.com/torchbox/dft-rfs-alpha) + +Without `django-pattern-library`: + +- [Wagtail NHS.UK frontend](https://github.com/nhsuk/wagtail-nhsuk-frontend) +- [consumerfinance.gov](https://github.com/cfpb/consumerfinance.gov) diff --git a/mkdocs.yml b/mkdocs.yml index ba4fa822..e5abeda5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -34,10 +34,19 @@ nav: # display those sections expanded by default. # See https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/. # Choose the order carefully from fundamental topics to advanced ones. - - 'Defining template context': 'guides/defining-template-context.md' - - 'Overriding template tags': 'guides/overriding-template-tags.md' - - 'Customizing template rendering': 'guides/customizing-template-rendering.md' + - 'Template context': 'guides/defining-template-context.md' + - 'Overriding tags': 'guides/overriding-template-tags.md' + - 'Customizing rendering': 'guides/customizing-template-rendering.md' + - 'Workflows that work': 'guides/workflows-that-work.md' + - 'Reuse across projects': 'guides/reuse-across-projects.md' + - 'Recipes': + - 'Inclusion tags': 'recipes/inclusion-tags.md' + - 'Looping for tags': 'recipes/looping-for-tags.md' + - 'Image include': 'recipes/image-include.md' + - 'Image lazyload': 'recipes/image-lazyload.md' + - 'Multiple variants': 'recipes/multiple-variants.md' - 'Reference': + - 'API and settings': 'reference/api.md' - 'Concepts': 'reference/concepts.md' - - 'Examples': 'reference/examples.md' + - 'Known issues': 'reference/known-issues.md' - 'Related projects': 'reference/related-projects.md' From a39b8c9c99ec2fb1f5a885c8a633733fea46ed89 Mon Sep 17 00:00:00 2001 From: Thibaud Colas Date: Fri, 30 Oct 2020 08:23:17 +0000 Subject: [PATCH 6/6] Add README links --- README.md | 6 ++++++ docs/guides/overriding-template-tags.md | 13 +++++++------ docs/reference/known-issues.md | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 95ad70ed..2f7b7b8d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,13 @@ Documentation is available at [torchbox.github.io/django-pattern-library/](https - [Getting started](https://torchbox.github.io/django-pattern-library/getting-started/) - Guides + - [Defining template context](https://torchbox.github.io/django-pattern-library/guides/defining-template-context/) + - [Overriding template tags](https://torchbox.github.io/django-pattern-library/guides/overriding-template-tags/) + - [Customizing template rendering](https://torchbox.github.io/django-pattern-library/guides/customizing-template-rendering/) + - [Workflows that work](https://torchbox.github.io/django-pattern-library/guides/workflows-that-work/) - Reference + - [API & settings](https://torchbox.github.io/django-pattern-library/reference/api/) + - [Known issues and limitations](https://torchbox.github.io/django-pattern-library/reference/known-issues/) ## Contributing diff --git a/docs/guides/overriding-template-tags.md b/docs/guides/overriding-template-tags.md index 3d609f45..f0e9b3a0 100644 --- a/docs/guides/overriding-template-tags.md +++ b/docs/guides/overriding-template-tags.md @@ -13,20 +13,22 @@ The override process has two parts: 1. Override your template tag with a mock implementation 2. Define fake result for your tag in a YAML file +## Providing a default value for template tags -### Providing a default value for template tags To provide a default for a template tag, you need to provide a keyword argument default_html when overriding your tag. -``` +```python from pattern_library.monkey_utils import override_tag -override_tag(register, 'a_tag_name', default_html="https://potato.com") +override_tag(register, 'a_tag_name', default_html="https://example.com/") ``` This default is used for any tag that's not passed its own context, allowing specificity for those elements that need it while preventing the tag from breaking when it's not structural to the component. -#### Limitation -Currently this feature only supports providing a default for the output of the tag, this does not support modifying context in templates such as {% an_example_tag page.url as example_variable %}. +### Limitation + +Currently this feature only supports providing a default for the output of the tag, this does not support modifying context in templates such as `{% an_example_tag page.url as example_variable %}`. + ### When do I need to override a template tag? Ideally your pattern library should be independent, so it doesn't fail when @@ -252,4 +254,3 @@ tags: ``` Note the `target_var` field. - diff --git a/docs/reference/known-issues.md b/docs/reference/known-issues.md index ca34c41a..25344550 100644 --- a/docs/reference/known-issues.md +++ b/docs/reference/known-issues.md @@ -1,4 +1,4 @@ -# Known issues +# Known issues and limitations django-pattern-library has a few known limitations due to its design, which are worth knowing about when authoring templates or attempting to document them in the pattern library.