Spaces:
Running
Running
CI deploy local
Browse files- .pytest_cache/v/cache/nodeids +2 -0
- app.py +4 -1
- src/ui/chrome.py +12 -0
- src/ui/shell.py +1 -3
- tests/test_ui.py +45 -0
.pytest_cache/v/cache/nodeids
CHANGED
|
@@ -156,6 +156,7 @@
|
|
| 156 |
"tests/test_ui.py::test_emit_rejects_values_containing_the_nonce_separator",
|
| 157 |
"tests/test_ui.py::test_empty_index_says_so_rather_than_rendering_zeros",
|
| 158 |
"tests/test_ui.py::test_every_chrome_action_is_on_the_allow_list",
|
|
|
|
| 159 |
"tests/test_ui.py::test_every_design_stylesheet_reaches_the_page[--bg-canvas-colors.css]",
|
| 160 |
"tests/test_ui.py::test_every_design_stylesheet_reaches_the_page[--space--spacing.css]",
|
| 161 |
"tests/test_ui.py::test_every_design_stylesheet_reaches_the_page[--text-2xs-typography.css]",
|
|
@@ -181,6 +182,7 @@
|
|
| 181 |
"tests/test_ui.py::test_search_bar_is_live_not_enter_only",
|
| 182 |
"tests/test_ui.py::test_sidebar_renders_collapsed_by_default",
|
| 183 |
"tests/test_ui.py::test_sidebar_toggle_costs_no_round_trip",
|
|
|
|
| 184 |
"tests/test_ui.py::test_theme_uses_the_designs_dense_type_scale",
|
| 185 |
"tests/test_ui.py::test_trending_panel_admits_when_it_has_no_data",
|
| 186 |
"tests/test_ui.py::test_uncapped_render_uses_the_designs_wording",
|
|
|
|
| 156 |
"tests/test_ui.py::test_emit_rejects_values_containing_the_nonce_separator",
|
| 157 |
"tests/test_ui.py::test_empty_index_says_so_rather_than_rendering_zeros",
|
| 158 |
"tests/test_ui.py::test_every_chrome_action_is_on_the_allow_list",
|
| 159 |
+
"tests/test_ui.py::test_every_class_the_table_renders_has_a_rule",
|
| 160 |
"tests/test_ui.py::test_every_design_stylesheet_reaches_the_page[--bg-canvas-colors.css]",
|
| 161 |
"tests/test_ui.py::test_every_design_stylesheet_reaches_the_page[--space--spacing.css]",
|
| 162 |
"tests/test_ui.py::test_every_design_stylesheet_reaches_the_page[--text-2xs-typography.css]",
|
|
|
|
| 182 |
"tests/test_ui.py::test_search_bar_is_live_not_enter_only",
|
| 183 |
"tests/test_ui.py::test_sidebar_renders_collapsed_by_default",
|
| 184 |
"tests/test_ui.py::test_sidebar_toggle_costs_no_round_trip",
|
| 185 |
+
"tests/test_ui.py::test_the_app_serves_this_spaces_own_css_not_just_the_shared_css",
|
| 186 |
"tests/test_ui.py::test_theme_uses_the_designs_dense_type_scale",
|
| 187 |
"tests/test_ui.py::test_trending_panel_admits_when_it_has_no_data",
|
| 188 |
"tests/test_ui.py::test_uncapped_render_uses_the_designs_wording",
|
app.py
CHANGED
|
@@ -433,7 +433,10 @@ def build_app() -> gr.Blocks:
|
|
| 433 |
with gr.Blocks(
|
| 434 |
title="Finance Atlas — The Bit Trading Company",
|
| 435 |
theme=theme.bit_theme(),
|
| 436 |
-
|
|
|
|
|
|
|
|
|
|
| 437 |
analytics_enabled=False,
|
| 438 |
fill_width=True,
|
| 439 |
) as demo:
|
|
|
|
| 433 |
with gr.Blocks(
|
| 434 |
title="Finance Atlas — The Bit Trading Company",
|
| 435 |
theme=theme.bit_theme(),
|
| 436 |
+
# chrome.full_css(), not theme.full_css(): the latter is bit-ui's
|
| 437 |
+
# shared CSS only and silently drops this Space's own -- the table
|
| 438 |
+
# row styling, the sticky header, the responsive breakpoints.
|
| 439 |
+
css=chrome.full_css(),
|
| 440 |
analytics_enabled=False,
|
| 441 |
fill_width=True,
|
| 442 |
) as demo:
|
src/ui/chrome.py
CHANGED
|
@@ -53,6 +53,18 @@ def parse_action(raw):
|
|
| 53 |
# bit_ui.theme. What is left is what only this Space has.
|
| 54 |
|
| 55 |
ATLAS_CSS = """
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
/* The header sticks under nothing, so top:0; the rail is z-index 30 and wins
|
| 57 |
where they meet. A solid background matters: rows scroll underneath it. */
|
| 58 |
.bit-header {
|
|
|
|
| 53 |
# bit_ui.theme. What is left is what only this Space has.
|
| 54 |
|
| 55 |
ATLAS_CSS = """
|
| 56 |
+
/* The page root. Was inline on the div; here so `.bit-atlas` is a real hook
|
| 57 |
+
for Space-specific overrides rather than a bare marker class.
|
| 58 |
+
Deliberately no min-height in vh -- see bit_ui.theme. */
|
| 59 |
+
.bit-atlas {
|
| 60 |
+
background: var(--bg-canvas);
|
| 61 |
+
color: var(--text-primary);
|
| 62 |
+
display: flex;
|
| 63 |
+
align-items: stretch;
|
| 64 |
+
font-family: var(--font-body);
|
| 65 |
+
font-size: var(--text-base);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
/* The header sticks under nothing, so top:0; the rail is z-index 30 and wins
|
| 69 |
where they meet. A solid background matters: rows scroll underneath it. */
|
| 70 |
.bit-header {
|
src/ui/shell.py
CHANGED
|
@@ -1381,9 +1381,7 @@ def page(index, state, view) -> str:
|
|
| 1381 |
overlay = drawer(selected, index)
|
| 1382 |
|
| 1383 |
return f"""
|
| 1384 |
-
<div class="bit-atlas bit-app" data-theme="dark"
|
| 1385 |
-
style="background:var(--bg-canvas);color:var(--text-primary);display:flex;
|
| 1386 |
-
align-items:stretch;font-family:var(--font-body);font-size:var(--text-base)">
|
| 1387 |
{bit_sidebar.render(navigation, active='Finance Atlas', emit=emit)}
|
| 1388 |
<div style="flex:1 1 auto;min-width:0;display:flex;flex-direction:column">
|
| 1389 |
{header(index, view['tape'])}
|
|
|
|
| 1381 |
overlay = drawer(selected, index)
|
| 1382 |
|
| 1383 |
return f"""
|
| 1384 |
+
<div class="bit-atlas bit-app" data-theme="dark">
|
|
|
|
|
|
|
| 1385 |
{bit_sidebar.render(navigation, active='Finance Atlas', emit=emit)}
|
| 1386 |
<div style="flex:1 1 auto;min-width:0;display:flex;flex-direction:column">
|
| 1387 |
{header(index, view['tape'])}
|
tests/test_ui.py
CHANGED
|
@@ -8,6 +8,8 @@ by hand against the deployed Space.
|
|
| 8 |
|
| 9 |
from __future__ import annotations
|
| 10 |
|
|
|
|
|
|
|
| 11 |
import pytest
|
| 12 |
|
| 13 |
from src import atlas
|
|
@@ -412,3 +414,46 @@ def test_every_chrome_action_is_on_the_allow_list(index):
|
|
| 412 |
emitted |= set(re.findall(r'data-bit-(?:input|live)="([^="]+)"', html))
|
| 413 |
unknown = emitted - chrome.ALLOWED_KEYS
|
| 414 |
assert not unknown, f"chrome emits actions the bridge rejects: {unknown}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
from __future__ import annotations
|
| 10 |
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
import pytest
|
| 14 |
|
| 15 |
from src import atlas
|
|
|
|
| 414 |
emitted |= set(re.findall(r'data-bit-(?:input|live)="([^="]+)"', html))
|
| 415 |
unknown = emitted - chrome.ALLOWED_KEYS
|
| 416 |
assert not unknown, f"chrome emits actions the bridge rejects: {unknown}"
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def test_the_app_serves_this_spaces_own_css_not_just_the_shared_css():
|
| 420 |
+
"""`theme.full_css()` is bit-ui's CSS only and silently drops the Atlas's.
|
| 421 |
+
|
| 422 |
+
This shipped once: the table had just been converted to CSS classes whose
|
| 423 |
+
rules live in ATLAS_CSS, and none of them reached the page. Everything
|
| 424 |
+
still rendered, just unstyled -- exactly the kind of break that looks like
|
| 425 |
+
a design regression rather than a wiring mistake.
|
| 426 |
+
"""
|
| 427 |
+
import app as module
|
| 428 |
+
|
| 429 |
+
css = module.chrome.full_css()
|
| 430 |
+
for marker in (".bit-row", ".bit-header", ".bit-cell", ".bit-tag"):
|
| 431 |
+
assert marker in css, f"{marker} is missing from the app's CSS"
|
| 432 |
+
|
| 433 |
+
source = (Path(module.__file__).read_text())
|
| 434 |
+
assert "css=chrome.full_css()" in source, (
|
| 435 |
+
"app.py is not serving this Space's CSS")
|
| 436 |
+
assert "css=theme.full_css()" not in source
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
def test_every_class_the_table_renders_has_a_rule():
|
| 440 |
+
"""A class with no rule is invisible styling debt."""
|
| 441 |
+
import re
|
| 442 |
+
|
| 443 |
+
import app as module
|
| 444 |
+
from bit_ui import nav as bit_nav
|
| 445 |
+
|
| 446 |
+
index = module.INDEX
|
| 447 |
+
css = module.chrome.full_css()
|
| 448 |
+
state = atlas.default_state()
|
| 449 |
+
view = {"rows": index.rows[:3], "matched": 3, "truncated": 0, "hidden": 0,
|
| 450 |
+
"tape": [], "trending": [], "links": {},
|
| 451 |
+
"nav": bit_nav.default_document(), "palette_groups": []}
|
| 452 |
+
html = shell.page(index, state, view)
|
| 453 |
+
|
| 454 |
+
rendered = set()
|
| 455 |
+
for attr in re.findall(r'class="([^"]+)"', html):
|
| 456 |
+
rendered.update(c for c in attr.split() if c.startswith("bit-"))
|
| 457 |
+
|
| 458 |
+
missing = sorted(c for c in rendered if f".{c}" not in css)
|
| 459 |
+
assert not missing, f"classes rendered with no CSS rule: {missing}"
|