Skip to content

👌 Sync tabs via change events, open on URL hash, persist choice#284

Merged
chrisjsewell merged 1 commit into
mainfrom
brief/11-tabs-js
Jul 14, 2026
Merged

👌 Sync tabs via change events, open on URL hash, persist choice#284
chrisjsewell merged 1 commit into
mainfrom
brief/11-tabs-js

Conversation

@chrisjsewell

@chrisjsewell chrisjsewell commented Jul 14, 2026

Copy link
Copy Markdown
Member

Three tabs-JS improvements, plus a new declarative config option.

Changes

  • Click+drag on tab causes synced tabs to go out of sync #46 — sync via change events: synchronised-tab updates now hang off the radio input's change event instead of label.onclick, so click-drag releases and keyboard arrow selection sync correctly. Programmatic sync sets input.checked directly (never .click()), so no event cascades.
  • Open tab when its :name: is used in URL #239 — URL fragments open tabs: a hash targeting a tab label (:name:) or any element inside a tab panel selects that tab — including walking up nested tab-sets so every enclosing panel opens and the target is actually visible — then scrolls it into view. Also handles hashchange without a reload. Deliberate decisions: hash selection does not propagate to synced siblings and is not persisted (a fragment names one specific tab).
  • Support sticky tab selection #103 — persistent, configurable storage: selection storage moves from sessionStorage to localStorage (behaviour change: tab choice now survives new browsing sessions), with a new TOML-representable option sd_tabs_storage_prefix (default unchanged key prefix; empty string disables persistence — useful for shared origins like RTD subprojects). The value reaches the JS declaratively as a data- attribute on the script tag. Storage access is fully guarded: browsers that throw on localStorage (blocked site data, disabled storage) keep all tab functionality except persistence.
  • Removed the stray console.log in the query-param path; fixed the header comment that wrongly claimed localStorage previously.

Verification

  • Playwright runtime suite, 16/16 on the built docs: change-event sync both directions, hash opening (label, in-panel target, nested panels with visibility asserted, hashchange), localStorage persistence across a real reload, empty-prefix disable, storage-throwing browser still fully functional, and attribute HTML-escaping (a hostile prefix cannot break out of the tag).
  • Independent review verified the config's rebuild="env" category empirically: changing the prefix and rebuilding incrementally updates the script tag on unrebuilt pages — no staleness. Hash precedence, same-group multi-set, unsynced named tabs, and non-tab hashes all probed clean.
  • Full suite on pinned Sphinx 7.2.6 / 7.4.7 / 8.2.3 (175 passed each); add_js_file(**attrs) attribute forwarding verified on the 7.2.6 floor; tsc strict JSDoc green; new tests/test_tabs_js.py covers the script-tag attribute for default/custom/empty config plus DOM-order pinning.

Closes #46
Closes #239
Closes #103

@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.74%. Comparing base (abbf459) to head (7625cc5).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #284      +/-   ##
==========================================
+ Coverage   90.72%   90.74%   +0.02%     
==========================================
  Files          13       13              
  Lines        1207     1210       +3     
==========================================
+ Hits         1095     1098       +3     
  Misses        112      112              
Flag Coverage Δ
pytests 90.74% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- #46: sync grouped tabs via the radio input's `change` event instead of
  `label.onclick`, so click-and-drag release and keyboard activation stay in
  sync; programmatic sync sets `input.checked` directly (never `.click()`).
- #239: open the targeted tab when the URL fragment points at a tab label, or
  at an element inside a tab panel, both on load and on `hashchange`.
- #103: persist selections in `localStorage` (was `sessionStorage`) so they
  survive new browsing sessions, with a configurable `sd_tabs_storage_prefix`
  (empty string disables persistence), delivered to JS as a script-tag data
  attribute. Also drop the query-param `console.log`.

Closes #46, #239, #103
@chrisjsewell chrisjsewell merged commit a12b0dc into main Jul 14, 2026
23 checks passed
@chrisjsewell chrisjsewell deleted the brief/11-tabs-js branch July 14, 2026 22:24
chrisjsewell added a commit that referenced this pull request Jul 15, 2026
Modernizes the FontAwesome integration around three declarative config
options.

## Changes

- **v6 roles**: `fa-solid` / `fa-brands` / `fa-regular`, emitting
exactly those classes. Purely additive: the concise
`fa`/`fas`/`fab`/`far` roles are equally supported, unchanged, with no
plans to remove them — the docs present both spellings as first-class,
noting the `fa-*` names are only *required* for FontAwesome v6+ setups
that drop the compatibility aliases (most notably Pro kits, the #174
failure mode), since the role name is emitted verbatim as the CSS class.
- **`sd_fontawesome_source`** (`"none"` default / `"cdn"`) +
**`sd_fontawesome_cdn_url`**: opt-in CDN loading via `app.add_css_file`,
replacing the docs' copy-paste `html_css_files` instruction. The docs
now dogfood it.
- **`sd_fontawesome_latex`** grows string values while keeping
`True`/`False` working (`True` ≡ `"fontawesome"`, output verified
byte-identical to main): `"fontawesome5"` loads `fontawesome5.sty` and
emits `\faIcon{name}` / `\faIcon[regular]{name}` (brands resolve by bare
name — mapping independently verified against the package's completion
data). This resolves the #242 class of clash: if your theme loads
`fontawesome5`, set the same value so both agree. The stored value stays
raw (TOML-round-trippable); normalization is a computed property.
- Per-icon "not included in output" warnings now fire once per build
(`once=True`, verified on the 7.2 floor).
- Docs: FA section rewritten around the config, with an explicit
Pro-kits subsection, the both-spellings-supported note, the LaTeX value
table, and a caveat that the LaTeX packages resolve FA5-era icon names
(v6-renamed icons render in HTML but not in PDF).

## Verification

- Independent review: no correctness findings. Verified empirically:
incremental rebuilds propagate config flips to unrebuilt pages
(`rebuild="env"`); absolute CDN URLs are emitted verbatim (no `?v=`
mangling, no copy attempt) on 7.2.6 and 9.x; epub with `"cdn"` behaves
identically to the pre-existing `html_css_files` approach; `bool|str`
confval type-checking clean across the range; no duplicate CDN link in
the dogfooding docs build.
- Legacy parity locked by test: `sd_fontawesome_latex = True` produces
the same `\usepackage{fontawesome}` + `\faicon{...}` output as main.
- Full suite on pinned Sphinx 7.2.6 / 7.4.7 / 8.2.3 (184 passed each;
204 on the post-#284 rebase); new `tests/test_fontawesome.py` covers
roles, CDN link exactly-once/absent, both LaTeX packages, and
once-per-build warnings.

Closes #174
Closes #242
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.

Open tab when its :name: is used in URL Support sticky tab selection Click+drag on tab causes synced tabs to go out of sync

1 participant