Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/unfold/templates/unfold/helpers/fieldset_row.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="{% fieldset_row_classes %} {% if forloop.last %}last{% endif %}">
{% for field in line %}
{% with adminform.model_admin.conditional_fields|index:field.field.name as conditional_display %}
<div class="{% fieldset_line_classes %}" {% if has_conditional_display and conditional_display %}x-show="{{ conditional_display }}"{% endif %}>
<div class="{% fieldset_line_classes %}" {% if has_conditional_display and conditional_display %}x-show="{{ conditional_display }}" x-effect="$el.querySelectorAll('input, select, textarea').forEach(el => {el.disabled = !({{ conditional_display }})})"{% endif %}>
{% if has_conditional_display %}
{% with field|changeform_condition as field %}
{% if field.is_checkbox %}
Expand Down
3 changes: 2 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from pytest_factoryboy import register

from .factories import TagFactory, UserFactory
from .factories import ConditionalFieldsTestModelFactory, TagFactory, UserFactory
from .fixtures import * # noqa: F403

register(TagFactory)
register(UserFactory)
register(ConditionalFieldsTestModelFactory)
12 changes: 11 additions & 1 deletion tests/factories.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from example.models import Tag, User
import factory
from example.models import ConditionalFieldsTestModel, Tag, User
from factory.django import DjangoModelFactory


Expand All @@ -10,3 +11,12 @@ class Meta:
class TagFactory(DjangoModelFactory):
class Meta:
model = Tag


class ConditionalFieldsTestModelFactory(DjangoModelFactory):
class Meta:
model = ConditionalFieldsTestModel

name = factory.Faker("name")
conditional_field_active = factory.Faker("name")
conditional_field_inactive = factory.Faker("name")
18 changes: 17 additions & 1 deletion tests/server/example/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from unfold.forms import AdminPasswordChangeForm, UserChangeForm, UserCreationForm
from unfold.sections import TableSection, TemplateSection

from .models import ActionUser, SectionUser, Tag, User
from .models import ActionUser, ConditionalFieldsTestModel, SectionUser, Tag, User

admin.site.unregister(Group)

Expand Down Expand Up @@ -52,6 +52,22 @@ class SectionUserAdmin(UserAdmin):
]


@admin.register(ConditionalFieldsTestModel)
class ConditionalFieldsModelAdmin(ModelAdmin):
list_display = ["name", "status"]
fields = [
"name",
"status",
"conditional_field_active",
"conditional_field_inactive",
]

conditional_fields = {
"conditional_field_active": 'status === "ACTIVE"',
"conditional_field_inactive": 'status === "INACTIVE"',
}


@admin.register(ActionUser)
class ActionsUserAdmin(BaseUserAdmin, ModelAdmin):
form = UserChangeForm
Expand Down
51 changes: 51 additions & 0 deletions tests/server/example/migrations/0005_conditionalfieldstestmodel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Generated by Django 4.2.22 on 2025-08-26 07:42

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("example", "0004_actionuser_sectionuser"),
]

operations = [
migrations.CreateModel(
name="ConditionalFieldsTestModel",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=100)),
(
"status",
models.CharField(
choices=[("ACTIVE", "Active"), ("INACTIVE", "Inactive")],
default="ACTIVE",
max_length=10,
),
),
(
"conditional_field_active",
models.CharField(
blank=True,
help_text="This field is only visible when status is ACTIVE",
max_length=100,
),
),
(
"conditional_field_inactive",
models.CharField(
blank=True,
help_text="This field is only visible when status is INACTIVE",
max_length=100,
),
),
],
),
]
22 changes: 22 additions & 0 deletions tests/server/example/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,25 @@ class Tag(models.Model):

def __str__(self):
return self.name


class ConditionalFieldsTestModel(models.Model):
STATUS_CHOICES = (
("ACTIVE", "Active"),
("INACTIVE", "Inactive"),
)
name = models.CharField(max_length=100)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default="ACTIVE")
conditional_field_active = models.CharField(
max_length=100,
blank=True,
help_text="This field is only visible when status is ACTIVE",
)
conditional_field_inactive = models.CharField(
max_length=100,
blank=True,
help_text="This field is only visible when status is INACTIVE",
)

def __str__(self):
return self.name
34 changes: 34 additions & 0 deletions tests/test_conditional_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import re
from http import HTTPStatus

import pytest
from django.urls import reverse

from tests.factories import ConditionalFieldsTestModelFactory


@pytest.mark.django_db
def test_conditional_fields_in_context(admin_client):
"""Test that conditional fields are properly included in the admin form context."""
conditional_fields_test_instance = ConditionalFieldsTestModelFactory(
status="ACTIVE",
conditional_field_active="Active Value",
conditional_field_inactive="Inactive Value",
)
change_url = reverse(
"admin:example_conditionalfieldstestmodel_change",
args=[conditional_fields_test_instance.pk],
)

response = admin_client.get(change_url)

assert response.status_code == HTTPStatus.OK
content = response.content.decode()
# Test that the x-bind:disabled directive is present to disable hidden fields
assert re.search(
r'x-bind:disabled="!\(status\s*===\s*&quot;ACTIVE&quot;\)"', content
)
assert re.search(
r'x-bind:disabled="!\(status\s*===\s*&quot;INACTIVE&quot;\)"', content
)
# TODO: test that the fields are disabled (at init & on update)