Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow adding JS callbacks in ChatInterface.button_properties #6706

Merged
merged 5 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions examples/reference/chat/ChatInterface.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@
" * The provided callbacks should have a signature that accepts\n",
"two positional arguments: instance (the ChatInterface instance)\n",
"and event (the button click event).\n",
" * The `js_on_click` key should be a string of JavaScript code\n",
"to execute when the button is clicked. The `js_args` key\n",
"should be a dictionary of arguments to pass to the JavaScript\n",
" * The `js_on_click` key should be a dict, with a\n",
"`code` key, containing the JavaScript code\n",
"to execute when the button is clicked, and `args` key,\n",
"containing dictionary of arguments to pass to the JavaScript\n",
"code.\n",
"\n",
"##### Styling\n",
Expand Down Expand Up @@ -494,7 +495,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"You may also use custom Javascript code with `js_on_click` for the buttons, and also set the `button_properties` after definition.\n",
"You may also use custom Javascript code with `js_on_click` containing `code` and `args` keys for the buttons, and also set the `button_properties` after definition.\n",
"\n",
"Try typing something in the chat input, and then click the new `Help` button on the bottom right."
]
Expand All @@ -506,11 +507,13 @@
"outputs": [],
"source": [
"chat_interface = pn.chat.ChatInterface()\n",
"chat_interface.button_properties={\n",
"chat_interface.button_properties = {\n",
" \"help\": {\n",
" \"icon\": \"help\",\n",
" \"js_on_click\": \"alert(`Typed: '${chat_input.value}'`)\",\n",
" \"js_args\": {\"chat_input\": chat_interface.active_widget},\n",
" \"js_on_click\": {\n",
" \"code\": \"alert(`Typed: '${chat_input.value}'`)\",\n",
" \"args\": {\"chat_input\": chat_interface.active_widget},\n",
" },\n",
" },\n",
"}\n",
"chat_interface"
Expand Down
31 changes: 19 additions & 12 deletions panel/chat/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,8 @@ class _ChatButtonData:
The buttons to display.
callback : Callable
The callback to execute when the button is clicked.
js_on_click : str | None
The JavaScript to execute when the button is clicked.
js_args : Dict[str, Any] | None
The JavaScript arguments to pass when the button is clicked.
js_on_click : dict | None
The JavaScript `code` and `args` to execute when the button is clicked.
"""

index: int
Expand All @@ -59,8 +57,7 @@ class _ChatButtonData:
objects: List
buttons: List
callback: Callable
js_on_click: str | None = None
js_args: Dict[str, Any] | None = None
js_on_click: dict | None = None


class ChatInterface(ChatFeed):
Expand Down Expand Up @@ -137,9 +134,10 @@ class ChatInterface(ChatFeed):
two positional arguments: instance (the ChatInterface instance)
and event (the button click event).

The `js_on_click` key should be a string of JavaScript code
to execute when the button is clicked. The `js_args` key
should be a dictionary of arguments to pass to the JavaScript
The `js_on_click` key should be a dict, with a
`code` key, containing the JavaScript code
to execute when the button is clicked, and `args` key,
containing dictionary of arguments to pass to the JavaScript
code.
""")

Expand Down Expand Up @@ -251,7 +249,6 @@ def _init_widgets(self):
buttons=[],
callback=callback,
js_on_click=js_on_click,
js_args=properties.get("js_args"),
)

widgets = self.widgets
Expand Down Expand Up @@ -324,9 +321,19 @@ def _init_widgets(self):
callback = partial(button_data.callback, self)
button.on_click(callback)
if button_data.js_on_click:
if not isinstance(button_data.js_on_click, dict):
raise ValueError(
f"The 'js_on_click' key for the {action!r} button must be a dict "
f"containing 'code' and 'args' keys, but got {button_data.js_on_click!r}"
)
elif "code" not in button_data.js_on_click:
raise ValueError(
f"A 'code' key is required for the {action!r} button's "
"'js_on_click' key"
)
button.js_on_click(
args=(button_data.js_args or {}),
code=button_data.js_on_click
args=(button_data.js_on_click.get("args") or {}),
code=button_data.js_on_click["code"],
)
self._buttons[action] = button
button_data.buttons.append(button)
Expand Down
22 changes: 22 additions & 0 deletions panel/tests/chat/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,28 @@ def post_callback(instance, event):
assert chat_interface.objects[1].object == "2"
assert chat_interface.objects[2].object == "3"

def test_custom_js_no_code(self):
chat_interface = ChatInterface()
with pytest.raises(ValueError, match="A 'code' key is required for"):
chat_interface.button_properties={
"help": {
"icon": "help",
"js_on_click": {
"args": {"chat_input": chat_interface.active_widget},
},
},
}

def test_custom_js_not_dict(self):
ahuang11 marked this conversation as resolved.
Show resolved Hide resolved
chat_interface = ChatInterface()
with pytest.raises(ValueError, match="must be a dict"):
chat_interface.button_properties={
"help": {
"icon": "help",
"js_on_click": "alert('Hello')",
},
}

ahuang11 marked this conversation as resolved.
Show resolved Hide resolved
def test_manual_user(self):
chat_interface = ChatInterface(user="New User")
assert chat_interface.user == "New User"
Expand Down
6 changes: 4 additions & 2 deletions panel/tests/ui/chat/test_chat_interface_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ def test_chat_interface_custom_js(page):
chat_interface.button_properties={
"help": {
"icon": "help",
"js_on_click": "console.log(`Typed: '${chat_input.value}'`)",
"js_args": {"chat_input": chat_interface.active_widget},
"js_on_click": {
"code": "console.log(`Typed: '${chat_input.value}'`)",
"args": {"chat_input": chat_interface.active_widget},
},
},
}
serve_component(page, chat_interface)
Expand Down
Loading