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

Tweak ChatMessage layout #7209

Merged
merged 6 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
7 changes: 6 additions & 1 deletion panel/chat/icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ def _render_icons(self):
icon._reaction = option
icon.param.watch(self._update_value, "value")
self._rendered_icons[option] = icon
self._composite[:] = [self.default_layout(*list(self._rendered_icons.values()))]
self._composite[:] = [
self.default_layout(
*list(self._rendered_icons.values()),
sizing_mode=self.param.sizing_mode,
stylesheets=self._stylesheets + self.param.stylesheets.rx(),
)]

@param.depends("value", watch=True)
def _update_icons(self):
Expand Down
10 changes: 6 additions & 4 deletions panel/chat/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def __init__(self, object=None, **params):

reaction_icons = params.get("reaction_icons", {"favorite": "heart"})
if isinstance(reaction_icons, dict):
params["reaction_icons"] = ChatReactionIcons(options=reaction_icons, default_layout=Row)
params["reaction_icons"] = ChatReactionIcons(options=reaction_icons, default_layout=Row, sizing_mode=None)
self._internal = True
super().__init__(object=object, **params)
self.chat_copy_icon = ChatCopyIcon(
Expand Down Expand Up @@ -294,18 +294,19 @@ def _build_layout(self):
self.param.user, height=20,
css_classes=["name"],
visible=self.param.show_user,
sizing_mode=None,
)

self._activity_dot = HTML(
"●",
css_classes=["activity-dot"],
visible=self.param.show_activity_dot,
sizing_mode=None,
)

meta_row = Row(
self._user_html,
self._activity_dot,
sizing_mode="stretch_width",
css_classes=["meta"],
stylesheets=self._stylesheets + self.param.stylesheets.rx(),
)
Expand Down Expand Up @@ -344,8 +345,8 @@ def _build_layout(self):
header_col,
self._center_row,
footer_col,
self._timestamp_html,
self._icons_row,
self._timestamp_html,
css_classes=["right"],
sizing_mode=None,
stylesheets=self._stylesheets + self.param.stylesheets.rx(),
Expand Down Expand Up @@ -556,6 +557,7 @@ def _render_reaction_icons(self):

def _update_reaction_icons(self, _):
self._icons_row[-1] = self._render_reaction_icons()
self._icon_divider.visible = len(self.reaction_icons.options) > 0 and self.chat_copy_icon.visible

