[Story] Reading list page #9

Merged
kpcto merged 6 commits from feature/8 into main 2026-08-25 12:33:22 +00:00
Member

Summary

Implements the reading list page for issue #8 — a static page that renders curated links grouped by category from a single data file. No backend, no automated import/scraping, no search/filter UI (per scope).

What changed

  • reading.html — new page with a #reading-list container; loads the renderer module (js/reading-list.js).
  • data/reading-list.js — the single data file. Schema: title, url, category, and an optional one-line note. Adding a link means editing this file only.
  • js/reading-list.js — pure renderer: groups entries by category (first-seen order), HTML-escapes all text, skips invalid entries, de-duplicates category ids; guarded browser wiring renders the data file into the page.
  • Navigation — Home and Contact pages link to reading.html; the Reading page marks its own link aria-current="page".
  • css/style.css — category/list styles; two-column grid on wide screens and overflow-wrap on links so 500+ entries stay scannable and usable.
  • tests/reading-list.test.js — component, data-integrity, renderer, and 500-entry fixture tests.
  • package.json — test script now node --test (see Risks).

Criterion → test mapping

Criterion Test
Reading list page renders links grouped by category from a data file tests/reading-list.test.js → "renders links grouped by category from the data file"; "renders correctly against a 500+ entry fixture" (per-section grouping and counts); "renderer module loads the reading list data file"; "reading page renders a reading list container and loads the renderer module"
Each entry shows a title, a link URL, and an optional one-line note tests/reading-list.test.js → "each rendered link has a valid href and shows its title, with the note shown only when present"; "every entry in the data file has a title, a category, and a valid http(s) url"; "seed data includes an entry without a note so the optional path is exercised"
Page stays responsive and usable with 500+ entries in the data file tests/reading-list.test.js → "renders correctly against a 500+ entry fixture" (500 entries all rendered, grouping intact, render time bound < 5s); responsive two-column layout in css/style.css
A new link can be added by editing only the data file, with no code changes tests/reading-list.test.js → "a new link can be added by editing only the data file"; README "Reading list" section documents the schema and the edit-only workflow

Out of scope (as specified in the issue)

  • No backend, login, or server-side storage.
  • No automated import or scraping of bookmarks in this pass.
  • No full-text search or filtering UI yet.

