Skip to content

Commit 60f9249

Browse files
committed
buttons: Add SpoilerButton class showing spoiler header.
1 parent be9f757 commit 60f9249

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

tests/ui_tools/test_buttons.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
MessageLinkButton,
1515
ParsedNarrowLink,
1616
PMButton,
17+
SpoilerButton,
1718
StarredButton,
1819
StreamButton,
1920
TopButton,
@@ -308,6 +309,75 @@ def test_keypress_USER_INFO(
308309
pop_up.assert_called_once_with(user_button.user_id)
309310

310311

312+
class TestSpoilerButton:
313+
@pytest.fixture(autouse=True)
314+
def mock_external_classes(self, mocker: MockerFixture) -> None:
315+
self.controller = mocker.Mock()
316+
self.super_init = mocker.patch(MODULE + ".urwid.Button.__init__")
317+
self.connect_signal = mocker.patch(MODULE + ".urwid.connect_signal")
318+
319+
def spoiler_button(
320+
self,
321+
header_len: int = 0,
322+
header: List[Any] = [""],
323+
content: List[Any] = [""],
324+
display_attr: Optional[str] = None,
325+
) -> SpoilerButton:
326+
self.content = content
327+
self.header_len = header_len
328+
self.header = header
329+
self.display_attr = display_attr
330+
return SpoilerButton(self.controller, header_len, header, content, display_attr)
331+
332+
def test_init(self, mocker: MockerFixture) -> None:
333+
self.update_widget = mocker.patch(MODULE + ".SpoilerButton.update_widget")
334+
335+
mocked_button = self.spoiler_button()
336+
337+
assert mocked_button.controller == self.controller
338+
assert mocked_button.content == self.content
339+
self.super_init.assert_called_once_with("")
340+
self.update_widget.assert_called_once_with(
341+
self.header_len, self.header, self.display_attr
342+
)
343+
assert self.connect_signal.called
344+
345+
@pytest.mark.parametrize(
346+
"header, header_len, expected_cursor_position",
347+
[
348+
(["Test"], 4, 5),
349+
(["Check"], 5, 6),
350+
],
351+
)
352+
def test_update_widget(
353+
self,
354+
mocker: MockerFixture,
355+
header: List[Any],
356+
header_len: int,
357+
expected_cursor_position: int,
358+
display_attr: Optional[str] = None,
359+
) -> None:
360+
self.selectable_icon = mocker.patch(MODULE + ".urwid.SelectableIcon")
361+
362+
# The method update_widget() is called in SpoilerButton's init.
363+
mocked_button = self.spoiler_button(
364+
header=header, header_len=header_len, display_attr=display_attr
365+
)
366+
self.selectable_icon.assert_called_once_with(
367+
header, cursor_position=expected_cursor_position
368+
)
369+
assert isinstance(mocked_button._w, AttrMap)
370+
371+
def test_show_spoiler(self) -> None:
372+
mocked_button = self.spoiler_button()
373+
374+
mocked_button.show_spoiler()
375+
376+
mocked_button.controller.show_spoiler.assert_called_once_with(
377+
mocked_button.content
378+
)
379+
380+
311381
class TestEmojiButton:
312382
@pytest.mark.parametrize(
313383
"emoji_unit, to_vary_in_message, count",

zulipterminal/ui_tools/buttons.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,36 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
317317
return super().keypress(size, key)
318318

319319

320+
class SpoilerButton(urwid.Button):
321+
def __init__(
322+
self,
323+
controller: Any,
324+
header_len: int,
325+
header: List[Any],
326+
content: List[Any],
327+
display_attr: Optional[str],
328+
) -> None:
329+
self.controller = controller
330+
self.content = content
331+
332+
super().__init__("")
333+
self.update_widget(header_len, header, display_attr)
334+
urwid.connect_signal(self, "click", callback=self.show_spoiler)
335+
336+
def update_widget(
337+
self, header_len: int, header: List[Any], display_attr: Optional[str] = None
338+
) -> None:
339+
"""
340+
Overrides the existing button widget for custom styling.
341+
"""
342+
# Set cursor position next to header_len to avoid the cursor.
343+
icon = urwid.SelectableIcon(header, cursor_position=header_len + 1)
344+
self._w = urwid.AttrMap(icon, display_attr, focus_map="selected")
345+
346+
def show_spoiler(self, *_: Any) -> None:
347+
self.controller.show_spoiler(self.content)
348+
349+
320350
class TopicButton(TopButton):
321351
def __init__(
322352
self,

0 commit comments

Comments
 (0)