def _update(self, ref, old_models):
"""
Expand Down Expand Up @@ -598,7 +600,7 @@ def _update_chat_copy_icon(self):
if isinstance(object_panel, str) and self.show_copy_icon:
self.chat_copy_icon.value = object_panel
self.chat_copy_icon.visible = True
self._icon_divider.visible = True
self._icon_divider.visible = len(self.reaction_icons.options) > 0
else:
self.chat_copy_icon.value = ""
self.chat_copy_icon.visible = False
Expand Down
2 changes: 1 addition & 1 deletion panel/dist/css/chat_copy_icon.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:host {
margin-top: 5px;
margin-top: 8px;
}
1 change: 1 addition & 0 deletions panel/dist/css/chat_message.css
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@

.name {
font-size: 1em;
width: fit-content;
}

.center {
Expand Down
4 changes: 4 additions & 0 deletions panel/dist/css/chat_reaction_icons.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:host {
margin-top: 4px;
width: fit-content;
}
1 change: 0 additions & 1 deletion panel/dist/css/icon.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@
.bk-IconRow {
display: flex;
align-items: center;
justify-content: center;
}
30 changes: 20 additions & 10 deletions panel/tests/chat/test_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_layout(self):
assert isinstance(object_pane, Markdown)
assert object_pane.object == "ABC"

icons = columns[1][5][2]
icons = columns[1][4][2]
assert isinstance(icons, ChatReactionIcons)

footer_col = columns[1][3]
Expand All @@ -65,22 +65,29 @@ def test_layout(self):
assert isinstance(footer_col[1], Markdown)
assert footer_col[1].object == "Footer 2"

timestamp_pane = columns[1][4][0]
timestamp_pane = columns[1][5][0]
assert isinstance(timestamp_pane, HTML)

def test_reactions_dynamic(self):
message = ChatMessage(reactions=["favorite"])
message = ChatMessage("hi", reactions=["favorite"])
assert message.reaction_icons.value == ["favorite"]

message.reactions = ["thumbs-up"]
assert message.reaction_icons.value == ["thumbs-up"]

def test_reaction_icons_dynamic(self):
message = ChatMessage(reaction_icons={"favorite": "heart"})
message = ChatMessage("hi", reaction_icons={"favorite": "heart"})
assert message.reaction_icons.options == {"favorite": "heart"}

message.reaction_icons = ChatReactionIcons(options={"like": "thumb-up"})
assert message._icons_row[-1] == message.reaction_icons
assert message._icon_divider.visible

message.reaction_icons = ChatReactionIcons(options={})
assert not message._icon_divider.visible

message = ChatMessage("hi", reaction_icons={})
assert not message._icon_divider.visible

def test_reactions_link(self):
# on init
Expand Down Expand Up @@ -171,39 +178,39 @@ def test_update_object(self):
def test_update_timestamp(self):
message = ChatMessage()
columns = message._composite.objects
timestamp_pane = columns[1][4][0]
timestamp_pane = columns[1][5][0]
assert isinstance(timestamp_pane, HTML)
dt_str = datetime.datetime.now().strftime("%H:%M")
assert timestamp_pane.object == dt_str

message = ChatMessage(timestamp_tz="UTC")
columns = message._composite.objects
timestamp_pane = columns[1][4][0]
timestamp_pane = columns[1][5][0]
assert isinstance(timestamp_pane, HTML)
dt_str = datetime.datetime.now(datetime.timezone.utc).strftime("%H:%M")
assert timestamp_pane.object == dt_str

message = ChatMessage(timestamp_tz="US/Pacific")
columns = message._composite.objects
timestamp_pane = columns[1][4][0]
timestamp_pane = columns[1][5][0]
assert isinstance(timestamp_pane, HTML)
dt_str = datetime.datetime.now(tz=ZoneInfo("US/Pacific")).strftime("%H:%M")
assert timestamp_pane.object == dt_str

special_dt = datetime.datetime(2023, 6, 24, 15)
message.timestamp = special_dt
timestamp_pane = columns[1][4][0]
timestamp_pane = columns[1][5][0]
dt_str = special_dt.strftime("%H:%M")
assert timestamp_pane.object == dt_str

mm_dd_yyyy = "%b %d, %Y"
message.timestamp_format = mm_dd_yyyy
timestamp_pane = columns[1][4][0]
timestamp_pane = columns[1][5][0]
dt_str = special_dt.strftime(mm_dd_yyyy)
assert timestamp_pane.object == dt_str

message.show_timestamp = False
timestamp_pane = columns[1][4][0]
timestamp_pane = columns[1][5][0]
assert not timestamp_pane.visible

def test_does_not_turn_widget_into_str(self):
Expand Down Expand Up @@ -322,17 +329,20 @@ def test_chat_copy_icon_text_widget(self, widget):
message = ChatMessage(object=widget(value="testing"))
assert message.chat_copy_icon.visible
assert message.chat_copy_icon.value == "testing"
assert message._icon_divider.visible

def test_chat_copy_icon_disabled(self):
message = ChatMessage(object="testing", show_copy_icon=False)
assert not message.chat_copy_icon.visible
assert not message.chat_copy_icon.value
assert not message._icon_divider.visible

@pytest.mark.parametrize("component", [Column, FileInput])
def test_chat_copy_icon_not_string(self, component):
message = ChatMessage(object=component())
assert not message.chat_copy_icon.visible
assert not message.chat_copy_icon.value
assert not message._icon_divider.visible

def test_serialize_text_prefix_with_viewable_type(self):
message = ChatMessage(Markdown("string"))
Expand Down
Loading