Skip to content

Feat: notification.add_action#4819

Open
evnchn wants to merge 5 commits intozauberzeug:mainfrom
evnchn:notification-actions
Open

Feat: notification.add_action#4819
evnchn wants to merge 5 commits intozauberzeug:mainfrom
evnchn:notification-actions

Conversation

@evnchn
Copy link
Copy Markdown
Collaborator

@evnchn evnchn commented May 31, 2025

Motivation

Addresses NiceGUI Wishlist (view), how we had to leverage kwargs to define actions for notifications.

Implementation

  • I strongly disagree with the original proposed ui.notification_action, since:
    • It is not a UI element
    • We can't leverage the expansive .on method, which covers both Python handler and JavaScript handler
  • Therefore, I chose for notification.add_action, which lets adding a button (yep the action is a QBtn definition, check Quasar docs) which triggers an event when clicked
  • Then, we can leverage .on to listen to the event we just defined.
  • To enable Quasar, Tailwind and CSS colors, I stole the logic from mixins.color_elements, but now since it is not an element but just a dictionary, we update it a bit differently.

Progress

  • I chose a meaningful title that completes the sentence: "If applied, this PR will..."
  • The implementation is complete.
  • Pytests have been added (or are not necessary).
  • Documentation has been added (or is not necessary).

-> Do we need pytest? If so, tell me and I'll take a shot at it...


Feature request: #4514

@evnchn evnchn added feature Type/scope: New or intentionally changed behavior review Status: PR is open and needs review ⚪️ minor Priority: Low impact, nice-to-have labels May 31, 2025
@falkoschindler falkoschindler self-requested a review May 31, 2025 20:26
@falkoschindler falkoschindler added this to the 3.9 milestone Feb 16, 2026
Copy link
Copy Markdown
Contributor

@falkoschindler falkoschindler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pull request, @evnchn! Now I finally looked into it more closely. After merging from main and fixing some obvious linter errors there are still several unresolved issues:

  • Mypy isn't happy with action['class'] = f'text-{color} ' + action.get('class', '') because the class could be any type. Same for "style".
  • But I don't see the point of adding action.get('class', '') because it should be always undefined. Same for "style".
  • Why should we ask the user for an event name and not provide a handler parameter directly? I think js_handler and other on parameters aren't needed for now.

And here is Claude's review:


BLOCKER

1. assert used for input validation (notification.py:227)

assert '"' not in event_name, 'Event names must not contain double quotes (")'

assert statements are stripped when Python runs with -O (optimize). This is the only guard against injection into the :handler JS string. Should be a proper ValueError:

- assert '"' not in event_name, 'Event names must not contain double quotes (")'
+ if '"' in event_name:
+     raise ValueError('Event names must not contain double quotes (")')

2. Insufficient sanitization of event_name — potential JS injection (notification.py:235)

The handler string is:

f'() => getElement({self.id}).$emit("{event_name}")'

Only " is blocked. But backslashes and other characters could break out of the string context. For example, event_name = 'x\\")+alert(1)//' or crafted sequences with backslash escapes. Consider allowlisting (e.g., re.match(r'^[a-zA-Z0-9_-]+$', event_name)) rather than blocklisting a single character.

3. Missing tests

The PR checklist marks tests as not done. For a new public API method on a core element, tests are needed — at minimum verifying the actions dict structure, color handling for all three branches (Quasar/Tailwind/CSS), the no_dismiss flag, and that invalid event names are rejected.


MAJOR

4. kwargs can silently overwrite color-derived keys (notification.py:243)

action.update(kwargs)

If a user passes class_='some-class' or style='...' via kwargs, it will overwrite the Tailwind class or CSS style set by the color logic just above. The color handling should either merge with kwargs or run after update(kwargs). For example:

action.update(kwargs)
# Then apply color on top, or merge rather than overwrite

5. icon: None is always included in the action dict (notification.py:234)

When icon is None, it's still sent as 'icon': None. The notification constructor itself skips the key when icon is None (line 86-87). For consistency and to avoid sending unnecessary props to Quasar:

  action = {
      'noDismiss': no_dismiss,
      'label': text,
-     'icon': icon,
      ':handler': f'() => getElement({self.id}).$emit("{event_name}")',
  }
+ if icon is not None:
+     action['icon'] = icon

6. Documentation claim is inaccurate

The demo docstring says "It takes the same arguments as ui.button" but it doesn't — it takes text, color, icon, and no_dismiss, plus Quasar QBtn kwargs. The ui.button API is much broader (with on_click, validation, etc.). This could mislead users.


CLEANUP

7. label parameter naming

The parameter is called text but maps to 'label' in the dict. NiceGUI's ui.button also uses text and maps to label internally, so this is consistent. Just noting it's fine.

8. The setdefault + append pattern could be simplified

self._props['options'].setdefault('actions', [])
# ...
self._props['options']['actions'].append(action)

Could be one line shorter but it's readable as-is — no action needed.

@falkoschindler falkoschindler modified the milestones: 3.9, 3.x Mar 12, 2026
…tization and tests

- Replace event_name param with on_click callback (auto-generates internal event names)
- Remove js_handler param (not needed per review)
- Apply kwargs before color logic so color-derived class/style cannot be overwritten
- Only include icon in action dict when not None
- Fix docstring to accurately describe parameters
- Add 11 tests covering structure, colors, no_dismiss, chaining, kwargs ordering
- Update demo to use new on_click API

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Type/scope: New or intentionally changed behavior ⚪️ minor Priority: Low impact, nice-to-have review Status: PR is open and needs review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants