pax_global_header00006660000000000000000000000064145665766340014540gustar00rootroot0000000000000052 comment=1b218092f133a4d1bfc46b62febc2606e2faebfe crispy-bootstrap5-2024.2/000077500000000000000000000000001456657663400152425ustar00rootroot00000000000000crispy-bootstrap5-2024.2/.github/000077500000000000000000000000001456657663400166025ustar00rootroot00000000000000crispy-bootstrap5-2024.2/.github/FUNDING.yml000066400000000000000000000014711456657663400204220ustar00rootroot00000000000000# These are supported funding model platforms github: [django-crispy-forms] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] crispy-bootstrap5-2024.2/.github/workflows/000077500000000000000000000000001456657663400206375ustar00rootroot00000000000000crispy-bootstrap5-2024.2/.github/workflows/publish.yml000066400000000000000000000030361456657663400230320ustar00rootroot00000000000000name: Publish Python Package on: release: types: [created] jobs: tests: name: Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip wheel setuptools python -m pip install --upgrade tox - name: Run tox targets for ${{ matrix.python-version }} run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .) deploy: runs-on: ubuntu-latest needs: [tests] steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: "3.10" - uses: actions/cache@v2 name: Configure pip caching with: path: ~/.cache/pip key: ${{ runner.os }}-publish-pip-${{ hashFiles('**/setup.py') }} restore-keys: | ${{ runner.os }}-publish-pip- - name: Install dependencies run: | pip install build twine - name: Publish env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} run: | python -m build twine upload dist/* crispy-bootstrap5-2024.2/.github/workflows/test.yml000066400000000000000000000022341456657663400223420ustar00rootroot00000000000000name: Test on: push: branches: - main pull_request: jobs: tests: name: Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip wheel setuptools python -m pip install --upgrade tox - name: Run tox targets for ${{ matrix.python-version }} run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .) lint: name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.11" - name: Install dependencies run: | python -m pip install --upgrade pip tox - name: Run lint run: tox -e lint crispy-bootstrap5-2024.2/.gitignore000066400000000000000000000001571456657663400172350ustar00rootroot00000000000000.venv __pycache__/ *.py[cod] *$py.class venv .eggs .pytest_cache *.egg-info .DS_Store .vscode *python-version crispy-bootstrap5-2024.2/.pre-commit-config.yaml000066400000000000000000000004231456657663400215220ustar00rootroot00000000000000repos: - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black - repo: https://github.com/pycqa/isort rev: 5.12.0 hooks: - id: isort - repo: https://github.com/pycqa/flake8 rev: 6.0.0 hooks: - id: flake8crispy-bootstrap5-2024.2/CHANGELOG.md000066400000000000000000000047161456657663400170630ustar00rootroot00000000000000# CHANGELOG FOR CRISPY-BOOTSTRAP5 ## 2024.2 (2024-02-24) * Added support for [Switches](https://getbootstrap.com/docs/5.2/forms/checks-radios/#switches). (#162) * Used `
` and `` to group related inputs. * Added modal template. ## 2023.10 (2023-10-2023) * Added Django 5.0 and 4.2 support * Added Python 3.11 and 3.12 support * Dropped Python 3.7 support * Dropped Django 3.2, 4.0 and 4.1 support * Switched to CalVer versioning See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/8?closed=1) for full change log. ## 0.7 (2022-09-28) * Added Django 4.1 support See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestones?state=closed) for for change list. ## 0.6 (2021-09-28) * Added Django 4.0 support ## 0.5 (2021-08-20) * Added support for [Accordion Flush](https://getbootstrap.com/docs/5.0/components/accordion/#flush) and [Always Open](https://getbootstrap.com/docs/5.0/components/accordion/#always-open) (#63) * Added support for grouped inputs (#64) * Added support for clearable file field (#53) * Removed various `|safe` filters in templates See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/6?closed=1) for full changelog. ## 0.4 (2021-05-27) * Added support for Bootstrap 5 Floating Labels (#42) * Dropped support for Django 3.0 * Added support for Django 3.2 See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/5?closed=1) for full changelog. ## 0.3.1(2021-03-03) * Fixed classes for `row` layout object (#36) See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/4?closed=1) for full changelog. ## 0.3 (2021-02-21) * Fixed rendering of select widgets (#31) See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/3?closed=1) for full changelog. ## 0.2 (2021-01-31) * Tested for compatibility with Bootstrap5 Beta 1 * Fixed InlineField (#28) * Implemented new Bootstrap5 accordion (#24) * Improved tests and fixed rendering of blank attributes (#23) See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/2) for full changelog. ## 0.1 (2020-11-19) * Initial release, compatibility with Bootstrap5 Alpha 3 * Converted templates from Bootstrap 4, and initial set of fixes * Brought forward Bootstrap 4 test suite and updated for Bootstrap5 See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/1) for full changelog. crispy-bootstrap5-2024.2/LICENSE000066400000000000000000000020601456657663400162450ustar00rootroot00000000000000Copyright (c) 2020 David Smith and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.crispy-bootstrap5-2024.2/MANIFEST.in000066400000000000000000000001451456657663400170000ustar00rootroot00000000000000include LICENSE include MANIFEST.in include README.md recursive-include crispy_bootstrap5/templates *crispy-bootstrap5-2024.2/README.md000066400000000000000000000045771456657663400165360ustar00rootroot00000000000000# crispy-bootstrap5 [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/smithdc1/crispy-bootstrap5/blob/main/LICENSE) Bootstrap5 template pack for django-crispy-forms ## Installation Install this plugin using `pip`: ```bash $ pip install crispy-bootstrap5 ``` ## Usage You will need to update your project's settings file to add ``crispy_forms`` and ``crispy_bootstrap5`` to your projects ``INSTALLED_APPS``. Also set ``bootstrap5`` as and allowed template pack and as the default template pack for your project ```python INSTALLED_APPS = ( ... "crispy_forms", "crispy_bootstrap5", ... ) CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_TEMPLATE_PACK = "bootstrap5" ``` ## What's new? Bootstrap 5 introduces [floating labels](https://getbootstrap.com/docs/5.0/forms/floating-labels/). This template pack include a layout object to use this input type ```python from crispy_bootstrap5.bootstrap5 import FloatingField # then in your Layout ... Layout( FloatingField("first_name"), ) ``` Accordions also have new features, such as [Accordion flush](https://getbootstrap.com/docs/5.0/components/accordion/#flush) and [Always open](https://getbootstrap.com/docs/5.0/components/accordion/#always-open). There is a new layout object to use them ```python from crispy_bootstrap5.bootstrap5 import BS5Accordion # then in your Layout # if not informed, flush and always_open default to False ... Layout( BS5Accordion( AccordionGroup("group name", "form_field_1", "form_field_2"), AccordionGroup("another group name", "form_field"), flush=True, always_open=True ) ) ``` Support is added for [Switches](https://getbootstrap.com/docs/5.2/forms/checks-radios/#switches). Switches are a custom checkbox rendered as a toggle switch. The widget for these fields should be a [CheckboxInput](https://docs.djangoproject.com/en/4.2/ref/forms/widgets/#django.forms.CheckboxInput). ```python from crispy_bootstrap5.bootstrap5 import Switch ... Layout(Switch("is_company")) ``` ## Development To contribute to this library, first checkout the code. Then create a new virtual environment: ```bash cd crispy-bootstrap5 python -mvenv venv source venv/bin/activate ``` Or if you are using `pipenv`: ```bash pipenv shell ``` Now install the dependencies and tests: ```bash pip install -e '.[test]' ``` To run the tests: ```bash pytest ``` crispy-bootstrap5-2024.2/crispy_bootstrap5/000077500000000000000000000000001456657663400207355ustar00rootroot00000000000000crispy-bootstrap5-2024.2/crispy_bootstrap5/__init__.py000066400000000000000000000000271456657663400230450ustar00rootroot00000000000000__version__ = "2024.2" crispy-bootstrap5-2024.2/crispy_bootstrap5/bootstrap5.py000066400000000000000000000017441456657663400234170ustar00rootroot00000000000000from crispy_forms.bootstrap import Accordion from crispy_forms.layout import Field class FloatingField(Field): template = "bootstrap5/layout/floating_field.html" class BS5Accordion(Accordion): """ Bootstrap5 Accordion menu object. It wraps `AccordionGroup` objects in a container. It also allows the usage of accordion-flush, introduced in bootstrap5:: BS5Accordion( AccordionGroup("group name", "form_field_1", "form_field_2"), AccordionGroup("another group name", "form_field"), flush=True, always_open=True ) """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.flush = kwargs.pop("flush", False) self.always_open = kwargs.pop("always_open", False) if self.always_open: for accordion_group in self.fields: accordion_group.always_open = True class Switch(Field): template = "bootstrap5/layout/switch.html" crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/000077500000000000000000000000001456657663400227335ustar00rootroot00000000000000crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/000077500000000000000000000000001456657663400250355ustar00rootroot00000000000000crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/accordion-group.html000066400000000000000000000012441456657663400310170ustar00rootroot00000000000000

