diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6dc8c94..29c35ec1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,45 +8,41 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - run: pipx install "poetry>=1.1.12,<2" + - uses: actions/setup-python@v5 with: - python-version: "3.10" - - uses: Gr1n/setup-poetry@v7 - with: - poetry-version: '1.1.12' + python-version: "3.13" + cache: "poetry" - run: pip install tox - - run: tox -e lint,py310-dj40 + - run: tox -e lint,py313-dj51 test_compatibility: needs: test runs-on: ubuntu-latest strategy: + fail-fast: false matrix: include: # Test with all supported Django versions, for all compatible Python versions. - # See https://docs.djangoproject.com/en/4.0/faq/install/#what-python-version-can-i-use-with-django for the official matrix. + # See https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django for the official matrix. # Additionally test on Django’s main branch with the most recent Python version. - - python: "3.7" - toxenv: py37-dj32 - - python: "3.8" - toxenv: py38-dj32,py38-dj40,py38-dj41 - python: "3.9" - toxenv: py39-dj32,py39-dj40,py39-dj41 + toxenv: py39-dj42 - python: "3.10" - # Skip testing Django 4.0, already tested in previous workflow job. - toxenv: py310-dj32,py310-dj41,py310-djmain - # Tentative support for next Python pre-release. For the correct specifier, - # Check: https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json. - - python: "3.11.0-beta.2" - toxenv: py311-dj41 + toxenv: py310-dj42,py310-dj51,py310-dj52 + - python: "3.11" + toxenv: py311-dj42,py311-dj51,py311-dj52 + - python: "3.12" + toxenv: py312-dj42,py312-dj52 + - python: "3.13" + toxenv: py313-dj42,py313-dj52,py312-djmain steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - run: pipx install "poetry>=1.1.12,<2" + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - - uses: Gr1n/setup-poetry@v7 - with: - poetry-version: '1.1.12' + allow-prereleases: true - run: pip install tox - run: tox -q env: @@ -55,31 +51,24 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version-file: '.nvmrc' + node-version-file: ".nvmrc" - id: node-cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: + # Cache node_modules rather than the npm cache, as we rarely update npm packages. path: node_modules key: ${{ runner.os }}-node-${{ hashFiles('**/.nvmrc') }}-${{ hashFiles('**/package-lock.json') }} - if: steps.node-cache.outputs.cache-hit != 'true' run: npm ci --no-audit - run: npm run build - - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - uses: Gr1n/setup-poetry@v7 - with: - poetry-version: '1.1.12' - - uses: actions/cache@v3 + - run: pipx install "poetry>=1.1.12,<2" + - uses: actions/setup-python@v5 with: - path: ~/.cache/pip - key: ${{ runner.os }}-python-py310-${{ hashFiles('**/pyproject.toml') }} - restore-keys: | - ${{ runner.os }}-python-py310- - - run: poetry config virtualenvs.create false + python-version-file: pyproject.toml + cache: "poetry" - run: poetry install - run: poetry run django-admin runserver --settings=tests.settings.production --pythonpath=. & # Docs website build. @@ -95,20 +84,19 @@ jobs: - run: cat pyproject.toml | awk '{sub(/^version = .+/,"version = \"0.0.0.dev\"")}1' > pyproject.toml.tmp && mv pyproject.toml.tmp pyproject.toml - run: poetry build - run: mv dist site - - uses: actions/upload-artifact@v3 + - uses: actions/configure-pages@v4 + - uses: actions/upload-pages-artifact@v3 with: - name: site path: site - retention-days: 1 deploy_site: - runs-on: ubuntu-latest needs: build_site + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 - - uses: JamesIves/github-pages-deploy-action@v4.3.3 - with: - branch: gh-pages - folder: site - clean: true + - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 31720810..0c18ad31 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -3,9 +3,19 @@ name: "CodeQL" on: push: branches: [main] + paths-ignore: + - '**/*.md' + - '**/*.yml' + - '**/*.html' + - '**/*.scss' pull_request: # The branches below must be a subset of the branches above branches: [main] + paths-ignore: + - '**/*.md' + - '**/*.yml' + - '**/*.html' + - '**/*.scss' schedule: - cron: "28 20 * * 5" diff --git a/.gitignore b/.gitignore index 132a0c25..2c03d5b7 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,6 @@ var/ *.egg-info/ .installed.cfg *.egg -poetry.lock # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. diff --git a/CHANGELOG.md b/CHANGELOG.md index b6ed404a..48698781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,65 @@ ## [Unreleased] +## [1.3.0](https://github.com/torchbox/django-pattern-library/releases/tag/v1.3.0) - 2024-12-11 + +### Added + +- Add support for Django 5.1 ([#251](https://github.com/torchbox/django-pattern-library/pull/251)) + +### Removed + +- Drop support for Python 3.8 ([#251](https://github.com/torchbox/django-pattern-library/pull/251)) +- Drop support for Django 4.1 ([#242](https://github.com/torchbox/django-pattern-library/pull/242)) + +### Documentation + +- Note requirement for `.md` extension for pattern documentation files ([#248](https://github.com/torchbox/django-pattern-library/pull/248)) +- Mention complementary package django-viewcomponent, and django-lookbook as an alternative. ([#250](https://github.com/torchbox/django-pattern-library/pull/250)) + +### Maintenance + +- Test with Python 3.12 ([#242](https://github.com/torchbox/django-pattern-library/pull/242)) + +## [1.2.0](https://github.com/torchbox/django-pattern-library/releases/tag/v1.2.0) - 2024-01-16 + +### Added + +- Add support for Django 5.0 ([#241](https://github.com/torchbox/django-pattern-library/pull/241)) + +### Changed + +- From Django >= 4.0, calls to `Node.render()` must always return a string, but this app previously allowed non-string values to be passed in the `default_html` parameter to `override_tag`. Passing a non-string now raises a `TypeError` when using Django >= 4.0, and raises a warning for older versions ([issue #211](https://github.com/torchbox/django-pattern-library/issues/211)). + +## [1.1.0](https://github.com/torchbox/django-pattern-library/releases/tag/v1.1.0) - 2023-10-25 + +### Added + +- Add support for Django 4.2 ([#231](https://github.com/torchbox/django-pattern-library/pull/231)) + +### Changed + +- Switch to the `poetry-core` build backend ([#232](https://github.com/torchbox/django-pattern-library/pull/232)) + +### Removed + +- Drop support for Python 3.7 ([#231](https://github.com/torchbox/django-pattern-library/pull/231)) +- Drop support for Django 4.0 ([#231](https://github.com/torchbox/django-pattern-library/pull/231)) + +### Fixed + +- Ensure the project root is on `sys.path` so tests etc. can be run in by Docker Compose ([#233](https://github.com/torchbox/django-pattern-library/issues/233), [#234](https://github.com/torchbox/django-pattern-library/pull/234)) +- Fix URL pattern matching for template with dashes in the file name ([#229](https://github.com/torchbox/django-pattern-library/issues/229), [#230](https://github.com/torchbox/django-pattern-library/pull/230)) + +## [1.0.1](https://github.com/torchbox/django-pattern-library/releases/tag/v1.0.1) - 2023-08-19 + +### Fixed + - Disable pointer events on menu chevron to allow clicks ([#202](https://github.com/torchbox/django-pattern-library/issues/202), [#205](https://github.com/torchbox/django-pattern-library/pull/205)) +- Improve menu accessibility by using buttons for menu items ([#202](https://github.com/torchbox/django-pattern-library/issues/202), [#207](https://github.com/torchbox/django-pattern-library/pull/207)). +- Fix pattern name URL regex to account for Windows paths with backslash ([#222](https://github.com/torchbox/django-pattern-library/issues/222), [#223](https://github.com/torchbox/django-pattern-library/pull/223)) +- Use the correct iframe width with resize buttons ([#226](https://github.com/torchbox/django-pattern-library/issues/226), [#225](https://github.com/torchbox/django-pattern-library/pull/225)). +- Update the project’s test matrix for upcoming Django 4.2 support ([#212](https://github.com/torchbox/django-pattern-library/issues/212),[#220](https://github.com/torchbox/django-pattern-library/pull/220)). ## [1.0.0](https://github.com/torchbox/django-pattern-library/releases/tag/v1.0.0) - 2022-06-10 diff --git a/Dockerfile b/Dockerfile index a62978ec..9470f964 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ RUN useradd --create-home dpl && \ chown -R dpl:dpl /venv/ /app/ ENV PATH=/venv/bin:/home/dpl/.local/bin:$PATH \ + PYTHONPATH=/app/ \ VIRTUAL_ENV=/venv/ \ DJANGO_SETTINGS_MODULE=tests.settings.dev diff --git a/docker-compose.yml b/docker-compose.yml index 7ec76d6a..287aba1e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.2' - services: web: build: . diff --git a/docs/community/related-projects.md b/docs/community/related-projects.md index e9dae47e..00b1521b 100644 --- a/docs/community/related-projects.md +++ b/docs/community/related-projects.md @@ -8,7 +8,8 @@ Here are other projects that are related to django-pattern-library, and may be r - [django-components](https://github.com/EmilStenstrom/django-components/) – Reusable UI components for Django, going further than template partials. - [django-component-tags](https://github.com/syse-i/django-component-tags) – Create advanced HTML components using Django Tags. - [slippers](https://github.com/mixxorz/slippers) – Reusable components for Django, without writing a single line of Python. -- (Jinja only, incompatible but interesting) [Template Components (tcom)](https://tcom.scaletti.dev/) – Write server-side components as single Jinja template files. Use them as HTML tags without doing any importing. +- (Jinja only, incompatible but interesting) [JinjaX](https://jinjax.scaletti.dev/) – Write server-side components as single Jinja template files. Use them as HTML tags without doing any importing. +- [django-viewcomponent](https://github.com/rails-inspire-django/django-viewcomponent) - Build reusable components in Django, inspired by Rails ViewComponent, the components built by django-viewcomponent can be used in both Django template or Python code. ## Alternatives @@ -16,6 +17,7 @@ Here are other projects that are related 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 +- [django-lookbook](https://github.com/rails-inspire-django/django-lookbook) - Empower your Django development with this pluggable app for creating a robust component library. Includes preview system, documentation engine, and parameter editor for building modular UI effortlessly. ## Pattern libraries based on Django diff --git a/docs/getting-started.md b/docs/getting-started.md index d3bd9045..f5ac2d6f 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -17,8 +17,8 @@ poetry add --dev django-pattern-library We support: -- Django 3.2, 4.0, 4.1 (experimental), 4.2 (experimental) -- Python 3.7, 3.8, 3.9, 3.10, 3.11 (experimental) +- Django 4.2, 5.0, 5.1 +- Python 3.9, 3.10, 3.11, 3.12 - Django Templates only, no Jinja support - Modern “evergreen” desktop and mobile browsers diff --git a/docs/guides/automated-tests.md b/docs/guides/automated-tests.md index fa6f08c6..cb3bd27c 100644 --- a/docs/guides/automated-tests.md +++ b/docs/guides/automated-tests.md @@ -3,7 +3,7 @@ Although pattern libraries often start as tools for manual tests during development, they can also be useful for automated UI testing. There are a few benefits to doing UI tests with a pattern library: - Test the components in isolation. When tests fail, you will know exactly which component has issues, rather than having to inspect whole pages to understand what might have changed. -- Test the components with mock data. One of the issues with UI tests is to have test data for your UIs to render – you can reuse the pattern library data for this purpose (althoug there are [limitations](../guides/multiple-variants.md)). +- Test the components with mock data. One of the issues with UI tests is to have test data for your UIs to render – you can reuse the pattern library data for this purpose (although there are [limitations](../guides/multiple-variants.md)). ## Setting up automated UI tests diff --git a/docs/guides/usage-tips.md b/docs/guides/usage-tips.md index c02aac8a..4bda3efc 100644 --- a/docs/guides/usage-tips.md +++ b/docs/guides/usage-tips.md @@ -10,7 +10,8 @@ You may also consider using the [`render_patterns` command](../reference/api.md# ## 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: +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, ensuring the filename has a `.md` extension (e.g., `call_to_action.md`): + ```markdown This template can be used in different places. In streamfield block diff --git a/docs/reference/api.md b/docs/reference/api.md index 27d10734..e20f82e7 100644 --- a/docs/reference/api.md +++ b/docs/reference/api.md @@ -6,7 +6,7 @@ YAML isn’t everyone’s favorite markup language, but it has the advantage of Here is what you need to know: -- Use `.yaml` or `.yml` as the file extension for pattern configuration files. If both are present, the `.yaml` file takes precendence. +- Use `.yaml` or `.yml` as the file extension for pattern configuration files. If both are present, the `.yaml` file takes precedence. - 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 diff --git a/pattern_library/loader_tags.py b/pattern_library/loader_tags.py index 4cc6a9b8..80dd1116 100644 --- a/pattern_library/loader_tags.py +++ b/pattern_library/loader_tags.py @@ -151,9 +151,10 @@ def do_include(parser, token): isolated_context=isolated_context, ) + def visit_extends(self, node, frame): """This method overrides the jinja extends tag - Is called as part of the compiler CodeGenerator + Is called as part of the compiler CodeGenerator and adds a line to use the template_new_context as part of the runtime render to pull in the dpl context Handles visiting extends @@ -196,4 +197,4 @@ def template_new_context( return new_context( self.environment, self.name, self.blocks, vars, shared, self.globals, locals - ) \ No newline at end of file + ) diff --git a/pattern_library/management/commands/render_patterns.py b/pattern_library/management/commands/render_patterns.py index 7f3caeed..32268b7a 100644 --- a/pattern_library/management/commands/render_patterns.py +++ b/pattern_library/management/commands/render_patterns.py @@ -5,11 +5,7 @@ from django.test.client import RequestFactory from pattern_library import get_base_template_names, get_pattern_base_template_name -from pattern_library.utils import ( - get_pattern_context, - render_pattern, - get_renderer, -) +from pattern_library.utils import get_pattern_context, get_renderer, render_pattern class Command(BaseCommand): diff --git a/pattern_library/monkey_utils.py b/pattern_library/monkey_utils.py index 7dbc659f..2da37f44 100644 --- a/pattern_library/monkey_utils.py +++ b/pattern_library/monkey_utils.py @@ -1,5 +1,7 @@ +import inspect import logging -from typing import Optional +import typing +import warnings import django from django.template.library import SimpleNode @@ -11,7 +13,9 @@ def override_tag( - register: django.template.Library, name: str, default_html: Optional[str] = None + register: django.template.Library, + name: str, + default_html: typing.Optional[typing.Any] = UNSPECIFIED, ): """ An utility that helps you override original tags for use in your pattern library. @@ -29,7 +33,7 @@ def node_render(context): tag_overridden = False result = "" - # Get overriden tag config. + # Get overridden tag config. tag_overrides = context.get("__pattern_library_tag_overrides", {}) # Extract values for lookup from the token @@ -79,6 +83,23 @@ def node_render(context): # See https://github.com/torchbox/django-pattern-library/issues/166. return str(result) elif default_html is not UNSPECIFIED: + # Ensure default_html is a string. + if not isinstance(default_html, str): + # Save the caller for the override tag in case it's needed for error reporting. + trace = inspect.stack()[1] + if django.VERSION < (4, 0): + warnings.warn( + "default_html argument to override_tag should be a string to ensure compatibility " + 'with Django >= 4.0 (line %s in "%s")' + % (trace.lineno, trace.filename), + Warning, + ) + else: + raise TypeError( + 'default_html argument to override_tag must be a string (line %s in "%s")' + % (trace.lineno, trace.filename) + ) + # Render provided default; # if no stub data supplied. return default_html @@ -97,14 +118,16 @@ def node_render(context): return tag_func + # have to export the original jinja visit Extends # in the case jinja tags are being overriden jinja_visit_Extends = None + def override_jinja_tags(): """ Overrides jinja extends and include tags for use in your pattern library. - Call it in your settings to override tags + Call it in your settings to override tags """ global jinja_visit_Extends try: @@ -112,8 +135,9 @@ def override_jinja_tags(): from jinja2.environment import Template as JinjaTemplate except ModuleNotFoundError: ModuleNotFoundError("install jinja2 to override jinja tags") - + from .loader_tags import template_new_context, visit_extends + jinja_visit_Extends = JinjaCodeGenerator.visit_Extends JinjaTemplate.new_context = template_new_context - JinjaCodeGenerator.visit_Extends = visit_extends \ No newline at end of file + JinjaCodeGenerator.visit_Extends = visit_extends diff --git a/pattern_library/static/pattern_library/src/js/components/navigation.js b/pattern_library/static/pattern_library/src/js/components/navigation.js index 8c62f537..31c211fa 100644 --- a/pattern_library/static/pattern_library/src/js/components/navigation.js +++ b/pattern_library/static/pattern_library/src/js/components/navigation.js @@ -1,10 +1,11 @@ export function toggleNavItems() { - const headings = document.querySelectorAll('.js-toggle-pattern'); - headings.forEach(heading => { - heading.addEventListener('click', e => { + const categoryButtons = document.querySelectorAll('.js-toggle-pattern'); + + categoryButtons.forEach((button) => { + button.addEventListener('click', (e) => { e.target.classList.toggle('is-open'); - for ( const element of e.target.parentNode.childNodes ) { - if ( element.nodeName === "UL" ){ + for (const element of e.target.closest('.js-list-item').childNodes) { + if (element.nodeName === 'UL') { element.classList.toggle('is-open'); } } diff --git a/pattern_library/static/pattern_library/src/scss/components/_iframe.scss b/pattern_library/static/pattern_library/src/scss/components/_iframe.scss index 07c7a581..47227106 100644 --- a/pattern_library/static/pattern_library/src/scss/components/_iframe.scss +++ b/pattern_library/static/pattern_library/src/scss/components/_iframe.scss @@ -1,4 +1,5 @@ .iframe { + box-sizing: content-box; width: 100%; border: 1px solid $light-grey; margin: 20px 0; diff --git a/pattern_library/static/pattern_library/src/scss/components/_list.scss b/pattern_library/static/pattern_library/src/scss/components/_list.scss index 5819421f..1a4df040 100644 --- a/pattern_library/static/pattern_library/src/scss/components/_list.scss +++ b/pattern_library/static/pattern_library/src/scss/components/_list.scss @@ -19,23 +19,24 @@ padding-left: 15px; } - &__item-heading { + &__button { display: flex; align-items: center; margin: 10px 0; user-select: none; - font-weight: 500; font-size: 19px; + appearance: none; + background-color: transparent; + border: 0; + gap: 5px; + padding: 0; &:hover { cursor: pointer; } - &--light { - font-weight: 200; - } - - &--small{ + &--child { + color: $mid-grey; font-size: 13px; } } @@ -44,7 +45,8 @@ width: 15px; height: 15px; pointer-events: none; - + transition: transform 0.15s ease-in-out; + .is-open > & { transform: rotate(90deg); } diff --git a/pattern_library/static/pattern_library/src/scss/layout/_sidebar.scss b/pattern_library/static/pattern_library/src/scss/layout/_sidebar.scss index 1a311c32..b24d5e71 100644 --- a/pattern_library/static/pattern_library/src/scss/layout/_sidebar.scss +++ b/pattern_library/static/pattern_library/src/scss/layout/_sidebar.scss @@ -1,12 +1,15 @@ .sidebar { background-color: $off-white; - padding: 20px; height: 100vh; margin-left: 0; overflow: auto; -ms-grid-column: 1; -ms-grid-row: 2; + &__inner { + padding: 20px; + } + &__search { width: 100%; padding: 10px; diff --git a/pattern_library/templates/pattern_library/base.html b/pattern_library/templates/pattern_library/base.html index e5deb10a..4b6b5be7 100644 --- a/pattern_library/templates/pattern_library/base.html +++ b/pattern_library/templates/pattern_library/base.html @@ -22,24 +22,26 @@

{% block content %}{% endblock %} diff --git a/pattern_library/templates/pattern_library/pattern_group.html b/pattern_library/templates/pattern_library/pattern_group.html index dd44d18e..d4b77089 100644 --- a/pattern_library/templates/pattern_library/pattern_group.html +++ b/pattern_library/templates/pattern_library/pattern_group.html @@ -1,12 +1,12 @@