Skip to content

Tier 5 refactor: molecule extraction, browser tests, project sub-nav#69

Merged
felipebalbi merged 13 commits into
OpenDevicePartnership:mainfrom
felipebalbi:tier5-refactor
May 15, 2026
Merged

Tier 5 refactor: molecule extraction, browser tests, project sub-nav#69
felipebalbi merged 13 commits into
OpenDevicePartnership:mainfrom
felipebalbi:tier5-refactor

Conversation

@felipebalbi
Copy link
Copy Markdown
Contributor

Tier 5 refactor: molecule extraction, browser tests, project sub-nav

Builds on the design-system primitives landed in #68. This tier
extracts the recurring composite UI patterns into named molecules
in src/components/ui/ and src/components/landing/, adds
browser-side wasm-bindgen tests for the new components, and
introduces cross-project navigation tabs.

Highlights

  • t28 -- drop the redundant "Get Started" / "Projects" CTA tiles
    on the home hero (they duplicated the menu bar one row above).
  • t29 -- new tests/wasm.rs integration suite + Firefox
    wasm-pack job in CI. Helpers: mount() (per-test container),
    next_tick() (microtask flush for Leptos reactive updates).
  • t30 -- t36 / t38 -- extract design-system molecules and
    backfill matching DOM tests for each:
    • <ArrowLink> (auto-detects external by http(s)://)
    • <IconBlock> (Standard / Hero size presets)
    • <ValuePropCard> (composes IconBlock + Heading + Text)
    • <TwoColumnIntro> + <LabeledSection> (slot-style props
      via ViewFnOnce so callers pass plain closures)
    • <DocLinkItem>
    • <AnnouncementCard> + src/data/announcements.rs
  • t33 -- split monolithic landing_page.rs into per-section
    files under src/components/landing/; the page shell collapses
    to 18 lines.
  • t37 -- horizontal <ProjectTabs> strip on each project
    page so visitors hop between sibling projects in one click.
    Active tab carries aria-current="page". Project URLs and
    short labels now live on ProjectCopy so the tabs derive
    everything from src/data/projects.rs.
  • build fix -- forward --enable-bulk-memory and
    --enable-nontrapping-float-to-int to wasm-opt via
    data-wasm-opt-params on index.html. Trunk's bundled
    wasm-opt v123 rejected rustc 1.82+ memory.copy
    output without these flags, breaking trunk build --release.

Tests

  • cargo test -- 46 host tests pass.
  • wasm-pack test --headless --firefox --test wasm -- 19 browser
    tests pass.
  • cargo clippy --target wasm32-unknown-unknown --all-targets --no-deps -- -D warnings -- clean.
  • trunk build --release -- now succeeds.

Going-forward convention introduced this tier: every new molecule
or section ships with its DOM test in the same commit.

felipebalbi and others added 13 commits May 15, 2026 12:04
The home hero shipped two large CTA tiles -- "Getting started" and
"Projects" -- right under a navigation bar that already has both as
top-level links. The duplication was visually loud and added two
hard-coded fixed-pixel widths (lg:w-[478px], h-[176px]) plus inline
styles that fought the surrounding flex layout.

This commit drops the tiles and lets the headline + subtitle span
the row. The Welcome + video block underneath is unchanged.

Drive-by typography migration of the Welcome block onto <Heading> /
<Text> from the design system, and a couple of inline-style ->
Tailwind cleanups (object-contain block mb-4, rounded-[10px],
max-w-screen) that were left over from earlier passes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds DOM-level browser tests using wasm-bindgen-test, runnable via
`wasm-pack test --headless --firefox --test wasm`. These cover behavior
the host-side `cargo test` cannot reach.

Tests in `tests/wasm.rs`:

* `themed_icon_renders_picture_with_dark_source_and_light_img` -
  `<ThemedIcon>` emits the expected `<picture>/<source>/<img>`
  shape with the prefers-color-scheme media query.
* `team_hero_renders_back_button_anchor` - `<TeamHero>` renders
  the history.back() anchor.
* `header_logo_is_wrapped_in_home_anchor` - clicking the logo
  navigates to `/` (the t27 logo-as-home behavior).
* `header_mobile_menu_toggles_aria_expanded` - the mobile burger
  toggles `aria-expanded` correctly. The assertion is async with
  a microtask flush helper because Leptos applies reactive
  attribute updates on the next microtask, not synchronously with
  the click handler.

Infrastructure:

* `wasm-bindgen-test`, `wasm-bindgen-futures`, and `js-sys` added
  as dev-dependencies.
* `mod components` -> `pub mod components` in `src/lib.rs` so
  integration tests can reach the UI types. `data` and `pages`
  remain private.
* New `wasm-test` job in `.github/workflows/ci.yaml` runs the
  browser tests headlessly under Firefox on `ubuntu-latest`
  (which ships Firefox by default; wasm-pack auto-fetches a
  matching geckodriver).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The "→ label" link with separate non-underlined arrow span and
underlined label appeared verbatim in three places. Hoist it into
`components/ui/arrow_link.rs` as a reusable design-system molecule
that auto-detects external URLs (uses `<a target=_blank rel=...>`)
vs. internal routes (uses `leptos_router::components::A`) from the
`href` scheme.

Migrated:
* `DocumentationTraining` -- the 5-link "Documentation" footer
  list now collapses to a single `<ArrowLink size=Large
  external=link.external />` per item.
* `ProjectsComponent::ProjectLink` -- now a thin wrapper that
  forwards to `<ArrowLink>`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The "themed icon stacked above content in a left-aligned column"
pattern recurs across the landing page value-prop cards, the
documentation footer, and the team hero. Hoist it into
`components/ui/icon_block.rs` with two size presets:

* `IconBlockSize::Standard` -- the `.icon` size used by the
  value-prop / community cards.
* `IconBlockSize::Hero` -- the larger 80/150px responsive icon
  used by the documentation footer and other hero rows.

Migrated the documentation footer's icon column to `<IconBlock>`
as the first consumer; remaining call sites land in t32 (value-prop
cards) and follow-on commits.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The three landing-page Value Proposition cards shared an
icon+H3+body shape. Hoist into `components/ui/value_prop_card.rs`,
which composes [\IconBlock\] + [\Heading\] (H3) + [\Text\]
(Large) and applies the `md:flex-1` equal-share Tailwind class so
the cards still split the row evenly.

The landing-page section drops from 3x10-line raw blocks to 3x6-line
`<ValuePropCard ... />` calls.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The 100-line landing-page view block was a single `LandingPage`
component holding four logically distinct sections back-to-back:
hero, value proposition, projects intro + tiles, and the closing
two-column call-to-action.

Hoist each into its own file under `src/components/landing/`:

* `hero_section.rs` -- `<HeroSection>`
* `value_proposition_section.rs` -- `<ValuePropositionSection>`
* `projects_section.rs` -- `<ProjectsSection>` (intro + tiles)
* `closing_columns_section.rs` -- `<ClosingColumnsSection>`

`components/landing_page.rs` collapses to an 18-line shell that
renders the four sections in order. The new `landing` module is
registered in `components/mod.rs`.

No visual changes; the rendered DOM is identical, just sourced from
smaller files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tier 5's stated rationale for landing wasm-bindgen-test (t29)
second was that later refactors get safety-netted by browser tests
as they go in. The molecule and section commits (t30-t33) shipped
without matching DOM tests; this commit closes that gap.

Adds eight tests in `tests/wasm.rs`:

* `arrow_link_external_renders_target_blank_and_external_class` -
  external href produces `target="_blank"`,
  `rel="noopener noreferrer"`, and the `external-link` wrapper
  class with both arrow and label spans present.
* `arrow_link_internal_renders_router_anchor_without_target` -
  internal href produces an `internal-link` wrapper, no `target`
  attribute, and routes via `leptos_router::A` (wrapped in
  `<Router>`).
* `icon_block_wraps_themed_icon_with_children` - `<IconBlock>`
  emits a `<picture>` followed by the supplied children.
* `value_prop_card_contains_icon_title_and_body` - title and
  body strings are present alongside the icon.
* `hero_section_renders_headline`,
  `value_proposition_section_renders_three_cards`,
  `projects_section_renders_intro_and_three_image_buttons`,
  `closing_columns_section_renders_both_columns` - each landing
  section composes correctly and exposes its known unique copy.

Reuses the existing `mount` helper. Switches from
`Element::query_selector_all` (not in our web-sys feature set) to
`Element::get_elements_by_tag_name` for tag-count assertions.

No production code changes. `cargo test` (host): 40 pass.
`wasm-pack test --headless --firefox --test wasm`: 12 pass.

Going forward: every new molecule/section in t34-t37 lands with
its DOM test in the same commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two intro layouts shared a "stack-on-mobile, two-columns-on-md+"
shape: the projects-page hero (System Firmware Domains) and every
project introduction hero. Both also rendered a recurring
`<Mono>{LABEL}</Mono>` + body block (WHAT / WHY / WHO).

* New `ui/two_column_intro.rs` -- `<TwoColumnIntro left=.. right=.. />`
  bakes in the [\Section\] shell, the responsive flex row, and the
  `mt-8 md:mt-0` mobile-spacing on the right column. Slot props
  use [\ViewFnOnce\] so callers pass plain `|| view! { ... }`
  closures instead of boxed children.
* New `ui/labeled_section.rs` -- `<LabeledSection label="WHAT">..</LabeledSection>`
  emits a Mono label followed by the supplied body content, removing
  the manual Mono+body pairing at every call site.

Migrated:
* `projects_component.rs` -- the System Firmware Domains hero row
  is now a single `<TwoColumnIntro>` + two `<LabeledSection>`s.
* `project_introduction.rs` -- WHAT / WHY / WHO blocks now use
  `<LabeledSection>`.

t38-mandate honored: two new wasm tests in `tests/wasm.rs`:
* `two_column_intro_places_left_and_right_in_separate_columns`
  -- asserts the row has exactly two children, the slots land in
  the correct one, and the right column carries the `mt-8` mobile
  spacing.
* `labeled_section_renders_uppercase_label_then_children` --
  asserts source-order (label before body) and label presence.

`cargo test`: 40 pass. `wasm-pack test --headless --firefox`:
14 pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Documentation footer rendered each link as `<li><ArrowLink size=Large external=link.external .../></li>`. Hoist into `ui/doc_link_item.rs` so the wrapper-li shape lives in one place and any future "list of brand links" surface (e.g. the announcements card in t36) can reuse it without re-deriving the size + li wrapping rules.

Migrated `DocumentationTraining` to use `<DocLinkItem href title external />`.

t38-mandate: two new wasm tests in `tests/wasm.rs`:
* `doc_link_item_external_renders_li_with_arrow_link` -- external href produces `<li>` containing an anchor with `target=_blank` and the title text.
* `doc_link_item_internal_uses_router_anchor` -- internal href produces an anchor without `target`, wrapped in `<Router>` so `leptos_router::A` has its context.

`cargo test`: 40 pass. `wasm-pack test --headless --firefox`: 16 pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two coupled cleanups for the announcements page:

* New `src/data/announcements.rs` -- `Announcement` struct
  (slug / link_label / title) and an `ANNOUNCEMENTS` slice as
  the canonical source of announcement metadata. Also exposes
  `index_of(slug) -> Option<usize>` so the URL `?id=` lookup
  doesn't have to re-implement linear search at the call site.
  Three host-side unit tests cover slug uniqueness and index_of.

* New `ui/announcement_card.rs` -- `<AnnouncementCard title=>..</>`
  bakes in the styled detail-panel wrapper (responsive padding,
  `h2` title row, `p2` body) so the page no longer has to spell
  out the typography classes inline.

* Refactored `pages/announcements.rs`:
  - Imports the data module instead of defining the announcement
    list inline.
  - Renders each detail panel via `<AnnouncementCard>`.
  - Extracted `slug_from_query()` so the `?id=` parsing has
    a name and is no longer entangled with the effect closure.
  - Effect now uses `index_of()` to convert slug -> index.

t38-mandate: one new wasm test in `tests/wasm.rs` --
`announcement_card_renders_title_and_body` asserts the title is
present, the body children render, and the title precedes the body
in source order.

`cargo test`: 43 pass (3 new in announcements.rs).
`wasm-pack test --headless --firefox`: 17 pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a horizontal <ProjectTabs> nav rendered between the project
hero and the repository graph on each of the three project pages
(/boot-firmware, /embedded-controller, /windows-ec-services). The
active tab carries aria-current="page" and is bolded; the other
two tabs are plain router links to the sibling project pages, so
visitors can hop directly between projects without backing out
to /projects.

ProjectCopy gains short_label and route fields so the tabs derive
their links and labels from the existing data module rather than
hard-coding strings in the component.

Tests: two new wasm-bindgen tests (tabs render exactly three
anchors with the correct hrefs and aria-current; the active marker
follows the active prop) plus three host unit tests on the data
mapping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
	runk build --release failed with a flood of wasm-validator errors
along the lines of:

    [wasm-validator error in function NNNN] unexpected false:
    memory.copy operations require bulk memory operations
    [--enable-bulk-memory-opt]

Trunk's bundled wasm-opt (binaryen v123) does not enable the
bulk-memory feature by default, but rustc 1.82+ (and several of our
dependencies) emit `memory.copy` instructions, so wasm-opt rejects
the staged wasm before it can be optimised.

Forward `--enable-bulk-memory` and `--enable-nontrapping-float-to-int`
to wasm-opt via Trunk's `data-wasm-opt-params` attribute on the
`rust` link in `index.html`. These are the same target features
the rustc wasm32 target enables, so the optimiser now accepts the
input and the release pipeline succeeds.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@felipebalbi felipebalbi merged commit 518714a into OpenDevicePartnership:main May 15, 2026
7 checks passed
@felipebalbi felipebalbi deleted the tier5-refactor branch May 15, 2026 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant