Fix: use 'refreshing' reactive to avoid Textual built-in loading dots

Widget.loading is a Textual built-in reactive that swaps the widget for the
default dots spinner. Our dashboard used the same name, so setting it True
activated the built-in indicator (dots) instead of the dog loader. Renamed
to 'refreshing' and assert app.screen.loading stays False in tests.
This commit is contained in:
2026-09-10 13:00:36 +03:00
parent 9f25d37b6e
commit 148e0a9391
2 changed files with 11 additions and 5 deletions
+5 -5
View File
@@ -134,7 +134,7 @@ class DashboardScreen(Screen[Any]):
] ]
data = reactive(DashboardData()) data = reactive(DashboardData())
loading = reactive(False) refreshing = reactive(False)
def __init__( def __init__(
self, self,
@@ -169,22 +169,22 @@ class DashboardScreen(Screen[Any]):
@work(exclusive=True, group="refresh") @work(exclusive=True, group="refresh")
async def _refresh(self) -> None: async def _refresh(self) -> None:
self.loading = True self.refreshing = True
try: try:
self.data = await self._service.refresh() self.data = await self._service.refresh()
except Exception as exc: # noqa: BLE001 - показываем toast при ошибке except Exception as exc: # noqa: BLE001 - показываем toast при ошибке
self.notify(f"Ошибка обновления: {exc}", severity="error") self.notify(f"Ошибка обновления: {exc}", severity="error")
finally: finally:
self.loading = False self.refreshing = False
def watch_loading(self, loading: bool) -> None: def watch_refreshing(self, refreshing: bool) -> None:
"""Показать/скрыть собачку и строку прогресса при обновлении.""" """Показать/скрыть собачку и строку прогресса при обновлении."""
try: try:
loader = self.query_one("#dog-loader", DogLoader) loader = self.query_one("#dog-loader", DogLoader)
line = self.query_one("#progress-line", Static) line = self.query_one("#progress-line", Static)
except NoMatches: # watcher может сработать до монтирования детей except NoMatches: # watcher может сработать до монтирования детей
return return
if loading: if refreshing:
loader.add_class("visible") loader.add_class("visible")
loader.start() loader.start()
line.update("[b]🐕 Собачка обновляет статистику…[/]") line.update("[b]🐕 Собачка обновляет статистику…[/]")
+6
View File
@@ -317,9 +317,12 @@ async def test_dog_loader_hidden_when_idle(tmp_path) -> None:
await pilot.pause() await pilot.pause()
loader = app.screen.query_one("#dog-loader", DogLoader) loader = app.screen.query_one("#dog-loader", DogLoader)
assert not loader.has_class("visible") assert not loader.has_class("visible")
assert loader.display is False
assert not loader.running assert not loader.running
line = app.screen.query_one("#progress-line", Static) line = app.screen.query_one("#progress-line", Static)
assert str(line.render()) == "" assert str(line.render()) == ""
# встроенный loading Textual не должен активироваться (иначе — точки)
assert app.screen.loading is False
async def test_dog_loader_shown_during_refresh(tmp_path) -> None: async def test_dog_loader_shown_during_refresh(tmp_path) -> None:
@@ -339,13 +342,16 @@ async def test_dog_loader_shown_during_refresh(tmp_path) -> None:
await pilot.pause() await pilot.pause()
loader = app.screen.query_one("#dog-loader", DogLoader) loader = app.screen.query_one("#dog-loader", DogLoader)
assert loader.has_class("visible") assert loader.has_class("visible")
assert loader.display is True
assert loader.running assert loader.running
line = app.screen.query_one("#progress-line", Static) line = app.screen.query_one("#progress-line", Static)
assert "обновляет статистику" in str(line.render()) assert "обновляет статистику" in str(line.render())
assert app.screen.loading is False
release.set() release.set()
await pilot.pause(0.1) await pilot.pause(0.1)
assert not loader.has_class("visible") assert not loader.has_class("visible")
assert loader.display is False
assert not loader.running assert not loader.running
assert str(line.render()) == "" assert str(line.render()) == ""