Skip to content

Commit 819c30d

Browse files
committed
buttons: Add SpoilerButton class showing spoiler header.
This commit adds a button that would allow user to view the spoiler content, i.e., onclick of button is SpoilerView popup. Tests added. Co-authored by: Preet Mishra (<[email protected]>).
1 parent f6fe51b commit 819c30d

File tree

2 files changed

+92
-1
lines changed

2 files changed

+92
-1
lines changed

tests/ui_tools/test_buttons.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from zulipterminal.config.keys import keys_for_command
88
from zulipterminal.ui_tools.buttons import (
99
MessageLinkButton,
10+
SpoilerButton,
1011
StarredButton,
1112
StreamButton,
1213
TopButton,
@@ -414,6 +415,66 @@ def test_keypress_EXIT_TOGGLE_TOPIC(self, mocker, topic_button, key, widget_size
414415
topic_button.view.left_panel.show_stream_view.assert_called_once_with()
415416

416417

418+
class TestSpoilerButton:
419+
@pytest.fixture(autouse=True)
420+
def mock_external_classes(self, mocker):
421+
self.controller = mocker.Mock()
422+
self.super_init = mocker.patch(BUTTONS + ".urwid.Button.__init__")
423+
self.connect_signal = mocker.patch(BUTTONS + ".urwid.connect_signal")
424+
425+
def spoiler_button(
426+
self, header_len=0, header=[""], content=[""], display_attr=None
427+
):
428+
self.content = content
429+
self.header_len = header_len
430+
self.header = header
431+
self.display_attr = display_attr
432+
return SpoilerButton(self.controller, header_len, header, content, display_attr)
433+
434+
def test_init(self, mocker):
435+
self.update_widget = mocker.patch(BUTTONS + ".SpoilerButton.update_widget")
436+
437+
mocked_button = self.spoiler_button()
438+
439+
assert mocked_button.controller == self.controller
440+
assert mocked_button.content == self.content
441+
self.super_init.assert_called_once_with("")
442+
self.update_widget.assert_called_once_with(
443+
self.header_len, self.header, self.display_attr
444+
)
445+
assert self.connect_signal.called
446+
447+
@pytest.mark.parametrize(
448+
"header, header_len, expected_cursor_position",
449+
[
450+
(["Test"], 4, 5),
451+
(["Check"], 5, 6),
452+
],
453+
)
454+
def test_update_widget(
455+
self, mocker, header, header_len, expected_cursor_position, display_attr=None
456+
):
457+
self.selectable_icon = mocker.patch(BUTTONS + ".urwid.SelectableIcon")
458+
459+
# The method update_widget() is called in SpoilerButton's init.
460+
mocked_button = self.spoiler_button(
461+
header=header, header_len=header_len, display_attr=display_attr
462+
)
463+
self.selectable_icon.assert_called_once_with(
464+
header, cursor_position=expected_cursor_position
465+
)
466+
assert isinstance(mocked_button._w, AttrMap)
467+
468+
def test_show_spoiler(self, mocker):
469+
mocked_button = self.spoiler_button()
470+
471+
mocked_button.show_spoiler()
472+
473+
mocked_button.controller.show_spoiler.assert_called_once_with(
474+
mocked_button.content
475+
)
476+
477+
417478
class TestMessageLinkButton:
418479
@pytest.fixture(autouse=True)
419480
def mock_external_classes(self, mocker):

zulipterminal/ui_tools/buttons.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import re
22
from functools import partial
3-
from typing import Any, Callable, Dict, Optional, Tuple, Union, cast
3+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
44
from urllib.parse import urljoin, urlparse
55

66
import urwid
@@ -154,6 +154,36 @@ def __init__(self, controller: Any, width: int, count: int = 0) -> None:
154154
)
155155

156156

157+
class SpoilerButton(urwid.Button):
158+
def __init__(
159+
self,
160+
controller: Any,
161+
header_len: int,
162+
header: List[Any],
163+
content: List[Any],
164+
display_attr: Optional[str],
165+
) -> None:
166+
self.controller = controller
167+
self.content = content
168+
169+
super().__init__("")
170+
self.update_widget(header_len, header, display_attr)
171+
urwid.connect_signal(self, "click", callback=self.show_spoiler)
172+
173+
def update_widget(
174+
self, header_len: int, header: List[Any], display_attr: Optional[str] = None
175+
) -> None:
176+
"""
177+
Overrides the existing button widget for custom styling.
178+
"""
179+
# Set cursor position next to header_len to avoid the cursor.
180+
icon = urwid.SelectableIcon(header, cursor_position=header_len + 1)
181+
self._w = urwid.AttrMap(icon, display_attr, focus_map="selected")
182+
183+
def show_spoiler(self, *_: Any) -> None:
184+
self.controller.show_spoiler(self.content)
185+
186+
157187
class StarredButton(TopButton):
158188
def __init__(self, controller: Any, width: int, count: int = 0) -> None:
159189
button_text = f"Starred messages [{primary_key_for_command('ALL_STARRED')}]"

0 commit comments

Comments
 (0)