User story
As a mobile user, I want easy access to a table of contents to aid my navigation throughout an article's content.
Requirements
Other than the detail that can easily be extracted from looking at the designs (see below) or acceptance criteria (below), here's what else to look out for:
- When editors chose to not show the TOC through the use of NOTOC magic word/template, we will respect that and not show the TOC
- Minerva will hide the TOC by default, but will show a collapsible TOC on screens wider than @min-width-breakpoint-tablet: 720px;. We need to ensure that the existing TOC is always hidden
Since we chose to respect the NOTOC directive, we can probably re-use the DOM that already exists on-page (document.querySelector( '#toc > ul' )).
If for whatever reason that does not suffice, we can fetch it from the parse API.
Below is a quick code sample that would produce the same HTML the parsers currently do:
async function getTocHtml() { const response = await new mw.Api().get( { action: 'parse', oldid: mw.config.get( 'wgRevisionId' ), prop: 'tocdata' } ); const sectionsByParent = response.parse.tocdata.sections.reduce( ( restructured, section ) => { const parent = section.number.replace( /(^|\.)[^/.]+$/, '' ); return { ...restructured, [ parent ]: ( restructured[ parent ] || [] ).concat( section ) }; }, {} ); const parseSectionsFor = ( parent ) => { if ( !( parent in sectionsByParent ) ) { return ''; } return '<ul>' + sectionsByParent[ parent ].map( ( section ) => `<li class="toclevel-${ section.tocLevel } tocsection-${ section.index }">` + `<a href="#${ section.anchor }">` + `<span class="tocnumber">${ section.number }</span>` + `<span class="toctext">${ section.line }</span>` + // note: can contain HTML (and is allowed to), e.g. italics '</a>' + parseSectionsFor( section.number ) + '</li>' ).join( '' ) + '</ul>'; }; return parseSectionsFor( '' ); }
Note that when fetching from the API, we ought to:
- make sure it is for the correct revision (mw.config.get( 'wgRevisionId' ))
- reduce calls as much as possible
- only load as needed
- cache the results
- insofar possible, potentially even attempt to use existing TOC first, before falling back to API
Design
This ticket only concerns the TOC, not the thing that invokes it (sticky header of floating button) and how exactly it presents in either design.
This ticket is specifically for building the TOC and whatever functionality is similar in both designs - the specific that differ (e.g. positioning/design of the container) will be tackled in other tickets.
- Sticky header: https://phabricator.wikimedia.org/T410325#11413314
- Floating button: https://phabricator.wikimedia.org/T410325#11413373
Acceptance criteria
- Existing Minerva TOC is guaranteed to be hidden, also on wide screens
- TOC does not show when editors explicitly disabled it through NOTOC (e.g. Main page)
- TOC gets an additional first entry (Top) which, when clicked, scroll the user to the top of the page
- Clicking anything in the TOC closes it and scrolls to the position of the item clicked
- With TOC open, keyboard navigation is trapped within the TOC container
- Escape key closes TOC
- Nice to have for instrumentation outside of repo: TOC open/close emits readerExperiments.toc.open & readerExperiments.toc.close, while initialization of the TOC emits readerExperiments.toc.init
- Nice to have for link sharing: TOC open/close is also triggered by url hash (#toc) changes, and opening/closing TOC programmatically triggers those