Skip to content

Commit a60ef04

Browse files
authored
Add base classes for rich choice select fields (#12524)
This will be used by the project create organization and team selection first. - Refs readthedocs/ext-theme#468
1 parent e1d4b3b commit a60ef04

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

readthedocs/core/forms.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Forms for core app."""
22

3+
from dataclasses import dataclass
4+
35
import structlog
46
from django import forms
57
from django.conf import settings
@@ -163,6 +165,61 @@ def __init__(self, message, code=None, params=None, header=None, message_class=N
163165
self.message_class = message_class
164166

165167

168+
@dataclass
169+
class RichChoice:
170+
"""
171+
Data class for rich content dropdowns.
172+
173+
Instead of just value and text, :py:class:`RichSelect` displays multiple
174+
attributes in each item content display. Choices can be passed as an array,
175+
however with the default :py:class:`django.forms.fields.ChoiceField`
176+
you should still pass in a tuple of ``(value, RichChoice(...))`` as the
177+
tuple values are used in field validation:
178+
179+
choices = [
180+
RichChoice(name="Foo", value="foo", ...),
181+
RichChoice(name="Bar", value="bar", ...),
182+
]
183+
field = forms.ChoiceField(
184+
...,
185+
widget=RichSelect(),
186+
choices=[(choice.value, choice) for choice in choices],
187+
)
188+
"""
189+
190+
#: Choice verbose text display
191+
text: str
192+
#: Choice input value
193+
value: str
194+
#: Right floated content for dropdown item
195+
description: str
196+
#: Optional image URL for item
197+
image_url: str = None
198+
#: Optional image alt text
199+
image_alt: str = None
200+
#: Error string to display next to text
201+
error: str = None
202+
#: Is choice disabled?
203+
disabled: bool = False
204+
205+
206+
class RichSelect(forms.Select):
207+
"""
208+
Rich content dropdown field widget type used for complex content.
209+
210+
This class is mostly used for special casing in Crispy form templates, it
211+
doesn't do anything special. This widget type requires use of the
212+
:py:class:`RichChoice` data class. Usage might look something comparable to:
213+
214+
choice = RichChoice(...)
215+
field = forms.ChoiceField(
216+
...,
217+
widget=RichSelect(),
218+
choices=[(choice.value, choice)]
219+
)
220+
"""
221+
222+
166223
class FacetField(forms.MultipleChoiceField):
167224
"""
168225
For filtering searches on a facet.

readthedocs/core/templatetags/core_tags.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from django.utils.safestring import mark_safe
88

99
from readthedocs import __version__
10+
from readthedocs.core.forms import RichSelect
1011
from readthedocs.core.resolver import Resolver
1112
from readthedocs.projects.models import Project
1213

@@ -102,3 +103,9 @@ def escapejson(data, indent=None):
102103
indent=indent,
103104
).translate(_json_script_escapes)
104105
)
106+
107+
108+
@register.filter
109+
def is_rich_select(field):
110+
"""Field type comparison used by Crispy form templates."""
111+
return isinstance(field.field.widget, RichSelect)

0 commit comments

Comments
 (0)