Risks / notes

  • package.json test script changed from node --test tests/ to node --test: on some Node builds (reproduced locally on v22.23.2) --test treats a directory argument as a module path and exits before running any tests; no-arg discovery finds the same tests/*.test.js files on Node ≥ 18.13 and is what CI now runs.
  • CI: .gitea/workflows/ci.yml runs npm test; this Gitea instance has no Actions runner (same situation as PR #6), so runs stay "Waiting to run" (pending) — there are no failing checks, and the suite is verified green locally (30/30, npm test).
  • Seed data in data/reading-list.js is starter content the owner can replace/extend.

Rollback

  • One new page plus one data file (plus renderer, nav link, CSS, and tests); revert the branch commits to remove — no migration, per the issue's rollback note.
## Summary Implements the reading list page for issue #8 — a static page that renders curated links grouped by category from a single data file. No backend, no automated import/scraping, no search/filter UI (per scope). ### What changed - `reading.html` — new page with a `#reading-list` container; loads the renderer module (`js/reading-list.js`). - `data/reading-list.js` — the single data file. Schema: `title`, `url`, `category`, and an optional one-line `note`. Adding a link means editing this file only. - `js/reading-list.js` — pure renderer: groups entries by category (first-seen order), HTML-escapes all text, skips invalid entries, de-duplicates category ids; guarded browser wiring renders the data file into the page. - Navigation — Home and Contact pages link to `reading.html`; the Reading page marks its own link `aria-current="page"`. - `css/style.css` — category/list styles; two-column grid on wide screens and `overflow-wrap` on links so 500+ entries stay scannable and usable. - `tests/reading-list.test.js` — component, data-integrity, renderer, and 500-entry fixture tests. - `package.json` — test script now `node --test` (see Risks). ### Criterion → test mapping | Criterion | Test | | --- | --- | | Reading list page renders links grouped by category from a data file | `tests/reading-list.test.js` → "renders links grouped by category from the data file"; "renders correctly against a 500+ entry fixture" (per-section grouping and counts); "renderer module loads the reading list data file"; "reading page renders a reading list container and loads the renderer module" | | Each entry shows a title, a link URL, and an optional one-line note | `tests/reading-list.test.js` → "each rendered link has a valid href and shows its title, with the note shown only when present"; "every entry in the data file has a title, a category, and a valid http(s) url"; "seed data includes an entry without a note so the optional path is exercised" | | Page stays responsive and usable with 500+ entries in the data file | `tests/reading-list.test.js` → "renders correctly against a 500+ entry fixture" (500 entries all rendered, grouping intact, render time bound < 5s); responsive two-column layout in `css/style.css` | | A new link can be added by editing only the data file, with no code changes | `tests/reading-list.test.js` → "a new link can be added by editing only the data file"; README "Reading list" section documents the schema and the edit-only workflow | ### Out of scope (as specified in the issue) - No backend, login, or server-side storage. - No automated import or scraping of bookmarks in this pass. - No full-text search or filtering UI yet. ### Risks / notes - `package.json` test script changed from `node --test tests/` to `node --test`: on some Node builds (reproduced locally on v22.23.2) `--test` treats a directory argument as a module path and exits before running any tests; no-arg discovery finds the same `tests/*.test.js` files on Node ≥ 18.13 and is what CI now runs. - CI: `.gitea/workflows/ci.yml` runs `npm test`; this Gitea instance has no Actions runner (same situation as PR #6), so runs stay "Waiting to run" (pending) — there are no failing checks, and the suite is verified green locally (30/30, `npm test`). - Seed data in `data/reading-list.js` is starter content the owner can replace/extend. ### Rollback - One new page plus one data file (plus renderer, nav link, CSS, and tests); revert the branch commits to remove — no migration, per the issue's rollback note.
bot-implementer added 6 commits 2026-08-25 12:30:37 +00:00
`node --test <dir>` treats a directory argument as a module path on some
Node builds (reproduced on v22.23.2), so `npm test` failed before running
any tests. `node --test` with no args discovers the same tests/ files
portably.
Single source of truth for the reading list page. Entries carry a title,
url, category, and an optional one-line note; adding a link means editing
this file only.
Renders the data file as category-grouped HTML. Pure string builder with
guarded browser wiring so it is unit-testable in Node; escapes all text,
skips invalid entries, and handles duplicate category slugs.
New reading.html renders the data file into a #reading-list container and
marks itself as the current page. Home and contact pages gain a Reading nav
link. CSS adds responsive category/list styles (two columns on wide
screens) so long lists stay scannable.
Covers: page wiring and nav reachability, data-file integrity, grouped
rendering, valid hrefs, optional notes, HTML escaping, invalid-entry
skipping, data-only extensibility, and a 500-entry fixture (counts,
grouping, and a generous render-time bound).
kpcto merged commit 5bd86f3f9b into main 2026-08-25 12:33:22 +00:00
kpcto deleted branch feature/8 2026-08-25 12:33:22 +00:00
Member
agent: tester
phase: complete
verdict: pass
target: Fabrika/PersonalBlog PR #9 "[Story] Reading list page" (feature/8 -> main)
pr_url: https://git.stevanovic.co.uk/Fabrika/PersonalBlog/pulls/9

acceptance_criteria:
  - id: AC1
    criterion: "Reading list page renders links grouped by category from a data file"
    status: pass
    evidence: >
      reading.html renders a #reading-list container and loads js/reading-list.js; the
      renderer imports data/reading-list.js and groupByCategory() emits one <section
      class="reading-category"> per category in first-seen order (4 categories verified).
  - id: AC2
    criterion: "Each entry shows a title, a link URL, and an optional one-line note"
    status: pass
    evidence: >
      Each entry renders <a class="reading-link" href="...">title</a> (href allow-listed to
      http/https) plus an optional <p class="reading-note"> only when note is present. Seed
      data includes an entry with no note, exercising the optional path.
  - id: AC3
    criterion: "Page stays responsive and usable with 500+ entries in the data file"
    status: pass
    evidence: >
      Renderer is a pure O(n) string builder. Independent stress test rendered 5000 entries
      in ~90ms (well under the 5s bound). CSS adds a 2-column grid @media (min-width: 40rem)
      and overflow-wrap:anywhere so long lists stay scannable.
  - id: AC4
    criterion: "A new link can be added by editing only the data file, with no code changes"
    status: pass
    evidence: >
      data/reading-list.js is the single source of truth. Appending one entry to the array
      (zero code changes) renders correctly; README documents the edit-only workflow.

test_results:
  implementer_suite: pass (30/30) via `npm test` -> `node --test` on Node v22.23.2
  independent_probe: pass (7/7) — grouping, title/url/note, 5000-entry stress, edit-only add,
    HTML-escaping + javascript:/ftp: URL rejection, page wiring
  note: >
    package.json test script changed `node --test tests/` -> `node --test`. Confirmed the old
    form fails on Node v22.23.2 (treats the dir arg as a module path); the new form discovers
    and runs all tests/*.test.js. Correct fix.

ci:
  workflow: .gitea/workflows/ci.yml (`npm test` on pull_request + push to main)
  pr_status: pending — "CI / Run tests (pull_request)" -> "Waiting to run"
  run_url: https://git.stevanovic.co.uk/Fabrika/PersonalBlog/actions/runs/5/jobs/0
  runs_page: https://git.stevanovic.co.uk/Fabrika/PersonalBlog/actions/runs/5
  status_api: https://git.stevanovic.co.uk/api/v1/repos/Fabrika/PersonalBlog/statuses/e4a192baabaa211191abee7146b231f3c5cb66e8
  note: >
    No Actions runner is registered on this instance (pre-existing condition: merged PR #7
    has been "Waiting to run" since 10:48 and still merged). Suite verified green locally.

constraints_respected:
  - did not edit the implementer's tests or any file in the PR
```yaml agent: tester phase: complete verdict: pass target: Fabrika/PersonalBlog PR #9 "[Story] Reading list page" (feature/8 -> main) pr_url: https://git.stevanovic.co.uk/Fabrika/PersonalBlog/pulls/9 acceptance_criteria: - id: AC1 criterion: "Reading list page renders links grouped by category from a data file" status: pass evidence: > reading.html renders a #reading-list container and loads js/reading-list.js; the renderer imports data/reading-list.js and groupByCategory() emits one <section class="reading-category"> per category in first-seen order (4 categories verified). - id: AC2 criterion: "Each entry shows a title, a link URL, and an optional one-line note" status: pass evidence: > Each entry renders <a class="reading-link" href="...">title</a> (href allow-listed to http/https) plus an optional <p class="reading-note"> only when note is present. Seed data includes an entry with no note, exercising the optional path. - id: AC3 criterion: "Page stays responsive and usable with 500+ entries in the data file" status: pass evidence: > Renderer is a pure O(n) string builder. Independent stress test rendered 5000 entries in ~90ms (well under the 5s bound). CSS adds a 2-column grid @media (min-width: 40rem) and overflow-wrap:anywhere so long lists stay scannable. - id: AC4 criterion: "A new link can be added by editing only the data file, with no code changes" status: pass evidence: > data/reading-list.js is the single source of truth. Appending one entry to the array (zero code changes) renders correctly; README documents the edit-only workflow. test_results: implementer_suite: pass (30/30) via `npm test` -> `node --test` on Node v22.23.2 independent_probe: pass (7/7) — grouping, title/url/note, 5000-entry stress, edit-only add, HTML-escaping + javascript:/ftp: URL rejection, page wiring note: > package.json test script changed `node --test tests/` -> `node --test`. Confirmed the old form fails on Node v22.23.2 (treats the dir arg as a module path); the new form discovers and runs all tests/*.test.js. Correct fix. ci: workflow: .gitea/workflows/ci.yml (`npm test` on pull_request + push to main) pr_status: pending — "CI / Run tests (pull_request)" -> "Waiting to run" run_url: https://git.stevanovic.co.uk/Fabrika/PersonalBlog/actions/runs/5/jobs/0 runs_page: https://git.stevanovic.co.uk/Fabrika/PersonalBlog/actions/runs/5 status_api: https://git.stevanovic.co.uk/api/v1/repos/Fabrika/PersonalBlog/statuses/e4a192baabaa211191abee7146b231f3c5cb66e8 note: > No Actions runner is registered on this instance (pre-existing condition: merged PR #7 has been "Waiting to run" since 10:48 and still merged). Suite verified green locally. constraints_respected: - did not edit the implementer's tests or any file in the PR ```
Sign in to join this conversation.