dj-design-system 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dj_design_system-0.1.0/.gitignore +52 -0
- dj_design_system-0.1.0/CHANGELOG.md +23 -0
- dj_design_system-0.1.0/CONTRIBUTING.md +49 -0
- dj_design_system-0.1.0/LICENSE +21 -0
- dj_design_system-0.1.0/PKG-INFO +118 -0
- dj_design_system-0.1.0/README.md +72 -0
- dj_design_system-0.1.0/dj_design_system/__init__.py +1 -0
- dj_design_system-0.1.0/dj_design_system/apps.py +13 -0
- dj_design_system-0.1.0/dj_design_system/components.py +293 -0
- dj_design_system-0.1.0/dj_design_system/data.py +256 -0
- dj_design_system-0.1.0/dj_design_system/finders.py +89 -0
- dj_design_system-0.1.0/dj_design_system/forms.py +81 -0
- dj_design_system-0.1.0/dj_design_system/migrations/0001_initial.py +33 -0
- dj_design_system-0.1.0/dj_design_system/migrations/__init__.py +0 -0
- dj_design_system-0.1.0/dj_design_system/models.py +12 -0
- dj_design_system-0.1.0/dj_design_system/parameters/__init__.py +20 -0
- dj_design_system-0.1.0/dj_design_system/parameters/base.py +151 -0
- dj_design_system-0.1.0/dj_design_system/parameters/field.py +28 -0
- dj_design_system-0.1.0/dj_design_system/parameters/model.py +203 -0
- dj_design_system-0.1.0/dj_design_system/parameters/user.py +16 -0
- dj_design_system-0.1.0/dj_design_system/services/__init__.py +0 -0
- dj_design_system-0.1.0/dj_design_system/services/canvas.py +234 -0
- dj_design_system-0.1.0/dj_design_system/services/component.py +69 -0
- dj_design_system-0.1.0/dj_design_system/services/markdown_canvas.py +306 -0
- dj_design_system-0.1.0/dj_design_system/services/media.py +100 -0
- dj_design_system-0.1.0/dj_design_system/services/navigation.py +533 -0
- dj_design_system-0.1.0/dj_design_system/services/registry.py +259 -0
- dj_design_system-0.1.0/dj_design_system/services/tag_signature.py +497 -0
- dj_design_system-0.1.0/dj_design_system/settings.py +113 -0
- dj_design_system-0.1.0/dj_design_system/static/django_design_system/canvas.css +36 -0
- dj_design_system-0.1.0/dj_design_system/static/django_design_system/gallery-highlight.css +229 -0
- dj_design_system-0.1.0/dj_design_system/static/django_design_system/gallery-markdown.css +108 -0
- dj_design_system-0.1.0/dj_design_system/static/django_design_system/gallery-measure.js +185 -0
- dj_design_system-0.1.0/dj_design_system/static/django_design_system/gallery-search.js +284 -0
- dj_design_system-0.1.0/dj_design_system/static/django_design_system/gallery-toolbar.css +179 -0
- dj_design_system-0.1.0/dj_design_system/static/django_design_system/gallery-toolbar.js +610 -0
- dj_design_system-0.1.0/dj_design_system/static/django_design_system/gallery.css +1416 -0
- dj_design_system-0.1.0/dj_design_system/static/django_design_system/htmx.min.js +1 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/canvas/iframe.html +43 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/gallery/base.html +83 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/gallery/breadcrumb.html +53 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/gallery/component.html +232 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/gallery/documentation.html +22 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/gallery/folder.html +47 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/gallery/index.html +16 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/gallery/navtree.html +48 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/gallery/sandbox_fragment.html +47 -0
- dj_design_system-0.1.0/dj_design_system/templates/dj_design_system/gallery/toolbar.html +170 -0
- dj_design_system-0.1.0/dj_design_system/templatetags/__init__.py +0 -0
- dj_design_system-0.1.0/dj_design_system/templatetags/design_components.py +82 -0
- dj_design_system-0.1.0/dj_design_system/templatetags/dj_design_system_gallery.py +160 -0
- dj_design_system-0.1.0/dj_design_system/types.py +26 -0
- dj_design_system-0.1.0/dj_design_system/urls.py +30 -0
- dj_design_system-0.1.0/dj_design_system/views.py +438 -0
- dj_design_system-0.1.0/docs/api/components.md +15 -0
- dj_design_system-0.1.0/docs/api/parameters.md +15 -0
- dj_design_system-0.1.0/docs/api-reference.md +6 -0
- dj_design_system-0.1.0/docs/assets/docs-theme.css +284 -0
- dj_design_system-0.1.0/docs/components.md +356 -0
- dj_design_system-0.1.0/docs/gallery.md +349 -0
- dj_design_system-0.1.0/docs/index.md +83 -0
- dj_design_system-0.1.0/docs/organisation.md +158 -0
- dj_design_system-0.1.0/docs/quickstart.md +144 -0
- dj_design_system-0.1.0/docs/registry.md +87 -0
- dj_design_system-0.1.0/docs/templatetags.md +233 -0
- dj_design_system-0.1.0/example_project/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_components/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_components/apps.py +6 -0
- dj_design_system-0.1.0/example_project/demo_components/components/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_components/components/alert.py +28 -0
- dj_design_system-0.1.0/example_project/demo_components/components/badge.py +19 -0
- dj_design_system-0.1.0/example_project/demo_components/components/button/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_components/components/button/button.css +34 -0
- dj_design_system-0.1.0/example_project/demo_components/components/button/button.js +11 -0
- dj_design_system-0.1.0/example_project/demo_components/components/button/button.py +40 -0
- dj_design_system-0.1.0/example_project/demo_components/components/card/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_components/components/card/abstract_card.py +12 -0
- dj_design_system-0.1.0/example_project/demo_components/components/card/info_card.py +32 -0
- dj_design_system-0.1.0/example_project/demo_components/components/card/layouts/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_components/components/card/layouts/hero.py +19 -0
- dj_design_system-0.1.0/example_project/demo_components/components/rich_button.css +5 -0
- dj_design_system-0.1.0/example_project/demo_components/components/rich_button.py +26 -0
- dj_design_system-0.1.0/example_project/demo_components/components/user_card.py +34 -0
- dj_design_system-0.1.0/example_project/demo_extra/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_extra/apps.py +6 -0
- dj_design_system-0.1.0/example_project/demo_extra/components/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_extra/components/button.py +22 -0
- dj_design_system-0.1.0/example_project/demo_nav/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_nav/apps.py +6 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/button.py +17 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/cards/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/cards/info_card/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/cards/info_card/info_card.py +12 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/design_guidelines.md +15 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/elements/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/elements/badge.py +12 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/elements/icon/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/elements/icon/accessibility.md +19 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/elements/icon/component.py +19 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/elements/icon/index.md +14 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/elements/index.md +6 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/generic/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/generic/divider.py +13 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/generic/tooltip/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_nav/components/generic/tooltip/component.py +16 -0
- dj_design_system-0.1.0/example_project/demo_single/__init__.py +0 -0
- dj_design_system-0.1.0/example_project/demo_single/apps.py +6 -0
- dj_design_system-0.1.0/example_project/demo_single/components.py +42 -0
- dj_design_system-0.1.0/example_project/manage.py +22 -0
- dj_design_system-0.1.0/example_project/settings.py +84 -0
- dj_design_system-0.1.0/example_project/static/example_project/demo.css +300 -0
- dj_design_system-0.1.0/example_project/urls.py +8 -0
- dj_design_system-0.1.0/example_project/wsgi.py +8 -0
- dj_design_system-0.1.0/pyproject.toml +161 -0
- dj_design_system-0.1.0/tests/__init__.py +0 -0
- dj_design_system-0.1.0/tests/conftest.py +136 -0
- dj_design_system-0.1.0/tests/e2e/__init__.py +0 -0
- dj_design_system-0.1.0/tests/e2e/conftest.py +22 -0
- dj_design_system-0.1.0/tests/e2e/test_gallery.py +201 -0
- dj_design_system-0.1.0/tests/settings.py +110 -0
- dj_design_system-0.1.0/tests/test_abstract_component.py +79 -0
- dj_design_system-0.1.0/tests/test_canvas_service.py +200 -0
- dj_design_system-0.1.0/tests/test_canvas_templatetag.py +68 -0
- dj_design_system-0.1.0/tests/test_canvas_views.py +148 -0
- dj_design_system-0.1.0/tests/test_components.py +201 -0
- dj_design_system-0.1.0/tests/test_data.py +239 -0
- dj_design_system-0.1.0/tests/test_finders.py +140 -0
- dj_design_system-0.1.0/tests/test_forms.py +283 -0
- dj_design_system-0.1.0/tests/test_markdown_canvas.py +359 -0
- dj_design_system-0.1.0/tests/test_navigation.py +887 -0
- dj_design_system-0.1.0/tests/test_parameters.py +643 -0
- dj_design_system-0.1.0/tests/test_registry.py +573 -0
- dj_design_system-0.1.0/tests/test_search.py +317 -0
- dj_design_system-0.1.0/tests/test_settings.py +40 -0
- dj_design_system-0.1.0/tests/test_tag_signature.py +355 -0
- dj_design_system-0.1.0/tests/test_templatetags.py +321 -0
- dj_design_system-0.1.0/tests/test_types.py +112 -0
- dj_design_system-0.1.0/tests/test_views.py +603 -0
- dj_design_system-0.1.0/tests/urls.py +8 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
*.egg-info/
|
|
7
|
+
*.egg
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# Tools
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.tox/
|
|
19
|
+
|
|
20
|
+
# Build outputs
|
|
21
|
+
dist/
|
|
22
|
+
build/
|
|
23
|
+
# mkdocs build output
|
|
24
|
+
site/
|
|
25
|
+
|
|
26
|
+
# Coverage
|
|
27
|
+
.coverage
|
|
28
|
+
.coverage.*
|
|
29
|
+
htmlcov/
|
|
30
|
+
coverage.xml
|
|
31
|
+
|
|
32
|
+
# Databases
|
|
33
|
+
*.db
|
|
34
|
+
*.sqlite3
|
|
35
|
+
|
|
36
|
+
# Secrets / local config
|
|
37
|
+
.env
|
|
38
|
+
.env.*
|
|
39
|
+
!.env.example
|
|
40
|
+
|
|
41
|
+
# OS
|
|
42
|
+
.DS_Store
|
|
43
|
+
Thumbs.db
|
|
44
|
+
|
|
45
|
+
# Editor
|
|
46
|
+
.vscode/
|
|
47
|
+
.idea/
|
|
48
|
+
*.swp
|
|
49
|
+
*.swo
|
|
50
|
+
|
|
51
|
+
# Static files collected by Django
|
|
52
|
+
staticfiles/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.0.1] - unreleased
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `TagComponent` and `BlockComponent` base classes for defining UI components
|
|
15
|
+
- Parameter system: `StrParam`, `BoolParam`, `StrCSSClassParam`, `BoolCSSClassParam`, `ModelParam`, `UserParam`, `FieldParam`
|
|
16
|
+
- Auto-discovery of components via Django's app registry (`AppConfig.ready()`)
|
|
17
|
+
- Component registry with lookup, listing, and navigation tree APIs
|
|
18
|
+
- Interactive gallery with live component previews in sandboxed iframes
|
|
19
|
+
- Searchable navigation tree with folder, component, and documentation node types
|
|
20
|
+
- Auto-generated templatetag usage examples from parameter definitions
|
|
21
|
+
- `ComponentsStaticFinder` to serve per-component CSS/JS via Django staticfiles
|
|
22
|
+
- Markdown documentation pages auto-discovered alongside components
|
|
23
|
+
- Configurable canvas backgrounds, toolbar, and gallery settings via `dj_design_system` Django settings dict
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Contributing to dj-design-system
|
|
2
|
+
|
|
3
|
+
Thank you for taking the time to contribute!
|
|
4
|
+
|
|
5
|
+
## Setting up a development environment
|
|
6
|
+
|
|
7
|
+
**Prerequisites:** [uv](https://docs.astral.sh/uv/) and [just](https://just.systems/) must be installed.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/Django-Design-System/django_design_system.git
|
|
11
|
+
cd django_design_system
|
|
12
|
+
just install
|
|
13
|
+
just install-hooks # installs the pre-commit git hook
|
|
14
|
+
just install-playwright # installs Playwright browsers (needed for e2e tests)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Running things locally
|
|
18
|
+
|
|
19
|
+
| Command | Purpose |
|
|
20
|
+
| ------------------------- | --------------------------------------------------- |
|
|
21
|
+
| `just test` | Run all unit tests |
|
|
22
|
+
| `just test-one <pattern>` | Run tests matching a keyword |
|
|
23
|
+
| `just e2e` | Run Playwright end-to-end tests |
|
|
24
|
+
| `just coverage` | Unit tests with coverage report |
|
|
25
|
+
| `just check` | Lint and formatting check (no changes) |
|
|
26
|
+
| `just fix` | Auto-fix all lint and formatting issues |
|
|
27
|
+
| `just typecheck` | Run mypy type checking |
|
|
28
|
+
| `just docs-serve` | Serve the documentation site locally |
|
|
29
|
+
| `just demo` | Start the example component gallery in your browser |
|
|
30
|
+
|
|
31
|
+
The pre-commit hook installed by `just install-hooks` runs `just fix` automatically before every commit.
|
|
32
|
+
|
|
33
|
+
## Code conventions
|
|
34
|
+
|
|
35
|
+
- **Python style** is enforced by [ruff](https://docs.astral.sh/ruff/) — run `just fix` before committing.
|
|
36
|
+
- **HTML/template style** is enforced by [djlint](https://djlint.com/) — also covered by `just fix`.
|
|
37
|
+
- **Type annotations** are checked by [mypy](https://mypy-lang.org/) — run `just typecheck`.
|
|
38
|
+
- Line length is 88 characters.
|
|
39
|
+
- All new public classes and functions should have docstrings — they feed directly into the auto-generated API documentation.
|
|
40
|
+
|
|
41
|
+
## Submitting a pull request
|
|
42
|
+
|
|
43
|
+
1. Fork the repository and create a branch from `main`.
|
|
44
|
+
2. Make your changes, add tests for any new behaviour.
|
|
45
|
+
3. Run `just test` and `just check` — both must pass.
|
|
46
|
+
4. Open a PR against `main`. All CI checks must be green before merging.
|
|
47
|
+
5. A maintainer will review and merge.
|
|
48
|
+
|
|
49
|
+
For bug reports or feature requests, please [open an issue](https://github.com/Django-Design-System/django_design_system/issues) first.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Department for Business and Trade
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dj-design-system
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Design System for Django
|
|
5
|
+
Project-URL: Homepage, https://django-design-system.github.io/django_design_system/
|
|
6
|
+
Project-URL: Repository, https://github.com/Django-Design-System/django_design_system
|
|
7
|
+
Project-URL: Documentation, https://django-design-system.github.io/django_design_system/
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/Django-Design-System/django_design_system/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/Django-Design-System/django_design_system/blob/main/CHANGELOG.md
|
|
10
|
+
Maintainer: marcelkornblum, CamLamb
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: Design System,component,django
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Framework :: Django
|
|
16
|
+
Classifier: Framework :: Django :: 5.2
|
|
17
|
+
Classifier: Framework :: Django :: 6.0
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Documentation
|
|
24
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
25
|
+
Requires-Python: >=3.13
|
|
26
|
+
Requires-Dist: django<8,>=5.2
|
|
27
|
+
Requires-Dist: markdown
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: djlint; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
32
|
+
Requires-Dist: types-markdown; extra == 'dev'
|
|
33
|
+
Requires-Dist: types-pygments; extra == 'dev'
|
|
34
|
+
Provides-Extra: docs
|
|
35
|
+
Requires-Dist: mkdocs-material; extra == 'docs'
|
|
36
|
+
Requires-Dist: mkdocstrings-python; extra == 'docs'
|
|
37
|
+
Provides-Extra: testing
|
|
38
|
+
Requires-Dist: coverage; extra == 'testing'
|
|
39
|
+
Requires-Dist: factory-boy; extra == 'testing'
|
|
40
|
+
Requires-Dist: playwright; extra == 'testing'
|
|
41
|
+
Requires-Dist: pytest-cov; extra == 'testing'
|
|
42
|
+
Requires-Dist: pytest-django; extra == 'testing'
|
|
43
|
+
Requires-Dist: pytest-mock; extra == 'testing'
|
|
44
|
+
Requires-Dist: pytest-playwright; extra == 'testing'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# dj-design-system
|
|
48
|
+
|
|
49
|
+
[](https://github.com/Django-Design-System/django_design_system/actions/workflows/ci.yml)
|
|
50
|
+
[](https://pypi.org/project/dj-design-system/)
|
|
51
|
+
[](https://pypi.org/project/dj-design-system/)
|
|
52
|
+
[](https://www.djangoproject.com/)
|
|
53
|
+
[](LICENSE)
|
|
54
|
+
|
|
55
|
+
DjDS is a Django-native approach to writing front end components that are exposed as templatetags. It comes with an auto-generated, customisable, live interactive gallery of your UI components that lives alongside your Django project.
|
|
56
|
+
|
|
57
|
+
Components are recognisably Django elements; they look and work like Models or Forms. The gallery auto-discovers them, renders live previews in sandboxed iframes, generates templatetag usage examples, and builds a searchable navigation tree — all from your existing code and docstrings.
|
|
58
|
+
|
|
59
|
+
**[Browse the demo gallery →](https://django-design-system.github.io/django_design_system/gallery/)**
|
|
60
|
+
|
|
61
|
+
## Quick start
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install dj-design-system
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then follow the [quickstart guide](https://django-design-system.github.io/django_design_system/quickstart/) to register your first component.
|
|
68
|
+
|
|
69
|
+
## Supported versions
|
|
70
|
+
|
|
71
|
+
| Python | Django |
|
|
72
|
+
| ------ | --------- |
|
|
73
|
+
| 3.13 | 5.2 (LTS) |
|
|
74
|
+
| 3.14 | 6.0 |
|
|
75
|
+
| | latest |
|
|
76
|
+
|
|
77
|
+
## Documentation
|
|
78
|
+
|
|
79
|
+
Full documentation is available at **[django-design-system.github.io/django_design_system/](https://django-design-system.github.io/django_design_system/)**.
|
|
80
|
+
|
|
81
|
+
To browse locally:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
just docs-serve
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
| Document | Contents |
|
|
88
|
+
| ------------------------------------- | ----------------------------------------- |
|
|
89
|
+
| [Quick start](docs/quickstart.md) | Installation and first component |
|
|
90
|
+
| [Components](docs/components.md) | Defining components and parameters |
|
|
91
|
+
| [Registry](docs/registry.md) | Auto-discovery and the component registry |
|
|
92
|
+
| [Gallery](docs/gallery.md) | Configuring and customising the gallery |
|
|
93
|
+
| [Template tags](docs/templatetags.md) | Using components in templates |
|
|
94
|
+
| [API reference](docs/api/) | Auto-generated class and parameter docs |
|
|
95
|
+
|
|
96
|
+
A static snapshot of the component gallery is browseable at **[django-design-system.github.io/django_design_system/gallery/](https://django-design-system.github.io/django_design_system/gallery/)** (HTMX interactions disabled; run `just demo` for the full live experience).
|
|
97
|
+
|
|
98
|
+
## Contributing
|
|
99
|
+
|
|
100
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for how to set up a dev environment, run tests, and submit a pull request.
|
|
101
|
+
|
|
102
|
+
## Code of conduct
|
|
103
|
+
|
|
104
|
+
This project follows the [Contributor Covenant](https://www.contributor-covenant.org/). Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before contributing.
|
|
105
|
+
|
|
106
|
+
## Issues and feature requests
|
|
107
|
+
|
|
108
|
+
Please open an issue on [GitHub](https://github.com/Django-Design-System/django_design_system/issues). Use the bug report template for defects and the feature request template for new ideas.
|
|
109
|
+
|
|
110
|
+
## Security
|
|
111
|
+
|
|
112
|
+
To report a vulnerability privately, see [SECURITY.md](SECURITY.md).
|
|
113
|
+
|
|
114
|
+
## Licence
|
|
115
|
+
|
|
116
|
+
[MIT](LICENSE)
|
|
117
|
+
|
|
118
|
+
With thanks to the UK [Department for Business and Trade](https://github.com/uktrade), where this was originally developed.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# dj-design-system
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Django-Design-System/django_design_system/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/dj-design-system/)
|
|
5
|
+
[](https://pypi.org/project/dj-design-system/)
|
|
6
|
+
[](https://www.djangoproject.com/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
DjDS is a Django-native approach to writing front end components that are exposed as templatetags. It comes with an auto-generated, customisable, live interactive gallery of your UI components that lives alongside your Django project.
|
|
10
|
+
|
|
11
|
+
Components are recognisably Django elements; they look and work like Models or Forms. The gallery auto-discovers them, renders live previews in sandboxed iframes, generates templatetag usage examples, and builds a searchable navigation tree — all from your existing code and docstrings.
|
|
12
|
+
|
|
13
|
+
**[Browse the demo gallery →](https://django-design-system.github.io/django_design_system/gallery/)**
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install dj-design-system
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then follow the [quickstart guide](https://django-design-system.github.io/django_design_system/quickstart/) to register your first component.
|
|
22
|
+
|
|
23
|
+
## Supported versions
|
|
24
|
+
|
|
25
|
+
| Python | Django |
|
|
26
|
+
| ------ | --------- |
|
|
27
|
+
| 3.13 | 5.2 (LTS) |
|
|
28
|
+
| 3.14 | 6.0 |
|
|
29
|
+
| | latest |
|
|
30
|
+
|
|
31
|
+
## Documentation
|
|
32
|
+
|
|
33
|
+
Full documentation is available at **[django-design-system.github.io/django_design_system/](https://django-design-system.github.io/django_design_system/)**.
|
|
34
|
+
|
|
35
|
+
To browse locally:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
just docs-serve
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
| Document | Contents |
|
|
42
|
+
| ------------------------------------- | ----------------------------------------- |
|
|
43
|
+
| [Quick start](docs/quickstart.md) | Installation and first component |
|
|
44
|
+
| [Components](docs/components.md) | Defining components and parameters |
|
|
45
|
+
| [Registry](docs/registry.md) | Auto-discovery and the component registry |
|
|
46
|
+
| [Gallery](docs/gallery.md) | Configuring and customising the gallery |
|
|
47
|
+
| [Template tags](docs/templatetags.md) | Using components in templates |
|
|
48
|
+
| [API reference](docs/api/) | Auto-generated class and parameter docs |
|
|
49
|
+
|
|
50
|
+
A static snapshot of the component gallery is browseable at **[django-design-system.github.io/django_design_system/gallery/](https://django-design-system.github.io/django_design_system/gallery/)** (HTMX interactions disabled; run `just demo` for the full live experience).
|
|
51
|
+
|
|
52
|
+
## Contributing
|
|
53
|
+
|
|
54
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for how to set up a dev environment, run tests, and submit a pull request.
|
|
55
|
+
|
|
56
|
+
## Code of conduct
|
|
57
|
+
|
|
58
|
+
This project follows the [Contributor Covenant](https://www.contributor-covenant.org/). Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before contributing.
|
|
59
|
+
|
|
60
|
+
## Issues and feature requests
|
|
61
|
+
|
|
62
|
+
Please open an issue on [GitHub](https://github.com/Django-Design-System/django_design_system/issues). Use the bug report template for defects and the feature request template for new ideas.
|
|
63
|
+
|
|
64
|
+
## Security
|
|
65
|
+
|
|
66
|
+
To report a vulnerability privately, see [SECURITY.md](SECURITY.md).
|
|
67
|
+
|
|
68
|
+
## Licence
|
|
69
|
+
|
|
70
|
+
[MIT](LICENSE)
|
|
71
|
+
|
|
72
|
+
With thanks to the UK [Department for Business and Trade](https://github.com/uktrade), where this was originally developed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from dj_design_system.services.registry import component_registry # noqa: F401
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DjangoDesignSystemConfig(AppConfig):
|
|
5
|
+
"""App configuration for dj_design_system."""
|
|
6
|
+
|
|
7
|
+
name = "dj_design_system"
|
|
8
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
9
|
+
|
|
10
|
+
def ready(self) -> None:
|
|
11
|
+
from dj_design_system import component_registry
|
|
12
|
+
|
|
13
|
+
component_registry.autodiscover()
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any
|
|
2
|
+
|
|
3
|
+
from django.utils.html import format_html
|
|
4
|
+
from django.utils.safestring import SafeString
|
|
5
|
+
|
|
6
|
+
from dj_design_system.parameters import BaseParam
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from dj_design_system.data import ComponentMedia
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseComponent:
|
|
14
|
+
# The template string itself to pass to format_html. Should be overridden by subclasses.
|
|
15
|
+
template_format_str: str = "<span class='{classes}'>ABSTRACT COMPONENT</span>"
|
|
16
|
+
|
|
17
|
+
class Meta:
|
|
18
|
+
abstract = True
|
|
19
|
+
|
|
20
|
+
def __init_subclass__(cls, **kwargs) -> None:
|
|
21
|
+
"""Validate Meta constraint declarations at class definition time."""
|
|
22
|
+
super().__init_subclass__(**kwargs)
|
|
23
|
+
from dj_design_system.services.component import get_own_meta, is_abstract
|
|
24
|
+
|
|
25
|
+
if is_abstract(cls):
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
meta = get_own_meta(cls)
|
|
29
|
+
param_names = set(cls.get_params().keys())
|
|
30
|
+
|
|
31
|
+
for a, b in getattr(meta, "mutually_exclusive", []):
|
|
32
|
+
for name in (a, b):
|
|
33
|
+
if name not in param_names:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"{cls.__name__}.Meta.mutually_exclusive references unknown param '{name}'."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
for dependent, dependency in getattr(meta, "requires", []):
|
|
39
|
+
for name in (dependent, dependency):
|
|
40
|
+
if name not in param_names:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
f"{cls.__name__}.Meta.requires references unknown param '{name}'."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def __init__(self, **kwargs):
|
|
46
|
+
self.context = {}
|
|
47
|
+
for var_name, var_value in kwargs.items():
|
|
48
|
+
setattr(self, var_name, var_value)
|
|
49
|
+
|
|
50
|
+
self._validate_meta_constraints()
|
|
51
|
+
self.validate_params()
|
|
52
|
+
|
|
53
|
+
def validate_params(self) -> None:
|
|
54
|
+
"""An override hook allowing param combinations or values to raise exceptions if necessary"""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
def _validate_meta_constraints(self) -> None:
|
|
58
|
+
"""Enforce mutually_exclusive and requires constraints declared on Meta.
|
|
59
|
+
|
|
60
|
+
Called automatically during __init__ before validate_params.
|
|
61
|
+
- Meta.mutually_exclusive: list of (param_a, param_b) pairs that cannot both be set.
|
|
62
|
+
- Meta.requires: list of (dependent, dependency) pairs where setting dependent
|
|
63
|
+
requires dependency to also be set.
|
|
64
|
+
"""
|
|
65
|
+
from dj_design_system.services.component import get_own_meta
|
|
66
|
+
|
|
67
|
+
params = type(self).get_params()
|
|
68
|
+
meta = get_own_meta(type(self))
|
|
69
|
+
|
|
70
|
+
for a, b in getattr(meta, "mutually_exclusive", []):
|
|
71
|
+
if params[a].has_been_set(self) and params[b].has_been_set(self):
|
|
72
|
+
raise ValueError(
|
|
73
|
+
f"'{a}' and '{b}' cannot both be set on {type(self).__name__}."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
for dependent, dependency in getattr(meta, "requires", []):
|
|
77
|
+
if params[dependent].has_been_set(self) and not params[
|
|
78
|
+
dependency
|
|
79
|
+
].has_been_set(self):
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"'{dependent}' requires '{dependency}' to also be set on {type(self).__name__}."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def get_context(self) -> dict[str, Any]:
|
|
85
|
+
"""
|
|
86
|
+
Get the context for rendering the component. This method can be overridden by subclasses to add additional or edit other context variables.
|
|
87
|
+
"""
|
|
88
|
+
self.context["classes"] = self.get_classes_string()
|
|
89
|
+
for param_name, spec in self.params.items():
|
|
90
|
+
value = getattr(self, param_name)
|
|
91
|
+
self.context[param_name] = value
|
|
92
|
+
self.context.update(spec.get_extra_context(param_name, value))
|
|
93
|
+
return self.context
|
|
94
|
+
|
|
95
|
+
def get_classes_string(self):
|
|
96
|
+
"""
|
|
97
|
+
Get a string of CSS classes based on the context. This can be used in the template to apply conditional styling.
|
|
98
|
+
"""
|
|
99
|
+
classes = []
|
|
100
|
+
for param_name, spec in self.params.items():
|
|
101
|
+
param_value = getattr(self, param_name)
|
|
102
|
+
classes.extend(spec.get_css_classes(param_name, param_value))
|
|
103
|
+
return " ".join(classes)
|
|
104
|
+
|
|
105
|
+
def render(self) -> str:
|
|
106
|
+
"""
|
|
107
|
+
Render the component as an HTML string.
|
|
108
|
+
"""
|
|
109
|
+
return format_html(format_string=self.template_format_str, **self.get_context())
|
|
110
|
+
|
|
111
|
+
def __str__(self) -> str:
|
|
112
|
+
return self.render()
|
|
113
|
+
|
|
114
|
+
def __html__(self) -> str:
|
|
115
|
+
return self.render()
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def description(self) -> str | None:
|
|
119
|
+
return self.__doc__
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def params(self) -> dict[str, "BaseParam"]:
|
|
123
|
+
return type(self).get_params()
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def get_params(cls) -> dict[str, "BaseParam"]:
|
|
127
|
+
"""
|
|
128
|
+
Get the parameters for this component. Returns a dictionary of all
|
|
129
|
+
BaseParam descriptors defined on the class (or any subclass in the MRO).
|
|
130
|
+
"""
|
|
131
|
+
result = {}
|
|
132
|
+
for klass in cls.__mro__:
|
|
133
|
+
for attr_name, attr_value in vars(klass).items():
|
|
134
|
+
if isinstance(attr_value, BaseParam) and attr_name not in result:
|
|
135
|
+
result[attr_name] = attr_value
|
|
136
|
+
return result
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def docstring(cls) -> str:
|
|
140
|
+
"""
|
|
141
|
+
Return a string describing the API of this component, including its parameters and their types.
|
|
142
|
+
"""
|
|
143
|
+
params = cls.get_params()
|
|
144
|
+
api_docs = f"{cls.__doc__}\n\n"
|
|
145
|
+
if len(params) > 0:
|
|
146
|
+
api_docs += "Parameters:\n"
|
|
147
|
+
for parameter_spec in params.values():
|
|
148
|
+
api_docs += f"- {parameter_spec.docstring()}\n"
|
|
149
|
+
return api_docs
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def get_name(cls) -> str:
|
|
153
|
+
"""Return the component's registered name from the registry."""
|
|
154
|
+
from dj_design_system import component_registry
|
|
155
|
+
|
|
156
|
+
return component_registry.get_info(cls).name
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def get_app_label(cls) -> str:
|
|
160
|
+
"""Return the app label this component was discovered in."""
|
|
161
|
+
from dj_design_system import component_registry
|
|
162
|
+
|
|
163
|
+
return component_registry.get_info(cls).app_label
|
|
164
|
+
|
|
165
|
+
@classmethod
|
|
166
|
+
def get_relative_path(cls) -> str:
|
|
167
|
+
"""Return the relative path within the app's components directory."""
|
|
168
|
+
from dj_design_system import component_registry
|
|
169
|
+
|
|
170
|
+
return component_registry.get_info(cls).relative_path
|
|
171
|
+
|
|
172
|
+
@classmethod
|
|
173
|
+
def get_media(cls) -> "ComponentMedia":
|
|
174
|
+
"""Return the CSS and JS static URL paths required by this component.
|
|
175
|
+
|
|
176
|
+
Delegates to ``ComponentInfo.media`` — see that property for full
|
|
177
|
+
documentation of auto-discovery and ``Media`` class override behaviour.
|
|
178
|
+
"""
|
|
179
|
+
from dj_design_system import component_registry
|
|
180
|
+
|
|
181
|
+
return component_registry.get_info(cls).media
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
def get_positional_args(cls) -> list[str]:
|
|
185
|
+
"""Return the list of positional arg names from the class's own Meta.positional_args.
|
|
186
|
+
|
|
187
|
+
Only looks at the class's own ``Meta`` — positional_args are NOT
|
|
188
|
+
inherited from parent classes, matching Django's convention that
|
|
189
|
+
Meta is not inherited. This avoids silent ordering surprises when
|
|
190
|
+
subclassing.
|
|
191
|
+
"""
|
|
192
|
+
from dj_design_system.services.component import get_own_meta
|
|
193
|
+
|
|
194
|
+
meta = get_own_meta(cls)
|
|
195
|
+
positional = getattr(meta, "positional_args", None)
|
|
196
|
+
return list(positional) if positional else []
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def map_positional_args(
|
|
200
|
+
positional_args: list[str], args: tuple, kwargs: dict
|
|
201
|
+
) -> dict:
|
|
202
|
+
"""Map positional arguments to keyword arguments using the positional_args spec."""
|
|
203
|
+
for i, arg_name in enumerate(positional_args):
|
|
204
|
+
if i < len(args):
|
|
205
|
+
kwargs[arg_name] = args[i]
|
|
206
|
+
return kwargs
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class TagComponent(BaseComponent):
|
|
210
|
+
"""
|
|
211
|
+
A component registered as a Django ``simple_tag``.
|
|
212
|
+
|
|
213
|
+
Subclass this for components that produce a single HTML fragment
|
|
214
|
+
without wrapping nested template content.
|
|
215
|
+
|
|
216
|
+
Use ``Meta.positional_args`` to declare parameters that can be passed
|
|
217
|
+
as positional arguments in the template tag::
|
|
218
|
+
|
|
219
|
+
class IconComponent(TagComponent):
|
|
220
|
+
name = StrParam("The icon name.")
|
|
221
|
+
|
|
222
|
+
class Meta:
|
|
223
|
+
positional_args = ["name"]
|
|
224
|
+
|
|
225
|
+
This allows ``{% icon "check" %}`` instead of ``{% icon name="check" %}``.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
class Meta:
|
|
229
|
+
abstract = True
|
|
230
|
+
|
|
231
|
+
@classmethod
|
|
232
|
+
def as_tag(cls):
|
|
233
|
+
"""Return a template tag function mapping positional args via Meta.positional_args."""
|
|
234
|
+
positional_args = cls.get_positional_args()
|
|
235
|
+
|
|
236
|
+
def _tag(*args, **kwargs):
|
|
237
|
+
cls.map_positional_args(positional_args, args, kwargs)
|
|
238
|
+
return cls(**kwargs)
|
|
239
|
+
|
|
240
|
+
return _tag
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class BlockComponent(BaseComponent):
|
|
244
|
+
"""
|
|
245
|
+
A component registered as a Django ``simple_block_tag``, allowing
|
|
246
|
+
for nested template content.
|
|
247
|
+
|
|
248
|
+
The template should include a ``{content}`` placeholder where the
|
|
249
|
+
inner content will be rendered. ``content`` is always the first
|
|
250
|
+
positional argument and should NOT appear in ``Meta.positional_args``.
|
|
251
|
+
|
|
252
|
+
Use ``Meta.positional_args`` to declare additional positional args
|
|
253
|
+
beyond ``content``::
|
|
254
|
+
|
|
255
|
+
class SectionComponent(BlockComponent):
|
|
256
|
+
title = StrParam("Section title.")
|
|
257
|
+
|
|
258
|
+
class Meta:
|
|
259
|
+
positional_args = ["title"]
|
|
260
|
+
|
|
261
|
+
This allows ``{% section "My Title" %}...{% endsection %}``.
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
class Meta:
|
|
265
|
+
abstract = True
|
|
266
|
+
|
|
267
|
+
template_format_str: str = "<span class='{classes}'>{content}</span>"
|
|
268
|
+
|
|
269
|
+
def __init__(self, content: SafeString, **kwargs):
|
|
270
|
+
self.content = content
|
|
271
|
+
super().__init__(**kwargs)
|
|
272
|
+
|
|
273
|
+
def get_context(self) -> dict[str, Any]:
|
|
274
|
+
"""Add ``content`` to the context automatically.
|
|
275
|
+
|
|
276
|
+
``content`` is the block body passed by the template engine — it is
|
|
277
|
+
NOT a declared BaseParam and should not appear in ``Meta.positional_args``
|
|
278
|
+
or ``docstring()`` output.
|
|
279
|
+
"""
|
|
280
|
+
context = super().get_context()
|
|
281
|
+
context["content"] = self.content
|
|
282
|
+
return context
|
|
283
|
+
|
|
284
|
+
@classmethod
|
|
285
|
+
def as_tag(cls):
|
|
286
|
+
"""Return a template tag function with content as first arg, plus any Meta.positional_args."""
|
|
287
|
+
positional_args = cls.get_positional_args()
|
|
288
|
+
|
|
289
|
+
def _tag(content, *args, **kwargs):
|
|
290
|
+
cls.map_positional_args(positional_args, args, kwargs)
|
|
291
|
+
return cls(content=content, **kwargs)
|
|
292
|
+
|
|
293
|
+
return _tag
|