Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
pix666 committed Jan 2, 2024
2 parents 26964a6 + e89a5ef commit 96c5887
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## [0.5.1](https://github.com/dldevinc/paper-forms/tree/v0.5.1) - 2024-01-03

- Now you can set the `error_css_class` and `required_css_class` using Composer.

## [0.5.0](https://github.com/dldevinc/paper-forms/tree/v0.5.0) - 2024-01-02

### ⚠ BREAKING CHANGES
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ class ExampleForm(forms.Form):
Here, the `Composer` class provides labels and help text for the "name" and "age" fields,
offering clear instructions to users interacting with your forms.

Additionally, developers can enhance the customization of the form's appearance
by utilizing the `error_css_class` and `required_css_class` attributes
within the `Composer` class. These attributes allow you to define specific CSS classes
for handling errors and indicating required fields, respectively. Notably, any values
set for these attributes in the `Composer` class take precedence over those specified
at the form level.

### Specifying Custom Template Names

When using `paper-forms`, you have the flexibility to create custom templates for
Expand Down
2 changes: 1 addition & 1 deletion paper_forms/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.5.0"
__version__ = "0.5.1"
24 changes: 24 additions & 0 deletions paper_forms/boundfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,30 @@ def get_context(

return context

def css_classes(self, extra_classes=None):
if hasattr(extra_classes, "split"):
extra_classes = extra_classes.split()

# Remove duplicates while maintaining the order.
seen = set()
extra_classes = [
class_name
for class_name in (extra_classes or [])
if class_name not in seen and not seen.add(class_name)
]

if self.errors:
if self.composer.error_css_class:
extra_classes.append(self.composer.error_css_class)
elif hasattr(self.form, "error_css_class"):
extra_classes.append(self.form.error_css_class)
if self.field.required:
if self.composer.required_css_class:
extra_classes.append(self.composer.required_css_class)
elif hasattr(self.form, "required_css_class"):
extra_classes.append(self.form.required_css_class)
return " ".join(extra_classes)

@cached_property
def widget(self) -> Widget:
widget = self.composer.get_widget(self.name)
Expand Down
14 changes: 8 additions & 6 deletions paper_forms/composer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import copy
from typing import Any, Optional
from typing import Any, Optional, ClassVar

from django.forms import BaseForm
from django.forms.renderers import BaseRenderer
Expand All @@ -23,11 +23,13 @@ def __call__(cls, *args, **kwargs):

class BaseComposer(metaclass=SingletonMeta):
renderer = None
widgets: dict[str, Any] = None
labels: dict[str, str] = None
help_texts: dict[str, str] = None
css_classes: dict[str, str] = None
template_names: dict[str, str] = None
error_css_class: ClassVar[str] = None
required_css_class: ClassVar[str] = None
widgets: ClassVar[dict[str, Any]] = None
labels: ClassVar[dict[str, str]] = None
help_texts: ClassVar[dict[str, str]] = None
css_classes: ClassVar[dict[str, str]] = None
template_names: ClassVar[dict[str, str]] = None

def get_renderer(self, form: BaseForm) -> BaseRenderer:
renderer = self.renderer or form.default_renderer or conf.DEFAULT_FORM_RENDERER
Expand Down
38 changes: 38 additions & 0 deletions tests/tests/test_boundfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,41 @@ class MyForm(Form):
"css_classes": "",
"errors": [],
}


class TestCssClasses:
def test_get_values_from_composer(self):
class MyForm(Form):
error_css_class = "This will be overriden"
required_css_class = "This will be overriden"

name = CharField(
max_length=64,
)

class Composer(BaseComposer):
error_css_class = "invalid"
required_css_class = "required"

# Bound form
form = MyForm({})

bf = get_boundfield(form, "name", Composer())
css_classes = bf.css_classes()
assert css_classes == "invalid required"

def test_get_values_from_form(self):
class MyForm(Form):
error_css_class = "invalid"
required_css_class = "required"

name = CharField(
max_length=64,
)

# Bound form
form = MyForm({})

bf = get_boundfield(form, "name", BaseComposer())
css_classes = bf.css_classes()
assert css_classes == "invalid required"

0 comments on commit 96c5887

Please sign in to comment.