Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
22 changes: 19 additions & 3 deletions native/pages/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,38 @@ def link_card(name: str, href: str, count: int):
)


def get_links(source_dir: Path, examples_dir: Path, url_prefix: str):
def get_component_entries(
source_dir: Path, examples_dir: Path
) -> list[tuple[str, str, int]]:
entries = []

component_stems = [f.stem for f in source_dir.glob("*.py") if f.stem != "__init__"]

for file in source_dir.glob("*.py"):
stem = file.stem
if stem == "__init__":
continue
name = stem.replace("_", " ").title()
slug = stem.replace("_", "-")

example_count = len(list(examples_dir.glob(f"{stem}_*.py")))
sibling_prefixes = [f"{s}_" for s in component_stems if s.startswith(f"{stem}_")]
example_count = sum(
1
for p in examples_dir.glob(f"{stem}_*.py")
if not any(p.name.startswith(pref) for pref in sibling_prefixes)
)

entries.append((name, slug, example_count))

entries.sort(key=lambda x: x[0].lower())

return entries


def get_links(source_dir: Path, examples_dir: Path, url_prefix: str):
return [
link_card(name, f"{url_prefix}/{slug}", count) for name, slug, count in entries
link_card(name, f"{url_prefix}/{slug}", count)
for name, slug, count in get_component_entries(source_dir, examples_dir)
]


Expand Down
111 changes: 111 additions & 0 deletions tests/test_components_card_counts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import importlib
import os
import re
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
UI = ROOT / "components/ui"
EX = ROOT / "native/lib/components"
DOCS = ROOT / "docs/components"

DEMO_RE = re.compile(r"--DEMO\([^)]*\)--")

EXPECTED_BUTTON_DEMOS = 12
EXPECTED_BUTTON_GROUP_DEMOS = 6
EXPECTED_INPUT_DEMOS = 6
EXPECTED_INPUT_GROUP_DEMOS = 9


def _demo_count(slug: str) -> int:
md = DOCS / f"{slug.replace('-', '_')}.md"
return len(DEMO_RE.findall(md.read_text()))


def _entries():
from native.pages.components import get_component_entries

return get_component_entries(UI, EX)


def _cards():
return {slug: count for _, slug, count in _entries()}


def test_card_count_matches_detail_page_demos():
cards = _cards()
assert cards, "no component entries found (glob returned nothing)"
mismatches = []
for slug, count in cards.items():
demos = _demo_count(slug)
if count != demos:
mismatches.append(f"{slug}: card={count} demos={demos}")
assert not mismatches, "card counts disagree with detail-page demos:\n" + "\n".join(
mismatches,
)


def test_button_card_count_excludes_button_group():
cards = _cards()
assert cards["button"] == EXPECTED_BUTTON_DEMOS
assert cards["button-group"] == EXPECTED_BUTTON_GROUP_DEMOS
assert cards["button"] != EXPECTED_BUTTON_DEMOS + EXPECTED_BUTTON_GROUP_DEMOS


def test_input_card_count_excludes_input_group():
cards = _cards()
assert cards["input"] == EXPECTED_INPUT_DEMOS
assert cards["input-group"] == EXPECTED_INPUT_GROUP_DEMOS
assert cards["input"] != EXPECTED_INPUT_DEMOS + EXPECTED_INPUT_GROUP_DEMOS


def test_no_prefixed_sibling_double_counting():
stems = sorted(p.stem for p in UI.glob("*.py") if p.stem != "__init__")
cards = _cards()
for a in stems:
a_slug = a.replace("_", "-")
siblings = [s for s in stems if s.startswith(f"{a}_")]
a_only = sum(
1
for p in EX.glob(f"{a}_*.py")
if not any(p.name.startswith(f"{s}_") for s in siblings)
)
assert cards[a_slug] == a_only, (
f"{a} double-counts sibling files: card={cards[a_slug]} expected={a_only}"
)
for b in siblings:
b_slug = b.replace("_", "-")
b_files = list(EX.glob(f"{b}_*.py"))
assert cards[b_slug] == len(b_files), (
f"{b} card={cards[b_slug]} expected {len(b_files)}"
)


def test_entries_sorted_by_name_case_insensitive():
entries = _entries()
names_lower = [name.lower() for name, _, _ in entries]
assert names_lower == sorted(names_lower)


def test_get_links_returns_one_card_per_entry():
from native.pages.components import get_links

cards = get_links(UI, EX, "/docs/components")
entries = _entries()
assert len(cards) == len(entries)
assert cards
assert not isinstance(cards[0], tuple)


def test_module_component_links_nonempty_from_repo_root():
import native.pages.components as page

cwd = Path.cwd()
os.chdir(ROOT)
try:
importlib.reload(page)
assert len(page.component_links) > 0, (
"module-level component_links is empty; import-time relative-path "
"globs did not resolve from repo root (as `reflex run` does)"
)
finally:
os.chdir(cwd)