-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement rich radio buttons DSFR component
- Loading branch information
1 parent
121843e
commit f4d87da
Showing
4 changed files
with
409 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import functools | ||
from typing import Any | ||
|
||
from django.db import models | ||
from django.db.models import TextChoices | ||
from django.utils.safestring import mark_safe | ||
from django.utils.version import PY311 | ||
|
||
if PY311: | ||
from enum import property as enum_property | ||
else: | ||
from types import DynamicClassAttribute as enum_property | ||
|
||
|
||
class _ExtendedChoicesType(models.enums.ChoicesType): | ||
def __new__(metacls, classname, bases, classdict, **kwds): | ||
dynamic_attributes = {} | ||
for member in classdict._member_names: | ||
value = classdict[member] | ||
if isinstance(value, dict): | ||
value = value.copy() | ||
if "value" not in value: | ||
raise ValueError( | ||
"enum value for {member} should contain member 'value' " | ||
"when using a dict as value; got {member} = {value}".format( | ||
member=member, value=repr(value) | ||
) | ||
) | ||
|
||
dict.__setitem__(classdict, member, metacls.get_value_from_dict(value)) | ||
value.pop("value") | ||
value.pop("label", None) | ||
|
||
for k, v in value.items(): | ||
if metacls.is_sunder(k) or metacls.is_dunder(k): | ||
raise ValueError( | ||
( | ||
"enum value for {member} contains key {key}. " | ||
"Names surrounded with single or double underscores are " | ||
"not authorized as dict values" | ||
).format(member=member, key=k) | ||
) | ||
dynamic_attributes.setdefault(k, {}) | ||
dynamic_attributes[k][member] = v | ||
|
||
classdict._last_values = [ | ||
metacls.get_value_from_dict(item) for item in classdict._last_values | ||
] | ||
|
||
cls = super().__new__(metacls, classname, bases, classdict, **kwds) | ||
|
||
metacls.set_dynamic_attributes(cls, dynamic_attributes) | ||
|
||
return cls | ||
|
||
@staticmethod | ||
def set_dynamic_attributes(cls, dynamic_attributes: dict[str, dict[str, Any]]): | ||
cls.NO_VALUE = object() | ||
|
||
for k, v in dynamic_attributes.items(): | ||
variable = "_{}_".format(k) | ||
for instance in cls: | ||
if hasattr(instance, variable): | ||
raise ValueError( | ||
( | ||
"Can't set {} on {} members; please choose a different name " | ||
"or remove from the member value" | ||
).format(variable, cls.__name__) | ||
) | ||
setattr(instance, variable, v.get(instance.name, cls.NO_VALUE)) | ||
|
||
def _getter(name, self): | ||
result = getattr(self, name, cls.NO_VALUE) | ||
if result is cls.NO_VALUE: | ||
raise AttributeError( | ||
"{} not present in {}.{}".format( | ||
variable, cls.__name__, self.name | ||
) | ||
) | ||
return result | ||
|
||
setattr(cls, k, enum_property(functools.partial(_getter, variable))) | ||
|
||
@staticmethod | ||
def get_value_from_dict(value): | ||
if not isinstance(value, dict): | ||
return value | ||
elif "label" in value: | ||
return value["value"], value["label"] | ||
else: | ||
return value["value"] | ||
|
||
@staticmethod | ||
def is_dunder(name): | ||
""" | ||
Returns True if a __dunder__ name, False otherwise. | ||
""" | ||
return ( | ||
len(name) > 4 | ||
and name[:2] == name[-2:] == "__" | ||
and name[2] != "_" | ||
and name[-3] != "_" | ||
) | ||
|
||
@staticmethod | ||
def is_sunder(name): | ||
""" | ||
Returns True if a _sunder_ name, False otherwise. | ||
""" | ||
return ( | ||
len(name) > 2 | ||
and name[0] == name[-1] == "_" | ||
and name[1:2] != "_" | ||
and name[-2:-1] != "_" | ||
) | ||
|
||
|
||
class ExtendedChoices(models.Choices, metaclass=_ExtendedChoicesType): | ||
... | ||
|
||
|
||
class RichRadioButtonChoices(ExtendedChoices, TextChoices): | ||
@enum_property | ||
def pictogram(self): | ||
return self._pictogram_ if hasattr(self, "_pictogram_") else "" | ||
|
||
@enum_property | ||
def html_label(self): | ||
return ( | ||
mark_safe(self._html_label_) | ||
if hasattr(self, "_html_label_") | ||
else self.label | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.