{{ fields|safe }}
crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/accordion.html000066400000000000000000000002051456657663400276610ustar00rootroot00000000000000
{{ content|safe }}
crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/betterform.html000066400000000000000000000012341456657663400300740ustar00rootroot00000000000000{% for fieldset in form.fieldsets %}
{% if fieldset.legend %} {{ fieldset.legend }} {% endif %} {% if fieldset.description %}

{{ fieldset.description }}

{% endif %} {% for field in fieldset %} {% if field.is_hidden %} {{ field }} {% else %} {% include "bootstrap5/field.html" %} {% endif %} {% endfor %} {% if not forloop.last or not fieldset_open %}
{% endif %} {% endfor %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/display_form.html000066400000000000000000000004101456657663400304060ustar00rootroot00000000000000{% if form.form_html %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap5/errors.html" %} {% endif %} {{ form.form_html }} {% else %} {% include "bootstrap5/uni_form.html" %} {% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/errors.html000066400000000000000000000004471456657663400272440ustar00rootroot00000000000000{% if form.non_field_errors %}
{% if form_error_title %}

{{ form_error_title }}

{% endif %}
    {{ form.non_field_errors|unordered_list }}
{% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/errors_formset.html000066400000000000000000000004621456657663400310000ustar00rootroot00000000000000{% if formset.non_form_errors %}
{% if formset_error_title %}

{{ formset_error_title }}

{% endif %}
    {{ formset.non_form_errors|unordered_list }}
{% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/field.html000066400000000000000000000101471456657663400270110ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field|is_checkbox and tag != "td" %}
{% if label_class %}
{% endif %} {% endif %} <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="{% if field|is_checkbox and form_show_labels %}form-check{% else %}mb-3{% if 'form-horizontal' in form_class %} row{% endif %}{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> {% if field.label and not field|is_checkbox and form_show_labels %} {% if field.use_fieldset %}{% endif %} <{% if field.use_fieldset %}legend{% else %}label{% endif %} {% if field.id_for_label %}for="{{ field.id_for_label }}"{% endif %} class="{% if 'form-horizontal' in form_class %}col-form-label pt-0{% else %}form-label{% endif %}{% if label_class %} {{ label_class }}{% endif %}{% if field.field.required %} requiredField{% endif %}"> {{ field.label }}{% if field.field.required %}*{% endif %} {% endif %} {% if field|is_checkboxselectmultiple or field|is_radioselect %} {% include 'bootstrap5/layout/radio_checkbox_select.html' %} {% endif %} {% if not field|is_checkboxselectmultiple and not field|is_radioselect %} {% if field|is_checkbox and form_show_labels %} {% if field.errors %} {% crispy_field field 'class' 'form-check-input is-invalid' %} {% else %} {% crispy_field field 'class' 'form-check-input' %} {% endif %} {% include 'bootstrap5/layout/help_text_and_errors.html' %} {% else %} {% if field_class %}
{% endif %} {% if field|is_file %} {% include 'bootstrap5/layout/field_file.html' %} {% elif field|is_select %} {% if field.errors %} {% crispy_field field 'class' 'form-select is-invalid' %} {% else %} {% crispy_field field 'class' 'form-select' %} {% endif %} {% elif field|is_checkbox %} {% if field.errors %} {% crispy_field field 'class' 'form-check-input is-invalid' %} {% else %} {% crispy_field field 'class' 'form-check-input' %} {% endif %} {% elif field.errors %} {% crispy_field field 'class' 'form-control is-invalid' %} {% else %} {% crispy_field field 'class' 'form-control' %} {% endif %} {% if not field|is_file %} {% include 'bootstrap5/layout/help_text_and_errors.html' %} {% endif %} {% if field_class %}
{% endif %} {% endif %} {% endif %} {% if field.use_fieldset and field.label and form_show_labels %}
{% endif %} {% if field|is_checkbox and tag != "td" %} {% if label_class %} {% endif %} {% endif %} {% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/inputs.html000066400000000000000000000006241456657663400272470ustar00rootroot00000000000000{% if inputs %}
{% if label_class %}
{% endif %}
{% for input in inputs %} {% include "bootstrap5/layout/baseinput.html" %} {% endfor %}
{% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/000077500000000000000000000000001456657663400263525ustar00rootroot00000000000000crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/alert.html000066400000000000000000000004711456657663400303510ustar00rootroot00000000000000crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/attrs.html000066400000000000000000000002541456657663400303760ustar00rootroot00000000000000{% for name, value in widget.attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/baseinput.html000066400000000000000000000006471456657663400312410ustar00rootroot00000000000000 crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/button.html000066400000000000000000000001031456657663400305450ustar00rootroot00000000000000 crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/buttonholder.html000066400000000000000000000003241456657663400317500ustar00rootroot00000000000000
{{ fields_output|safe }}
checkboxselectmultiple_inline.html000066400000000000000000000016161456657663400352650ustar00rootroot00000000000000crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout{% if field.is_hidden %} {{ field }} {% else %}
{% if field.label %} {{ field.label }}{% if field.field.required %}*{% endif %} {% endif %} {% include 'bootstrap5/layout/radio_checkbox_select.html' %} {% if field.label %}{% endif %}
{% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/column.html000066400000000000000000000003671456657663400305430ustar00rootroot00000000000000
{{ fields|safe }}
crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/div.html000066400000000000000000000002621456657663400300220ustar00rootroot00000000000000
{{ fields|safe }}
crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/field_errors.html000066400000000000000000000003541456657663400317210ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %} {{ error }} {% endfor %} {% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/field_errors_block.html000066400000000000000000000003461456657663400330740ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %}

{{ error }}

{% endfor %} {% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/field_file.html000066400000000000000000000027471456657663400313340ustar00rootroot00000000000000{% load crispy_forms_field %} {% for widget in field.subwidgets %} {% if widget.data.is_initial %}
{{ widget.data.initial_text }}
{{ field.value.name }} {% if not widget.data.required %} {% endif %}
{% endif %} {% include 'bootstrap5/layout/help_text_and_errors.html' %} {% endfor %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/field_with_buttons.html000066400000000000000000000033621456657663400331400ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.label and form_show_labels %} {% endif %}
{% if field|is_select %} {% if field.errors %} {% crispy_field field 'class' 'form-select is-invalid' %} {% else %} {% crispy_field field 'class' 'form-select' %} {% endif %} {% else %} {% if field.errors %} {% crispy_field field 'class' 'form-control is-invalid' %} {% else %} {% crispy_field field 'class' 'form-control' %} {% endif %} {% endif %} {{ buttons|safe }}
{% for error in field.errors %}

{{ error }}

{% endfor %} {% include 'bootstrap5/layout/help_text.html' %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/fieldset.html000066400000000000000000000004661456657663400310450ustar00rootroot00000000000000
{% if legend %}{{ legend|safe }}{% endif %} {{ fields|safe }}
crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/floating_field.html000066400000000000000000000023521456657663400322100ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="form-floating mb-3{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> {% if field|is_select %} {%if field.errors %} {% crispy_field field 'class' 'form-select is-invalid' 'placeholder' field.name %} {% else %} {% crispy_field field 'class' 'form-select' 'placeholder' field.name %} {% endif %} {% else %} {% if field.errors %} {% crispy_field field 'class' 'form-control is-invalid' 'placeholder' field.name %} {% else %} {% crispy_field field 'class' 'form-control' 'placeholder' field.name %} {% endif %} {% endif %} {% include 'bootstrap5/layout/help_text_and_errors.html' %} {% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/formactions.html000066400000000000000000000004101456657663400315570ustar00rootroot00000000000000 {% if label_class %}
{% endif %}
{{ fields_output|safe }}
crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/help_text.html000066400000000000000000000005271456657663400312400ustar00rootroot00000000000000{% if field.help_text %} {% if help_text_inline %} {{ field.help_text|safe}} {% else %}
{{ field.help_text|safe }}
{% endif %} {% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/help_text_and_errors.html000066400000000000000000000005761456657663400334620ustar00rootroot00000000000000{% if help_text_inline and not error_text_inline %} {% include 'bootstrap5/layout/help_text.html' %} {% endif %} {% if error_text_inline %} {% include 'bootstrap5/layout/field_errors.html' %} {% else %} {% include 'bootstrap5/layout/field_errors_block.html' %} {% endif %} {% if not help_text_inline %} {% include 'bootstrap5/layout/help_text.html' %} {% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/inline_field.html000066400000000000000000000020121456657663400316540ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field|is_checkbox %}
{% crispy_field field 'class' 'form-check-input' %}
{% else %}
{% if field.errors %} {% crispy_field field 'class' 'form-control is-invalid' 'placeholder' field.label %} {% else %} {% crispy_field field 'class' 'form-control' 'placeholder' field.label %} {% endif %}
{% endif %} {% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/modal.html000066400000000000000000000011051456657663400303310ustar00rootroot00000000000000 crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/multifield.html000066400000000000000000000010251456657663400313740ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field.label %} {% endif %} {% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/prepended_appended_text.html000066400000000000000000000045521456657663400341200ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %}
{% if field.label and form_show_labels %} {% endif %}
{# prepend #} {% if crispy_prepended_text %} {{ crispy_prepended_text }} {% endif %} {# input #} {% if field|is_select %} {% if field.errors %} {% crispy_field field 'class' 'form-select is-invalid' %} {% else %} {% crispy_field field 'class' 'form-select' %} {% endif %} {% elif field.errors %} {% crispy_field field 'class' 'form-control is-invalid' %} {% else %} {% crispy_field field 'class' 'form-control' %} {% endif %} {# append #} {% if crispy_appended_text %} {{ crispy_appended_text }} {% endif %} {% if error_text_inline %} {% include 'bootstrap5/layout/field_errors.html' %} {% else %} {% include 'bootstrap5/layout/field_errors_block.html' %} {% endif %}
{% if not help_text_inline %} {% include 'bootstrap5/layout/help_text.html' %} {% endif %}
{% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/radio_checkbox_select.html000066400000000000000000000023641456657663400335500ustar00rootroot00000000000000{% load crispy_forms_filters %} {% load l10n %}
{% for group, options, index in field|optgroups %} {% if group %}{{ group }}{% endif %} {% for option in options %}
{% if field.errors and forloop.last and not inline_class and forloop.parentloop.last %} {% include 'bootstrap5/layout/field_errors_block.html' %} {% endif %}
{% endfor %} {% endfor %}
{% if field.errors and inline_class %} {% for error in field.errors %}

{{ error }}

{% endfor %} {% endif %} {% include 'bootstrap5/layout/help_text.html' %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/radioselect_inline.html000066400000000000000000000016161456657663400331000ustar00rootroot00000000000000{% if field.is_hidden %} {{ field }} {% else %}
{% if field.label %} {{ field.label }}{% if field.field.required %}*{% endif %} {% endif %} {% include 'bootstrap5/layout/radio_checkbox_select.html' %} {% if field.label %}{% endif %}
{% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/row.html000066400000000000000000000002251456657663400300460ustar00rootroot00000000000000
{{ fields|safe }}
crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/switch.html000066400000000000000000000021701456657663400305410ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="mb-3 form-check form-switch{% if 'form-horizontal' in form_class %} row{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.errors %} {% crispy_field field 'class' 'form-check-input is-invalid' 'role' 'checkbox' %} {% else %} {% crispy_field field 'class' 'form-check-input' 'role' 'checkbox' %} {% endif %} {% include 'bootstrap5/layout/help_text_and_errors.html' %}
{% endif %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/tab-link.html000066400000000000000000000003161456657663400307410ustar00rootroot00000000000000 crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/tab.html000066400000000000000000000002551456657663400300100ustar00rootroot00000000000000 {{ links|safe }}
{{ content|safe }}
crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/layout/uneditable_input.html000066400000000000000000000012501456657663400325710ustar00rootroot00000000000000{% load crispy_forms_field %}
{% crispy_field field 'disabled' 'disabled' %} {% include 'bootstrap5/layout/help_text.html' %}
crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/table_inline_formset.html000066400000000000000000000040221456657663400321050ustar00rootroot00000000000000{% load crispy_forms_tags %} {% load crispy_forms_utils %} {% load crispy_forms_field %} {% specialspaceless %} {% if formset_tag %}
{% endif %} {% if formset_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %}
{{ formset.management_form|crispy }}
{% if formset.readonly and not formset.queryset.exists %} {% else %} {% for field in formset.forms.0 %} {% if field.label and not field.is_hidden %} {{ field.label }}{% if field.field.required and not field|is_checkbox %}*{% endif %} {% endif %} {% endfor %} {% endif %} {% for field in formset.empty_form %} {% include 'bootstrap5/field.html' with tag="td" form_show_labels=False %} {% endfor %} {% for form in formset %} {% if form_show_errors and not form.is_extra %} {% include "bootstrap5/errors.html" %} {% endif %} {% for field in form %} {% include 'bootstrap5/field.html' with tag="td" form_show_labels=False %} {% endfor %} {% endfor %} {% include "bootstrap5/inputs.html" %} {% if formset_tag %}{% endif %} {% endspecialspaceless %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/uni_form.html000066400000000000000000000004651456657663400275460ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap5/errors.html" %} {% endif %} {% for field in form %} {% include field_template %} {% endfor %} {% endspecialspaceless %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/uni_formset.html000066400000000000000000000003461456657663400302600ustar00rootroot00000000000000{% with formset.management_form as form %} {% include 'bootstrap5/uni_form.html' %} {% endwith %} {% for form in formset %}
{% include 'bootstrap5/uni_form.html' %}
{% endfor %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/whole_uni_form.html000066400000000000000000000007151456657663400307420ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if form_tag %}
{% endif %} {% if form_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %} {% include "bootstrap5/display_form.html" %} {% include "bootstrap5/inputs.html" %} {% if form_tag %}
{% endif %} {% endspecialspaceless %} crispy-bootstrap5-2024.2/crispy_bootstrap5/templates/bootstrap5/whole_uni_formset.html000066400000000000000000000015141456657663400314540ustar00rootroot00000000000000{% load crispy_forms_tags %} {% load crispy_forms_utils %} {% specialspaceless %} {% if formset_tag %}
{% endif %} {% if formset_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %}
{{ formset.management_form|crispy }}
{% include "bootstrap5/errors_formset.html" %} {% for form in formset %} {% include "bootstrap5/display_form.html" %} {% endfor %} {% if inputs %}
{% for input in inputs %} {% include "bootstrap5/layout/baseinput.html" %} {% endfor %}
{% endif %} {% if formset_tag %}
{% endif %} {% endspecialspaceless %} crispy-bootstrap5-2024.2/pyproject.toml000066400000000000000000000031131456657663400201540ustar00rootroot00000000000000[build-system] build-backend = "setuptools.build_meta" requires = [ "setuptools>=61", ] [project] name="crispy-bootstrap5" description="Bootstrap5 template pack for django-crispy-forms" readme = "README.md" license = {text = "MIT"} authors = [{name = "David Smith"}] requires-python = ">=3.8" classifiers=[ "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", ] dynamic = [ "version", ] dependencies = [ "django>=4.2", "django-crispy-forms>=2", ] [project.optional-dependencies] test = [ "pytest", "pytest-django", ] [project.urls] "CI" = "https://github.com/django-crispy-forms/crispy-bootstrap5/actions" "Changelog" = "https://github.com/django-crispy-forms/crispy-bootstrap5/releases" "Homepage" = "https://github.com/django-crispy-forms/crispy-bootstrap5" "Issues" = "https://github.com/django-crispy-forms/crispy-bootstrap5/issues" [tool.setuptools.dynamic] version = {attr = "crispy_bootstrap5.__version__"} [tool.isort] profile = "black" [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE= "tests.test_settings" crispy-bootstrap5-2024.2/test.html000066400000000000000000000167371456657663400171250ustar00rootroot00000000000000 Document
  • Passwords dont match
company email* password* re-enter password* first name* last name* date time*
Insert your email
This field is required.Insert your email This field is required. This field is required. This field is required. This field is required. This field is required.
crispy-bootstrap5-2024.2/tests/000077500000000000000000000000001456657663400164045ustar00rootroot00000000000000crispy-bootstrap5-2024.2/tests/__init__.py000066400000000000000000000000001456657663400205030ustar00rootroot00000000000000crispy-bootstrap5-2024.2/tests/forms.py000066400000000000000000000201401456657663400201010ustar00rootroot00000000000000from crispy_forms.helper import FormHelper from django import forms from django.db import models class SampleForm(forms.Form): is_company = forms.CharField( label="company", required=False, widget=forms.CheckboxInput() ) email = forms.EmailField( label="email", max_length=30, required=True, widget=forms.TextInput(), help_text="Insert your email", ) password1 = forms.CharField( label="password", max_length=30, required=True, widget=forms.PasswordInput() ) password2 = forms.CharField( label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput(), ) first_name = forms.CharField( label="first name", max_length=5, required=True, widget=forms.TextInput() ) last_name = forms.CharField( label="last name", max_length=5, required=True, widget=forms.TextInput() ) datetime_field = forms.SplitDateTimeField( label="date time", widget=forms.SplitDateTimeWidget() ) def clean(self): super().clean() password1 = self.cleaned_data.get("password1", None) password2 = self.cleaned_data.get("password2", None) if not password1 and not password2 or password1 != password2: raise forms.ValidationError("Passwords dont match") return self.cleaned_data class SampleForm2(SampleForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper(self) class CheckboxesSampleForm(forms.Form): checkboxes = forms.MultipleChoiceField( choices=((1, "Option one"), (2, "Option two"), (3, "Option three")), initial=(1,), widget=forms.CheckboxSelectMultiple, ) alphacheckboxes = forms.MultipleChoiceField( choices=( ("option_one", "Option one"), ("option_two", "Option two"), ("option_three", "Option three"), ), initial=("option_two", "option_three"), widget=forms.CheckboxSelectMultiple, ) numeric_multiple_checkboxes = forms.MultipleChoiceField( choices=((1, "Option one"), (2, "Option two"), (3, "Option three")), initial=(1, 2), widget=forms.CheckboxSelectMultiple, ) inline_radios = forms.ChoiceField( choices=( ("option_one", "Option one"), ("option_two", "Option two"), ), widget=forms.RadioSelect, initial="option_two", ) class CrispyTestModel(models.Model): email = models.CharField(max_length=20) password = models.CharField(max_length=20) class SampleForm3(forms.ModelForm): class Meta: model = CrispyTestModel fields = ["email", "password"] exclude = ["password"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper(self) class SampleForm4(forms.ModelForm): class Meta: """ before Django1.6, one cannot use __all__ shortcut for fields without getting the following error: django.core.exceptions.FieldError: Unknown field(s) (a, l, _) specified for CrispyTestModel because obviously it casts the string to a set """ model = CrispyTestModel fields = "__all__" # eliminate RemovedInDjango18Warning class SampleForm5(forms.Form): choices = [ (1, 1), (2, 2), (1000, 1000), ] checkbox_select_multiple = forms.MultipleChoiceField( widget=forms.CheckboxSelectMultiple, choices=choices ) radio_select = forms.ChoiceField(widget=forms.RadioSelect, choices=choices) pk = forms.IntegerField() class SampleFormWithMedia(forms.Form): class Media: css = {"all": ("test.css",)} js = ("test.js",) class SampleFormWithMultiValueField(forms.Form): multi = forms.SplitDateTimeField() class CrispyEmptyChoiceTestModel(models.Model): fruit = models.CharField( choices=[("apple", "Apple"), ("pear", "Pear")], null=True, blank=True, max_length=20, ) class SampleForm6(forms.ModelForm): class Meta: """ When allowing null=True in a model field, the corresponding field will have a choice for the empty value. When the form is initialized by an instance with initial value None, this choice should be selected. """ model = CrispyEmptyChoiceTestModel fields = ["fruit"] widgets = {"fruit": forms.RadioSelect()} class SampleForm7(forms.ModelForm): is_company = forms.CharField( label="company", required=False, widget=forms.CheckboxInput() ) password2 = forms.CharField( label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput(), ) class Meta: model = CrispyTestModel fields = ("email", "password", "password2") class SampleForm8(forms.ModelForm): is_company = forms.CharField( label="company", required=False, widget=forms.CheckboxInput() ) password2 = forms.CharField( label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput(), ) class Meta: model = CrispyTestModel fields = ("email", "password2", "password") class FakeFieldFile: """ Quacks like a FieldFile (has a .url and string representation), but doesn't require us to care about storages etc. """ url = "something" def __str__(self): return self.url class FileForm(forms.Form): file_field = forms.FileField(widget=forms.FileInput) clearable_file = forms.FileField( widget=forms.ClearableFileInput, required=False, initial=FakeFieldFile() ) class FileFormRequired(forms.Form): file_field = forms.FileField(required=True, widget=forms.FileInput) clearable_file = forms.FileField(required=True, widget=forms.ClearableFileInput) class InputsForm(forms.Form): choices = ((1, "Option one"), (2, "Option two"), (3, "Option three")) text_input = forms.CharField() text_area = forms.CharField(widget=forms.Textarea()) checkboxes = forms.MultipleChoiceField( choices=choices, initial=(1,), widget=forms.CheckboxSelectMultiple, ) radio = forms.ChoiceField(widget=forms.RadioSelect, choices=choices) single_checkbox = forms.CharField( label="company", required=False, widget=forms.CheckboxInput() ) select_input = forms.ChoiceField(choices=choices) class LabelForm(forms.Form): text_input = forms.CharField(label="Test html escape <>&") class CustomRadioSelect(forms.RadioSelect): pass class CustomCheckboxSelectMultiple(forms.CheckboxSelectMultiple): pass class SampleFormCustomWidgets(forms.Form): inline_radios = forms.ChoiceField( choices=( ("option_one", "Option one"), ("option_two", "Option two"), ), widget=CustomRadioSelect, initial="option_two", ) checkboxes = forms.MultipleChoiceField( choices=((1, "Option one"), (2, "Option two"), (3, "Option three")), initial=(1,), widget=CustomCheckboxSelectMultiple, ) class GroupedChoiceForm(forms.Form): choices = [ ( "Audio", [ ("vinyl", "Vinyl"), ("cd", "CD"), ], ), ( "Video", [ ("vhs", "VHS Tape"), ("dvd", "DVD"), ], ), ("unknown", "Unknown"), ] checkbox_select_multiple = forms.MultipleChoiceField( widget=forms.CheckboxSelectMultiple, choices=choices, help_text="help", ) radio = forms.MultipleChoiceField(widget=forms.RadioSelect, choices=choices) class HelpTextForm(forms.Form): email = forms.EmailField( label="email", required=True, widget=forms.TextInput(), help_text="Insert your email", ) class SelectForm(forms.Form): fruit = forms.ChoiceField( choices=[("apple", "Apple"), ("pear", "Pear")], widget=forms.Select, ) crispy-bootstrap5-2024.2/tests/results/000077500000000000000000000000001456657663400201055ustar00rootroot00000000000000crispy-bootstrap5-2024.2/tests/results/accordion.html000066400000000000000000000037471456657663400227470ustar00rootroot00000000000000

crispy-bootstrap5-2024.2/tests/results/accordion_always_open.html000066400000000000000000000036451456657663400253450ustar00rootroot00000000000000

crispy-bootstrap5-2024.2/tests/results/accordion_flush.html000066400000000000000000000037671456657663400241520ustar00rootroot00000000000000

crispy-bootstrap5-2024.2/tests/results/alert.html000066400000000000000000000004071456657663400221030ustar00rootroot00000000000000 crispy-bootstrap5-2024.2/tests/results/checkboxes.html000066400000000000000000000021521456657663400231110ustar00rootroot00000000000000
Checkboxes*
crispy-bootstrap5-2024.2/tests/results/field_with_buttons.html000066400000000000000000000011201456657663400246610ustar00rootroot00000000000000
Insert your email
crispy-bootstrap5-2024.2/tests/results/field_with_buttons_failing.html000066400000000000000000000011371456657663400263620ustar00rootroot00000000000000

This field is required.

crispy-bootstrap5-2024.2/tests/results/field_with_buttons_failing_lt50.html000066400000000000000000000011131456657663400272200ustar00rootroot00000000000000

This field is required.

crispy-bootstrap5-2024.2/tests/results/field_with_buttons_lt50.html000066400000000000000000000010531456657663400255320ustar00rootroot00000000000000
Insert your email
crispy-bootstrap5-2024.2/tests/results/field_with_buttons_select.html000066400000000000000000000005731456657663400262330ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/flat_attrs.html000066400000000000000000000000541456657663400231350ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/help_text_escape.html000066400000000000000000000006321456657663400243100ustar00rootroot00000000000000
Insert your email
crispy-bootstrap5-2024.2/tests/results/help_text_escape_lt50.html000066400000000000000000000005651456657663400251610ustar00rootroot00000000000000
Insert your email
crispy-bootstrap5-2024.2/tests/results/inline_checkboxes.html000066400000000000000000000022201456657663400244430ustar00rootroot00000000000000
Checkboxes*
crispy-bootstrap5-2024.2/tests/results/inline_checkboxes_failing.html000066400000000000000000000024341456657663400261430ustar00rootroot00000000000000
Checkboxes*

This field is required.

crispy-bootstrap5-2024.2/tests/results/inline_checkboxes_failing_lt50.html000066400000000000000000000023401456657663400270030ustar00rootroot00000000000000
Checkboxes*

This field is required.

crispy-bootstrap5-2024.2/tests/results/inline_radios.html000066400000000000000000000016371456657663400236210ustar00rootroot00000000000000
Inline radios*
crispy-bootstrap5-2024.2/tests/results/inline_radios_failing.html000066400000000000000000000020371456657663400253050ustar00rootroot00000000000000
Inline radios*

This field is required.

crispy-bootstrap5-2024.2/tests/results/inline_radios_failing_lt50.html000066400000000000000000000017671456657663400261620ustar00rootroot00000000000000
Inline radios*

This field is required.

crispy-bootstrap5-2024.2/tests/results/modal.html000066400000000000000000000011221456657663400220630ustar00rootroot00000000000000 crispy-bootstrap5-2024.2/tests/results/radio.html000066400000000000000000000024671456657663400221020ustar00rootroot00000000000000
Radio*
crispy-bootstrap5-2024.2/tests/results/row.html000066400000000000000000000015531456657663400216060ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/single_checkbox.html000066400000000000000000000005711456657663400241250ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/test_bootstrap5_form_inline.html000066400000000000000000000017241456657663400265210ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/test_bootstrap5_form_inline_lt50.html000066400000000000000000000016571456657663400273720ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/test_clearable_file_field.html000066400000000000000000000020331456657663400261040ustar00rootroot00000000000000
Currently
crispy-bootstrap5-2024.2/tests/results/test_clearable_file_field_failing.html000066400000000000000000000013641456657663400276030ustar00rootroot00000000000000
This field is required.
crispy-bootstrap5-2024.2/tests/results/test_clearable_file_field_failing_lt50.html000066400000000000000000000011331456657663400304410ustar00rootroot00000000000000
This field is required.
crispy-bootstrap5-2024.2/tests/results/test_file_field.html000066400000000000000000000006211456657663400241130ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/test_file_field_failing.html000066400000000000000000000013341456657663400256060ustar00rootroot00000000000000
This field is required.
crispy-bootstrap5-2024.2/tests/results/test_file_field_failing_lt50.html000066400000000000000000000011031456657663400264440ustar00rootroot00000000000000
This field is required.
crispy-bootstrap5-2024.2/tests/results/test_floating_field.html000066400000000000000000000005421456657663400250010ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/test_floating_field_failing.html000066400000000000000000000022261456657663400264730ustar00rootroot00000000000000
This field is required.
This field is required.
crispy-bootstrap5-2024.2/tests/results/test_floating_field_failing_lt50.html000066400000000000000000000021561456657663400273410ustar00rootroot00000000000000
This field is required.
This field is required.
crispy-bootstrap5-2024.2/tests/results/test_grouped_checkboxes.html000066400000000000000000000036431456657663400257030ustar00rootroot00000000000000
Checkbox select multiple*
Audio
Video
help
crispy-bootstrap5-2024.2/tests/results/test_grouped_checkboxes_failing.html000066400000000000000000000043611456657663400273720ustar00rootroot00000000000000
Checkbox select multiple*
Audio
Video

This field is required.

help
crispy-bootstrap5-2024.2/tests/results/test_grouped_checkboxes_failing_lt50.html000066400000000000000000000042151456657663400302340ustar00rootroot00000000000000
Checkbox select multiple*
Audio
Video

This field is required.

help
crispy-bootstrap5-2024.2/tests/results/test_grouped_radios.html000066400000000000000000000026711456657663400250460ustar00rootroot00000000000000
Radio*
Audio
Video
crispy-bootstrap5-2024.2/tests/results/test_grouped_radios_failing.html000066400000000000000000000040241456657663400265310ustar00rootroot00000000000000
Radio*
Audio
Video

This field is required.

crispy-bootstrap5-2024.2/tests/results/test_grouped_radios_failing_lt50.html000066400000000000000000000036601456657663400274020ustar00rootroot00000000000000
Radio*
Audio
Video

This field is required.

crispy-bootstrap5-2024.2/tests/results/test_inline_field.html000066400000000000000000000012711456657663400244540ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/test_prepended_appended_select.html000066400000000000000000000035421456657663400272030ustar00rootroot00000000000000
bar
bar
£ .00
crispy-bootstrap5-2024.2/tests/results/test_prepended_appended_text.html000066400000000000000000000030451456657663400267060ustar00rootroot00000000000000
@<>&gmail.com
Insert your email
#
$
crispy-bootstrap5-2024.2/tests/results/test_prepended_appended_text_lt50.html000066400000000000000000000030001456657663400275410ustar00rootroot00000000000000
@<>&gmail.com
Insert your email
#
$
crispy-bootstrap5-2024.2/tests/results/test_select.html000066400000000000000000000007331456657663400233140ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/test_switch.html000066400000000000000000000006771456657663400233450ustar00rootroot00000000000000
is_company help text
crispy-bootstrap5-2024.2/tests/results/test_switch_horizontal.html000066400000000000000000000015431456657663400256070ustar00rootroot00000000000000
is_company help text
crispy-bootstrap5-2024.2/tests/results/test_tabular_formset_layout.html000066400000000000000000000202701456657663400266210ustar00rootroot00000000000000
company email* password* re-enter password* first name* last name* date time*
Insert your email
Insert your email
Insert your email
Insert your email
crispy-bootstrap5-2024.2/tests/results/test_tabular_formset_layout_failing.html000066400000000000000000000154351456657663400303210ustar00rootroot00000000000000
  • Passwords dont match
company email* password* re-enter password* first name* last name* date time*
Insert your email
This field is required.
Insert your email
This field is required. This field is required. This field is required. This field is required. This field is required.
crispy-bootstrap5-2024.2/tests/results/test_tabular_formset_layout_failing_lt50.html000066400000000000000000000150571456657663400311650ustar00rootroot00000000000000
  • Passwords dont match
company email* password* re-enter password* first name* last name* date time*
Insert your email
This field is required.
Insert your email
This field is required. This field is required. This field is required. This field is required. This field is required.
crispy-bootstrap5-2024.2/tests/results/test_tabular_formset_layout_lt50.html000066400000000000000000000177141456657663400274760ustar00rootroot00000000000000
company email* password* re-enter password* first name* last name* date time*
Insert your email
Insert your email
Insert your email
Insert your email
crispy-bootstrap5-2024.2/tests/results/text_area.html000066400000000000000000000006031456657663400227460ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/results/text_input.html000066400000000000000000000006151456657663400232000ustar00rootroot00000000000000
crispy-bootstrap5-2024.2/tests/templates/000077500000000000000000000000001456657663400204025ustar00rootroot00000000000000crispy-bootstrap5-2024.2/tests/templates/crispy_render_template.html000066400000000000000000000000601456657663400260270ustar00rootroot00000000000000{% load crispy_forms_tags %} {% crispy form %} crispy-bootstrap5-2024.2/tests/templates/custom_field_template.html000066400000000000000000000001041456657663400256330ustar00rootroot00000000000000

Special custom field

{% include 'bootstrap5/field.html' %} crispy-bootstrap5-2024.2/tests/templates/custom_form_template.html000066400000000000000000000001141456657663400255140ustar00rootroot00000000000000

Special custom form

{% include "bootstrap5/whole_uni_form.html" %} crispy-bootstrap5-2024.2/tests/templates/custom_form_template_with_context.html000066400000000000000000000002311456657663400303130ustar00rootroot00000000000000

Special custom form with context passthrough

Got prefix: {{ prefix }}. {% include "bootstrap5/whole_uni_form.html" %} Got suffix: {{ suffix }}. crispy-bootstrap5-2024.2/tests/test_form_helper.py000066400000000000000000000467051456657663400223330ustar00rootroot00000000000000import re import pytest from crispy_forms.bootstrap import ( AppendedText, FieldWithButtons, PrependedAppendedText, PrependedText, StrictButton, ) from crispy_forms.helper import FormHelper, FormHelpersException from crispy_forms.layout import Button, Hidden, Layout, Reset, Submit from crispy_forms.templatetags.crispy_forms_tags import CrispyFormNode from crispy_forms.utils import render_crispy_form from django import forms from django.forms.models import formset_factory from django.middleware.csrf import _get_new_csrf_string from django.template import Context, Template from django.urls import reverse from django.utils.translation import gettext_lazy as _ from .forms import SampleForm, SampleForm7, SampleForm8, SampleFormWithMedia def test_inputs(): form_helper = FormHelper() form_helper.add_input(Submit("my-submit", "Submit", css_class="button white")) form_helper.add_input(Reset("my-reset", "Reset")) form_helper.add_input(Hidden("my-hidden", "Hidden")) form_helper.add_input(Button("my-button", "Button")) template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": SampleForm(), "form_helper": form_helper}) html = template.render(c) assert "button white" in html assert 'id="submit-id-my-submit"' in html assert 'id="reset-id-my-reset"' in html assert 'name="my-hidden"' in html assert 'id="button-id-my-button"' in html assert 'class="btn"' in html assert "btn btn-primary" in html assert "btn btn-inverse" in html assert len(re.findall(r"]+> <", html)) == 9 def test_invalid_form_method(): form_helper = FormHelper() with pytest.raises(FormHelpersException): form_helper.form_method = "superPost" def test_form_with_helper_without_layout(settings): form_helper = FormHelper() form_helper.form_id = "this-form-rocks" form_helper.form_class = "forms-that-rock" form_helper.form_method = "GET" form_helper.form_action = "simpleAction" form_helper.form_error_title = "ERRORS" template = Template( """ {% load crispy_forms_tags %} {% crispy testForm form_helper %} """ ) # now we render it, with errors form = SampleForm({"password1": "wargame", "password2": "god"}) form.is_valid() c = Context({"testForm": form, "form_helper": form_helper}) html = template.render(c) # Lets make sure everything loads right assert html.count("Passwords dont match" in html # now lets remove the form tag and render it again. All the True items above # should now be false because the form tag is removed. form_helper.form_tag = False html = template.render(c) assert "Passwords dont match" in html assert str(_("This field is required.")) in html assert "error" in html # Now we render without errors form.helper.form_show_errors = False c = Context({"testForm": form}) html = template.render(c) # Ensure errors were not rendered assert "
  • Passwords dont match
  • " not in html assert str(_("This field is required.")) not in html assert "error" not in html def test_html5_required(): form = SampleForm() form.helper = FormHelper() form.helper.html5_required = True html = render_crispy_form(form) # 6 out of 7 fields are required and an extra one for the # SplitDateTimeWidget makes 7. assert len(re.findall(r"\brequired\b", html)) == 7 form = SampleForm() form.helper = FormHelper() form.helper.html5_required = False html = render_crispy_form(form) def test_media_is_included_by_default_with_bootstrap5(): form = SampleFormWithMedia() form.helper = FormHelper() form.helper.template_pack = "bootstrap5" html = render_crispy_form(form) assert "test.css" in html assert "test.js" in html def test_media_removed_when_include_media_is_false_with_bootstrap5(): form = SampleFormWithMedia() form.helper = FormHelper() form.helper.template_pack = "bootstrap5" form.helper.include_media = False html = render_crispy_form(form) assert "test.css" not in html assert "test.js" not in html def test_attrs(): form = SampleForm() form.helper = FormHelper() form.helper.attrs = {"id": "TestIdForm", "autocomplete": "off"} html = render_crispy_form(form) assert 'autocomplete="off"' in html assert 'id="TestIdForm"' in html def test_template_context(): helper = FormHelper() helper.attrs = { "id": "test-form", "class": "test-forms", "action": "submit/test/form", "autocomplete": "off", } node = CrispyFormNode("form", "helper") context = node.get_response_dict(helper, {}, False) assert context["form_id"] == "test-form" assert context["form_attrs"]["id"] == "test-form" assert "test-forms" in context["form_class"] assert "test-forms" in context["form_attrs"]["class"] assert context["form_action"] == "submit/test/form" assert context["form_attrs"]["action"] == "submit/test/form" assert context["form_attrs"]["autocomplete"] == "off" def test_template_context_using_form_attrs(): helper = FormHelper() helper.form_id = "test-form" helper.form_class = "test-forms" helper.form_action = "submit/test/form" node = CrispyFormNode("form", "helper") context = node.get_response_dict(helper, {}, False) assert context["form_id"] == "test-form" assert context["form_attrs"]["id"] == "test-form" assert "test-forms" in context["form_class"] assert "test-forms" in context["form_attrs"]["class"] assert context["form_action"] == "submit/test/form" assert context["form_attrs"]["action"] == "submit/test/form" def test_template_helper_access(): helper = FormHelper() helper.form_id = "test-form" assert helper["form_id"] == "test-form" def test_without_helper(): template = Template( """ {% load crispy_forms_tags %} {% crispy form %} """ ) c = Context({"form": SampleForm()}) html = template.render(c) # Lets make sure everything loads right assert "') assert contains_partial(html, '') def test_render_required_fields(): test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout("email") test_form.helper.render_required_fields = True html = render_crispy_form(test_form) assert html.count("Special custom form" in html def test_helper_custom_field_template(): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout("password1", "password2") form.helper.field_template = "custom_field_template.html" html = render_crispy_form(form) assert html.count("

    Special custom field

    ") == 2 def test_helper_custom_field_template_no_layout(): form = SampleForm() form.helper = FormHelper() form.helper.field_template = "custom_field_template.html" html = render_crispy_form(form) for field in form.fields: assert html.count('id="div_id_%s"' % field) == 1 assert html.count("

    Special custom field

    ") == len(form.fields) def test_helper_std_field_template_no_layout(): form = SampleForm() form.helper = FormHelper() html = render_crispy_form(form) for field in form.fields: assert html.count('id="div_id_%s"' % field) == 1 def test_bootstrap_form_show_errors_bs5(): form = SampleForm( { "email": "invalidemail", "first_name": "first_name_too_long", "last_name": "last_name_too_long", "password1": "yes", "password2": "yes", } ) form.helper = FormHelper() form.helper.layout = Layout( AppendedText("email", "whatever"), PrependedText("first_name", "blabla"), PrependedAppendedText("last_name", "foo", "bar"), AppendedText("password1", "whatever"), PrependedText("password2", "blabla"), ) form.is_valid() form.helper.form_show_errors = True html = render_crispy_form(form) assert html.count("error") == 3 form.helper.form_show_errors = False html = render_crispy_form(form) assert html.count("error") == 0 def test_error_text_inline(): form = SampleForm({"email": "invalidemail"}) form.helper = FormHelper() layout = Layout( AppendedText("first_name", "wat"), PrependedText("email", "@"), PrependedAppendedText("last_name", "@", "wat"), ) form.helper.layout = layout form.is_valid() html = render_crispy_form(form) help_class = "invalid-feedback" help_tag_name = "div" matches = re.findall( r'') error_position = html.find('

    ') assert help_position < error_position # Viceversa form = SampleForm({"email": "invalidemail"}) form.helper = FormHelper() form.helper.error_text_inline = True form.helper.help_text_inline = False form.helper.layout = Layout("email") form.is_valid() html = render_crispy_form(form) # Check that error goes before help, otherwise CSS won't work error_position = html.find('') help_position = html.find('

    ') assert error_position < help_position def test_form_show_labels(): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout( "password1", FieldWithButtons("password2", StrictButton("Confirm")), PrependedText("first_name", "Mr."), AppendedText("last_name", "@"), PrependedAppendedText("datetime_field", "on", "secs"), ) form.helper.form_show_labels = False html = render_crispy_form(form) assert html.count("' in html assert '
    ' in html assert html.count("col-lg-8") == 7 # TODO FIX THIS TEST # assert "offset" not in html form.helper.label_class = "col-sm-3 col-md-4" form.helper.field_class = "col-sm-8 col-md-6" html = render_crispy_form(form) assert '
    ' in html assert '
    ' in html assert html.count("col-sm-8") == 7 # TODO FIX THIS TEST # assert "offset" not in html def test_label_class_and_field_class_bs5_offset_when_horizontal(): # Test col-XX-YY pattern form = SampleForm() form.helper = FormHelper() form.helper.label_class = "col-lg-2" form.helper.field_class = "col-lg-8" form.helper.form_class = "form-horizontal" html = render_crispy_form(form) assert '
    ' in html assert '
    ' in html assert html.count("col-lg-8") == 7 # Test multi col-XX-YY pattern and col-X pattern form.helper.label_class = "col-sm-3 col-md-4 col-5 col-lg-4" form.helper.field_class = "col-sm-8 col-md-6 col-7 col-lg-8" html = render_crispy_form(form) assert '
    ' in html assert ( '
    ' in html ) assert html.count("col-sm-8") == 7 assert html.count("col-md-6") == 7 assert html.count("col-7") == 7 assert html.count("col-lg-8") == 7 def test_form_group_with_form_inline_bs5(): form = SampleForm() form.helper = FormHelper() html = render_crispy_form(form) assert '
    ' in html # .row class shouldn't be together with .form-group in inline forms form = SampleForm() form.helper = FormHelper() form.helper.form_class = "form-inline" form.helper.field_template = "bootstrap5/layout/inline_field.html" html = render_crispy_form(form) assert '
    ' not in html def test_passthrough_context(): """ Test to ensure that context is passed through implicitly from outside of the crispy form into the crispy form templates. """ form = SampleForm() form.helper = FormHelper() form.helper.template = "custom_form_template_with_context.html" c = {"prefix": "foo", "suffix": "bar"} html = render_crispy_form(form, helper=form.helper, context=c) assert "Got prefix: foo" in html assert "Got suffix: bar" in html crispy-bootstrap5-2024.2/tests/test_layout.py000066400000000000000000000467251456657663400213500ustar00rootroot00000000000000import django import pytest from crispy_forms.bootstrap import ( AppendedText, Field, FieldWithButtons, InlineCheckboxes, PrependedAppendedText, PrependedText, StrictButton, ) from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Column, Fieldset, Layout, Row from crispy_forms.utils import render_crispy_form from django import forms from django.forms.models import formset_factory, modelformset_factory from django.middleware.csrf import _get_new_csrf_string from django.shortcuts import render from django.template import Context, Template from django.test import override_settings from django.urls import reverse from django.utils.translation import gettext_lazy as _ from .forms import ( CheckboxesSampleForm, CrispyEmptyChoiceTestModel, CrispyTestModel, FileForm, FileFormRequired, HelpTextForm, InputsForm, LabelForm, SampleForm, SampleForm2, SampleForm3, SampleForm4, SampleForm6, SelectForm, ) from .utils import contains_partial, parse_expected, parse_form CONVERTERS = { "textinput": "inputtext textinput textInput", "fileinput": "fileinput fileUpload", "passwordinput": "textinput textInput", } def test_invalid_unicode_characters(settings): # Adds a BooleanField that uses non valid unicode characters "ñ" form_helper = FormHelper() form_helper.add_layout(Layout("españa")) template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": SampleForm(), "form_helper": form_helper}) settings.CRISPY_FAIL_SILENTLY = False with pytest.raises(Exception): template.render(c) def test_unicode_form_field(): class UnicodeForm(forms.Form): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["contraseña"] = forms.CharField() helper = FormHelper() helper.layout = Layout("contraseña") html = render_crispy_form(UnicodeForm()) assert 'id="id_contraseña"' in html def test_meta_extra_fields_with_missing_fields(): class FormWithMeta(SampleForm): class Meta: fields = ("email", "first_name", "last_name") form = FormWithMeta() # We remove email field on the go del form.fields["email"] form_helper = FormHelper() form_helper.layout = Layout("first_name") template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": form, "form_helper": form_helper}) html = template.render(c) assert "email" not in html def test_layout_unresolved_field(settings): form_helper = FormHelper() form_helper.add_layout(Layout("typo")) template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": SampleForm(), "form_helper": form_helper}) settings.CRISPY_FAIL_SILENTLY = False with pytest.raises(Exception): template.render(c) def test_double_rendered_field(settings): form_helper = FormHelper() form_helper.add_layout(Layout("is_company", "is_company")) template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": SampleForm(), "form_helper": form_helper}) settings.CRISPY_FAIL_SILENTLY = False with pytest.raises(Exception): template.render(c) def test_context_pollution(): class ExampleForm(forms.Form): comment = forms.CharField() form = ExampleForm() form2 = SampleForm() template = Template( """ {% load crispy_forms_tags %} {{ form.as_ul }} {% crispy form2 %} {{ form.as_ul }} """ ) c = Context({"form": form, "form2": form2}) html = template.render(c) assert html.count('name="comment"') == 2 assert html.count('name="is_company"') == 1 def test_layout_fieldset_row_html_with_unicode_fieldnames(): form_helper = FormHelper() form_helper.add_layout( Layout( Fieldset( "Company Data", "is_company", css_id="fieldset_company_data", css_class="fieldsets", title="fieldset_title", test_fieldset="123", ), Fieldset( "User Data", "email", Row( "password1", "password2", css_id="row_passwords", css_class="rows", ), HTML('test link'), HTML( """ {% if flag %}{{ message }}{% endif %} """ ), "first_name", "last_name", ), ) ) template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context( { "form": SampleForm(), "form_helper": form_helper, "flag": True, "message": "Hello!", } ) html = template.render(c) assert 'id="fieldset_company_data"' in html assert 'class="fieldsets' in html assert 'title="fieldset_title"' in html assert 'test-fieldset="123"' in html assert 'id="row_passwords"' in html assert html.count("click me"""), ) ), ) ) if django.VERSION < (5, 0): expected = "field_with_buttons_lt50.html" else: expected = "field_with_buttons.html" assert parse_form(form) == parse_expected(expected) form = SampleForm3({}) form.helper = FormHelper() form.helper.add_layout( Layout( Column( FieldWithButtons( "email", StrictButton("Go!", css_class="btn-outline-secondary") ) ), ) ) if django.VERSION < (5, 0): expected = "field_with_buttons_failing_lt50.html" else: expected = "field_with_buttons_failing.html" assert parse_form(form) == parse_expected(expected) def test_field_with_buttons_select(): form = SelectForm() form.helper = FormHelper() assert parse_form(form) == parse_expected("field_with_buttons_select.html") @override_settings(CRISPY_CLASS_CONVERTERS=CONVERTERS) def test_formset_layout(): SampleFormSet = formset_factory(SampleForm, extra=3) formset = SampleFormSet() helper = FormHelper() helper.form_id = "thisFormsetRocks" helper.form_class = "formsets-that-rock" helper.form_method = "POST" helper.form_action = "simpleAction" helper.layout = Layout( Fieldset( "Item {{ forloop.counter }}", "is_company", "email", ), HTML("{% if forloop.first %}Note for first form only{% endif %}"), Row("password1", "password2"), Fieldset("", "first_name", "last_name"), ) html = render_crispy_form( form=formset, helper=helper, context={"csrf_token": _get_new_csrf_string()} ) # Check formset fields assert contains_partial( html, '', ) assert contains_partial( html, '', ) assert contains_partial( html, '', ) assert contains_partial( html, '', ) assert html.count("hidden") == 5 # Check form structure assert html.count("', ) assert contains_partial( html, '', ) assert contains_partial( html, '', ) assert html.count('name="form-0-email"') == 1 assert html.count('name="form-1-email"') == 1 assert html.count('name="form-2-email"') == 1 assert html.count('name="form-3-email"') == 0 assert html.count("password") == 0 def test_i18n(): template = Template( """ {% load crispy_forms_tags %} {% crispy form form.helper %} """ ) form = SampleForm() form_helper = FormHelper() form_helper.layout = Layout( HTML(_("i18n text")), Fieldset( _("i18n legend"), "first_name", "last_name", ), ) form.helper = form_helper html = template.render(Context({"form": form})) assert html.count("i18n legend") == 1 def test_default_layout(): test_form = SampleForm2() assert test_form.helper.layout.fields == [ "is_company", "email", "password1", "password2", "first_name", "last_name", "datetime_field", ] def test_default_layout_two(): test_form = SampleForm3() assert test_form.helper.layout.fields == ["email"] def test_modelform_layout_without_meta(): test_form = SampleForm4() test_form.helper = FormHelper() test_form.helper.layout = Layout("email") html = render_crispy_form(test_form) assert "email" in html assert "password" not in html def test_specialspaceless_not_screwing_intended_spaces(): # see issue #250 test_form = SampleForm() test_form.fields["email"].widget = forms.Textarea() test_form.helper = FormHelper() test_form.helper.layout = Layout( "email", HTML("first span second span") ) html = render_crispy_form(test_form) assert "first span second span" in html def test_choice_with_none_is_selected(): # see issue #701 model_instance = CrispyEmptyChoiceTestModel() model_instance.fruit = None test_form = SampleForm6(instance=model_instance) html = render_crispy_form(test_form) assert "checked" in html def test_keepcontext_context_manager(): # Test case for issue #180 # Apparently it only manifest when using render_to_response this exact way form = CheckboxesSampleForm() form.helper = FormHelper() # We use here InlineCheckboxes as it updates context in an unsafe way form.helper.layout = Layout( "checkboxes", InlineCheckboxes("alphacheckboxes"), "numeric_multiple_checkboxes" ) context = {"form": form} response = render( request=None, template_name="crispy_render_template.html", context=context ) print(response.content) assert response.content.count(b"form-check-inline") == 3 assert response.content.count(b"form-check-input") > 0 @override_settings(CRISPY_CLASS_CONVERTERS=CONVERTERS) def test_bootstrap5_form_inline(): form = SampleForm() form.helper = FormHelper() form.helper.form_class = "form-inline" form.helper.field_template = "bootstrap5/layout/inline_field.html" form.helper.layout = Layout("email", "password1", "last_name") form.helper.form_class = "row row-cols-lg-auto" if django.VERSION < (5, 0): expected = "test_bootstrap5_form_inline_lt50.html" else: expected = "test_bootstrap5_form_inline.html" assert parse_form(form) == parse_expected(expected) def test_select(): form = InputsForm() form.helper = FormHelper() form.helper.layout = Layout("select_input") assert parse_form(form) == parse_expected("test_select.html") def test_select_prepended(): form = InputsForm() form.helper = FormHelper() form.helper.layout = Layout( PrependedText("select_input", "bar"), AppendedText("select_input", "bar"), PrependedAppendedText("select_input", "£", ".00"), ) assert parse_form(form) == parse_expected("test_prepended_appended_select.html") def test_update_attributes_class(): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout("email", Field("password1"), "password2") form.helper["password1"].update_attributes(css_class="hello") html = render_crispy_form(form) assert html.count(' class="hello') == 1 form.helper = FormHelper() form.helper.layout = Layout( "email", Field("password1", css_class="hello"), "password2", ) form.helper["password1"].update_attributes(css_class="hello2") html = render_crispy_form(form) assert html.count(' class="hello hello2') == 1 def test_file_field(): form = FileForm() form.helper = FormHelper() form.helper.layout = Layout("clearable_file") assert parse_form(form) == parse_expected("test_clearable_file_field.html") form.helper.layout = Layout("file_field") assert parse_form(form) == parse_expected("test_file_field.html") form = FileFormRequired({}) form.helper = FormHelper() form.helper.layout = Layout("clearable_file") if django.VERSION < (5, 0): expected = "test_clearable_file_field_failing_lt50.html" else: expected = "test_clearable_file_field_failing.html" assert parse_form(form) == parse_expected(expected) form.helper.layout = Layout("file_field") if django.VERSION < (5, 0): expected = "test_file_field_failing_lt50.html" else: expected = "test_file_field_failing.html" assert parse_form(form) == parse_expected(expected) def test_row(): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout( Row( Column("first_name"), Column("last_name"), ) ) assert parse_form(form) == parse_expected("row.html") def test_html_label_escape(): form = LabelForm() form.helper = FormHelper() form.helper.layout = Layout("text_input") html = render_crispy_form(form) assert "<>&" in html @override_settings(CRISPY_CLASS_CONVERTERS=CONVERTERS) def test_tabular_formset_layout(): SampleFormSet = formset_factory(SampleForm, extra=3) formset = SampleFormSet() formset.helper = FormHelper() formset.helper.template = "bootstrap5/table_inline_formset.html" if django.VERSION < (5, 0): expected = "test_tabular_formset_layout_lt50.html" else: expected = "test_tabular_formset_layout.html" assert parse_form(formset) == parse_expected(expected) SampleFormSet = formset_factory(SampleForm, extra=3) data = { "form-TOTAL_FORMS": "1", "form-INITIAL_FORMS": "0", } formset = SampleFormSet(data) formset.helper = FormHelper() formset.helper.template = "bootstrap5/table_inline_formset.html" if django.VERSION < (5, 0): expected = "test_tabular_formset_layout_failing_lt50.html" else: expected = "test_tabular_formset_layout_failing.html" assert parse_form(formset) == parse_expected(expected) def test_flat_attrs_safe(): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout( Row( aria_labelledby="test<>%", ) ) form.helper.form_tag = False assert parse_form(form) == parse_expected("flat_attrs.html") def test_help_text_no_escape(): form = HelpTextForm() form.helper = FormHelper() form.helper.form_tag = False if django.VERSION < (5, 0): expected = "help_text_escape_lt50.html" else: expected = "help_text_escape.html" assert parse_form(form) == parse_expected(expected) crispy-bootstrap5-2024.2/tests/test_layout_objects.py000066400000000000000000000523301456657663400230460ustar00rootroot00000000000000import random import django import pytest from crispy_forms.bootstrap import ( Accordion, AccordionGroup, Alert, AppendedText, FieldWithButtons, InlineCheckboxes, InlineField, InlineRadios, Modal, PrependedAppendedText, PrependedText, StrictButton, Tab, TabHolder, ) from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Field, Layout, MultiWidgetField from crispy_forms.utils import render_crispy_form from django import forms from django.template import Context, Template from django.test import override_settings from django.utils.translation import activate, deactivate from django.utils.translation import gettext as _ from crispy_bootstrap5.bootstrap5 import BS5Accordion, FloatingField, Switch from .forms import ( CheckboxesSampleForm, CustomCheckboxSelectMultiple, CustomRadioSelect, GroupedChoiceForm, InputsForm, SampleForm, SampleFormCustomWidgets, ) from .utils import parse_expected, parse_form CONVERTERS = { "textinput": "inputtext textinput textInput", "fileinput": "fileinput fileUpload", "passwordinput": "textinput textInput", } def test_field_with_custom_template(): test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( Field("email", template="custom_field_template.html") ) html = render_crispy_form(test_form) assert "

    Special custom field

    " in html def test_multiwidget_field(): template = Template( """ {% load crispy_forms_tags %} {% crispy form %} """ ) test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( MultiWidgetField( "datetime_field", attrs=( {"rel": "test_dateinput"}, {"rel": "test_timeinput", "style": "width: 30px;", "type": "hidden"}, ), ) ) c = Context({"form": test_form}) html = template.render(c) assert html.count('class="dateinput') == 1 assert html.count('rel="test_dateinput"') == 1 assert html.count('rel="test_timeinput"') == 2 assert html.count('style="width: 30px;"') == 2 assert html.count('type="hidden"') == 2 def test_field_type_hidden(): template = Template( """ {% load crispy_forms_tags %} {% crispy test_form %} """ ) test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( Field("email", type="hidden", data_test=12), Field("datetime_field"), ) c = Context({"test_form": test_form}) html = template.render(c) # Check form parameters assert html.count('data-test="12"') == 1 assert html.count('name="email"') == 1 assert html.count('class="dateinput') == 1 assert html.count('class="timeinput') == 1 def test_field_wrapper_class(settings): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout(Field("email", wrapper_class="testing")) html = render_crispy_form(form) if settings.CRISPY_TEMPLATE_PACK == "bootstrap": assert html.count('class="control-group testing"') == 1 elif settings.CRISPY_TEMPLATE_PACK in ("bootstrap3", "bootstrap4"): assert html.count('class="form-group testing"') == 1 def test_html_with_carriage_returns(settings): test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( HTML( """ if (a==b){ // some comment a+1; foo(); } """ ) ) html = render_crispy_form(test_form) if settings.CRISPY_TEMPLATE_PACK == "uni_form": assert html.count("\n") == 23 elif settings.CRISPY_TEMPLATE_PACK == "bootstrap": assert html.count("\n") == 25 else: assert html.count("\n") == 27 def test_i18n(): activate("es") form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout(HTML(_("Enter a valid value."))) html = render_crispy_form(form) assert "Introduzca un valor válido" in html deactivate() def test_remove_labels(): form = SampleForm() # remove boolean field as label is still printed in boostrap del form.fields["is_company"] for fields in form: fields.label = False html = render_crispy_form(form) assert "&", "gmail.com", css_class="form-control-lg" ), AppendedText("password1", "#"), PrependedText("password2", "$"), ) if django.VERSION < (5, 0): expected = "test_prepended_appended_text_lt50.html" else: expected = "test_prepended_appended_text.html" assert parse_form(test_form) == parse_expected(expected) def test_inline_radios(self): form = CheckboxesSampleForm() form.helper = FormHelper() form.helper.layout = Layout(InlineRadios("inline_radios")) assert parse_form(form) == parse_expected("inline_radios.html") def test_inline_checkboxes(self): form = CheckboxesSampleForm() form.helper = FormHelper() form.helper.layout = InlineRadios("checkboxes") assert parse_form(form) == parse_expected("inline_checkboxes.html") def test_inline_radios_failing(self): form = CheckboxesSampleForm({}) form.helper = FormHelper() form.helper.layout = Layout(InlineRadios("inline_radios")) if django.VERSION < (5, 0): expected = "inline_radios_failing_lt50.html" else: expected = "inline_radios_failing.html" assert parse_form(form) == parse_expected(expected) def test_inline_checkboxes_failing(self): form = CheckboxesSampleForm({}) form.helper = FormHelper() form.helper.layout = InlineRadios("checkboxes") if django.VERSION < (5, 0): expected = "inline_checkboxes_failing_lt50.html" else: expected = "inline_checkboxes_failing.html" assert parse_form(form) == parse_expected(expected) @override_settings(CRISPY_CLASS_CONVERTERS=CONVERTERS) def test_accordion_and_accordiongroup(self): random.seed(0) form = SampleForm() form.helper = FormHelper() form.helper.form_tag = False form.helper.layout = Layout( Accordion( AccordionGroup("one", "first_name"), AccordionGroup("two", "password1", "password2"), ) ) assert parse_form(form) == parse_expected("accordion.html") def test_accordion_active_false_not_rendered(self): test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( Accordion( AccordionGroup("one", "first_name"), # there is no ``active`` kwarg here. ) ) # The first time, there should be one of them there. html = render_crispy_form(test_form) accordion_class = "collapse show" assert ( html.count('
    " ) == 1 ) assert html.count("tab-pane") == 2 assert html.count('class="tab-pane first-tab-class active"') == 1 assert html.count('
    everything is fine test_form = SampleForm() html = render_crispy_form(test_form) # second render of form => first tab should be active, # but not duplicate class test_form = SampleForm() html = render_crispy_form(test_form) assert html.count('class="nav-item active active"') == 0 # render a new form, now with errors test_form = SampleForm(data={"val1": "foo"}) html = render_crispy_form(test_form) tab_class = "tab-pane" # if settings.CRISPY_TEMPLATE_PACK == 'bootstrap4': # tab_class = 'nav-link' # else: # tab_class = 'tab-pane' # tab 1 should not be active assert html.count('