You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by ar...@apache.org on 2026/04/14 15:46:59 UTC

(tooling-trusted-releases) 02/04: #776 - Add 50k limit to vote comment and add nav link to jump back to form

This is an automated email from the ASF dual-hosted git repository.

arm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git

commit bd5510006c7c3529b26165b32d684a3ad6c4e419
Author: Alastair McFarlane <ar...@apache.org>
AuthorDate: Tue Apr 14 10:46:04 2026 +0100

    #776 - Add 50k limit to vote comment and add nav link to jump back to form
---
 atr/form.py        | 21 ++++++++++++++-------
 atr/get/vote.py    |  2 +-
 atr/shared/vote.py |  2 +-
 3 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/atr/form.py b/atr/form.py
index 18c31277..57317323 100644
--- a/atr/form.py
+++ b/atr/form.py
@@ -138,16 +138,17 @@ def flash_error_summary(errors: list[pydantic_core.ErrorDetails], flash_data: di
     div = htm.Block(htm.div, classes=".atr-initial")
     div.text(f"Please fix the following {util.plural(len(errors), 'issue', include_count=False)}:")
     with div.block(htm.ul, classes=".mt-2.mb-0") as ul:
-        for i, flash_datum in enumerate(flash_data.values()):
+        for i, (field_name, flash_datum) in enumerate(flash_data.items()):
             if i > 9:
                 ul.li["And more, not shown here..."]
                 break
             if "msg" in flash_datum:
                 label = flash_datum["label"]
+                jump = htm.a(href=f"#{field_name}", class_="ms-1 text-decoration-none small")["\u2193"]
                 if label == "*":
-                    ul.li[flash_datum["msg"]]
+                    ul.li[flash_datum["msg"], " ", jump]
                 else:
-                    ul.li[htm.strong[label], ": ", flash_datum["msg"]]
+                    ul.li[htm.strong[label], ": ", flash_datum["msg"], " ", jump]
     summary = div.collect()
     return markupsafe.Markup(summary)
 
@@ -164,7 +165,13 @@ def json_suitable(field_value: Any) -> Any:
 
 
 def label(
-    description: str, documentation: str | None = None, *, default: Any = ..., widget: Widget | None = None, **kwargs
+    description: str,
+    documentation: str | None = None,
+    *,
+    default: Any = ...,
+    widget: Widget | None = None,
+    max_length: int | None = None,
+    **kwargs,
 ) -> Any:
     extra: dict[str, Any] = {}
     if widget is not None:
@@ -173,7 +180,7 @@ def label(
         extra["documentation"] = documentation
     if len(kwargs) > 0:
         extra.update(kwargs)
-    return pydantic.Field(default, description=description, json_schema_extra=extra)
+    return pydantic.Field(default, description=description, json_schema_extra=extra, max_length=max_length)
 
 
 def name_and_label(form_cls: type[Form], i: int, loc: tuple[str | int, ...]) -> tuple[str, str]:
@@ -938,7 +945,7 @@ def _render_widget(  # noqa: C901
                 checkbox_label = htpy.label(for_=checkbox_id, class_="form-check-label")[label]
                 checkboxes.append(htpy.div(class_="form-check")[checkbox_input, checkbox_label])
             elements.extend(checkboxes)
-            widget = htm.div[checkboxes]
+            widget = htm.div(id=field_name)[checkboxes]
 
         case Widget.CUSTOM:
             if custom and (field_name in custom):
@@ -997,7 +1004,7 @@ def _render_widget(  # noqa: C901
                 radio_label = htpy.label(for_=radio_id, class_="form-check-label")[label]
                 radios.append(htpy.div(class_="form-check")[radio_input, radio_label])
             elements.extend(radios)
-            widget = htm.div[radios]
+            widget = htm.div(id=field_name)[radios]
 
         case Widget.SELECT:
             # Check for dynamic choices from defaults or field_value
diff --git a/atr/get/vote.py b/atr/get/vote.py
index be0a98be..5d4c4bc1 100644
--- a/atr/get/vote.py
+++ b/atr/get/vote.py
@@ -574,7 +574,7 @@ async def _render_vote_authenticated(
         page.p["Your vote will be sent to ", htpy.code[mailing_list], "."]
 
     # Build the vote widget
-    vote_widget = htpy.div(class_="btn-group", role="group")[
+    vote_widget = htpy.div(id="decision", class_="btn-group", role="group")[
         htpy.input(type="radio", class_="btn-check", name="decision", id="decision_0", value="+1", autocomplete="off"),
         htpy.label(class_="btn btn-outline-success", for_="decision_0")[f"+1 ({potency})"],
         htpy.input(type="radio", class_="btn-check", name="decision", id="decision_1", value="0", autocomplete="off"),
diff --git a/atr/shared/vote.py b/atr/shared/vote.py
index e634293b..660dd898 100644
--- a/atr/shared/vote.py
+++ b/atr/shared/vote.py
@@ -24,7 +24,7 @@ import atr.storage as storage
 
 class CastVoteForm(form.Form):
     decision: Literal["+1", "0", "-1"] = form.label("Your vote", widget=form.Widget.CUSTOM)
-    comment: str = form.label("Comment (optional)", widget=form.Widget.TEXTAREA)
+    comment: str = form.label("Comment (optional)", widget=form.Widget.TEXTAREA, max_length=50_000)
 
 
 async def is_binding(


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org