Activate tabs containing search matches#14053
Merged
Conversation
Collaborator
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Complements the persistence test by confirming that quarto-hrChanged does clear marks after the 1000ms registration delay elapses. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR #13442 removed scroll-based highlight clearing. This commit adds Playwright tests verifying: scroll events never clear marks, query-change clearing still works, and no marks appear without ?q= parameter. Restores test fixture files dropped during rebase (source files were in the skipped JS fix commit). Updates changelog to credit @jtbayly and reference both #9802 and #14047. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
85b9c91 to
a5434e6
Compare
d1f5047 to
6ac6527
Compare
Collaborator
Author
|
Rebased on the updated All 24 Playwright tests pass (3 highlight + 5 tabset × 3 browsers). |
The previous test dispatched quarto-hrChanged/quarto-sectionChanged custom events, which were leftovers from when search code listened to those events. Since #13442 removed those listeners entirely, dispatching those events tested nothing. Replace with actual scroll behavior which is the real user scenario from #14047. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace .aa-DetachedSearchButton and .aa-Input with ARIA role
locators (getByRole('button'), getByRole('searchbox')). These are
resilient to Algolia autocomplete class name changes and follow
Playwright best practices used elsewhere in the test suite.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After highlight() adds <mark> tags to matching text, the new activateTabsWithMatches() function detects marks inside inactive Bootstrap tab panes and activates the first one. If the already-active tab in a tabset contains a match, no switch occurs. Includes test fixture for manual and future Playwright testing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Escape pane IDs with CSS.escape() before interpolating into
querySelector selectors to handle special characters in
user-derived tab IDs. Use closest(".tab-content") instead of
parentElement for resilience to intermediate wrapper elements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
activateTabsWithMatches() now walks up ancestor tab panes so matches inside nested tabsets activate both outer and inner tabs (outermost first via DOM depth sorting). Tab activation is deferred from DOMContentLoaded to a pageshow listener. This ensures it runs after tabsets.js restores grouped tab state from localStorage, so search results override stored tab preferences without flash. Listener ordering is guaranteed because tabsets.js (a module) registers its pageshow handler before DOMContentLoaded fires. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Five tests covering tab activation when navigating to a page with ?q=: - Match in inactive tab activates that tab - Match in already-active tab keeps it (no unnecessary switch) - Match outside tabs leaves tab state unchanged - Match in nested tabset activates both outer and inner tabs - Search activation overrides localStorage tab preference TDD verified: tests fail against stock v1.9.20 (3 of 5 fail where tab activation is needed), pass with our changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap bootstrap.Tab.show() call in try-catch so a single broken tab does not prevent activation of remaining tabsets with search matches. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…owing Replace empty catch block with console.debug so Bootstrap Tab API failures leave a diagnostic breadcrumb. Also add explicit format: html to search-tabsets test fixture for consistency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6ac6527 to
4739866
Compare
…tests
Tab pane IDs like `tabset-1-2` are auto-generated by Pandoc and break if
the fixture structure changes. Replace with `getByRole('tab', { name })`
and `toHaveClass(/active/)`, matching the pattern used in html-tabsets.spec.ts.
Section-scoped locators disambiguate duplicate tab names (R, Python).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After activating tabs that contain search matches, scroll the first visible <mark> element into view. This completes the UX: the user lands on the page with the correct tab open and the match centered in the viewport. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
This PR is to be merged AFTER #14049 which as a prerequisite fix
Tip
Demo website: https://cderv.github.io/quarto-search-highlight-demo/
When navigating to a page with
?q=termin the URL, search matches inside inactive Bootstrap tabs are invisible because the matching tab is not activated. This builds on #14049 (mark-clearing fix) to also activate tabs that contain highlighted matches.How it works
After
highlight()wraps matches in<mark>elements during DOMContentLoaded, apageshowlistener activates any tab panes containing marks. The pageshow timing is key —tabsets.js(loaded as an ES module) registers its own pageshow handler during module execution, which runs before DOMContentLoaded. By registering our handler during DOMContentLoaded, listener ordering guarantees we run after tabsets.js restores tab state from localStorage.This means search activation wins over stored tab preferences, which is the intended UX — if you searched for something in the Python tab, you want to see the Python tab regardless of your stored language preference.
Design decisions
bootstrap.Tab.show()used instead of.click()to avoid triggering group sync in tabsets.js. Search activation is intentionally local to the tabset containing the match..tab-paneelements, groups by.tab-content, activates outermost first.{once: true}and!event.persistedguard on the pageshow listener for clean teardown.Test plan
Fixes #14047 (partial — tab activation aspect)