User:JDrewniak (WMF)/notes/The issues with PageIssues js
Responsibilities
[edit]- Define an initialization module for entire page-issues feature. Currently this means:
M.define( 'skins.minerva.scripts/pageIssues', {
init: initPageIssues,
test: {
getAllIssuesSections: getAllIssuesSections,
createBanner: createBanner
}
} );
The initialization function, initPageIssues( [overlay, page] )
and essentially the whole feature, does the following:
- identifies pages-issues on the page
- parses their severities
- adds classes & icons to style them accordingly
- attaches events that open the overlay
- when clicking on the top page-issues, overlay should display all issues
- when clicking on section issues, only display issues from that section
- adds a 'read more' button.
This is the feature in a nutshell, (minus instrumentation). The functionality should work accordingly for the following contexts:
- only work on the main namespace, for the following scenarios:
- single issues at the top of the page
- groups of issues at the top of the page
- single issues in sections
- group of issues in sections
The majority of effort has been made to parse the enwiki page-issue markup (ambox) templates, given that many wikis use that as a base for their templates.
Creates a link element.
function createLinkElement( labelText ) {
return $( '<a class="cleanup mw-mf-cleanup"></a>' ) .text( labelText );
}
Given a page
, create (i.e. invoke) all the page-issues features, in-place, on a single (or not!) DOM element, populate a global allIssues
variable, and return the modified page-issues elements (or remove them!).
Code walkthrough
- GIVEN
page
,(object)labelText
,(string)section
,(number)inline
(boolean - the new or old treatment?),overlayManager
(object)
- DEFINE
- the overlay URL fragment
- the element CSS
selectors
- an empty array of
issues
- IF we're working with issues at the top of the page:
- select all the issues on the page ( var $metaData),
- otherwise select the issues in the section
- remove something called
.NavFrame
from that selection - LOOP through each element in the selection
- IF the element contains exactly zero child elements that match the
selector
, and has text:- create a PageIssue data model of that element using
pageIssuesParser.extract
- add that PageIssue to the array of
issues
.
- create a PageIssue data model of that element using
- IF the element contains exactly zero child elements that match the
- POPULATE
allIssues
with theissues
for thissection
. - IF there are
issues
, andinline
(new treatment) is true- APPEND and icon to the first issue
- CREATE a "learn more" button
- IF this is a "multiple issues" template, insert the button in one place
- ELSE insert the button in a different place
- ATTACH the click handlers
- ELSE
createLinkElement()
and insert that "this page has issues" link at the top of the page.- remove the selected issues from the DOM (var $metaData)
- RETURN selected issues (var $metaData)
getIssues
[edit]Given a page section
(number) or KEYWORD_ALL_SECTIONS
(string), returns the value of that section from allIssues
array. Hopefully that array exists in the outer scope and is populated.
Code walkthrough
- GIVEN
section
- IF
section
is NOTKEYWORD_ALL_SECTIONS
:- RETURN all issues for that section OR empty array
- IF
allIssues.all
exists- RETURN
allIssues.all
- RETURN
- ELSE RETURN an array containing all the key values of
allIssues
If minerva is run in desktop mode, it expects the property allIssues.all
to exist, and returns that value. Where is allIssues.all
set?
getAllIssuesSections
[edit]Given an array (or is it a map?) called allIssues
, flips that map and returns a flat array of numbers representing the section of each issue on the page. If that issue is part of a group, (i.e. multiple issues template) it only returns one section value for that group.
Code walkthrough
- GIVEN
allIssues
: - RETURN REDUCE:
allIssues
([]
,section
)- IF
section
has issues- LOOP through each issue
- IF issue is
.grouped
- AND the last issue is
.grouped
- AND the last issues has already been added to
[]
- set the last value in
[]
to the current issue section
- set the last value in
- ELSE
- push the section number into
[]
- push the section number into
- IF issue is
- LOOP through each issue
- IF
Do we need to set the last value in []
to the current issue section? Shouldn't that value already be set?
initPageIssues
[edit]Given overlayManager
and page
, creates the page-issue banners depending on the namespace and whether or not the new page-issues treatment has been enabled.
Code walkthrough
- GIVEN
ovevrlayManager
,page
- DEFINE
$lead
withpage.getLeadSectionElement()
I think this is a proxy for telling whether or not we're in desktop mode or not?issueOverlayShowAll
if we're in category or talk namespace, or there is no$lead
inline
if new treatment is enabled and we're in the main namespace.
- IF
newTreatmentEnabled
add a'issues-group-B
class to the element. - IF we're in the talk or category namespace:
createBanner
with a mw.msg for talk pages,KEYWORD_ALL_SECTIONS
,inline
(false)
- ELSE IF we're in the main namespace:
- SET
label
to a mw.msg for main pages "this page has issues" - IF
issueOverlayShowAll
we're in desktop modecreateBanner()
withlabel
,KEYWORD_ALL_SECTIONS
,inline
(true if new treatment enabled)
- ELSE we're in mobile mode
createBanner()
withpage
,label
, section "0",inline
- IF
newTreatmentEnabled
- FIND
Page.HEADING_SELECTOR
elements in pagepage
- FOREACH heading element
- FIND a section number in that heading element
- IF section number exists
createBanner
with that section number
- FIND
- SET
- add a route to
overlayManager
for section numbers and all issues overlay- when accessed, return new PageIssuesOverlay for that section and
getIssues( section )
- when accessed, return new PageIssuesOverlay for that section and
Suggestions
[edit]Mise en place! we should gather required variables & DOM elements up-front so that we don't have to do these operations as a side-effect of other functions.
allIssues
seems like an important variable used to represent all page-issues on a given page. It's currently populated as a side-effect ofcreateBanner()
. It should be populated before calling this function, OR, issues could be a property ofPage
.createBanner()
shouldn't have to loop through DOM nodes to find issues, this process should happen before createBanner is called, and createdBanner should be given a data-model of the page-issues instead (.i.e, allIssues). This would ease testing as well.createBanner()
is responsible for creating both the new banner and the old banner. This could be separated into separate "createNewBanner" and "createOldBanner" functions (but with a better names).- Both
createBanner()
andinitPageIssues()
do multiple checks for old vs new treatment. CreateBanner wouldn't have to make this check if was split into create "old banner" and "new banner" functions. initPageIssues()
contains deeply nested conditionals, which make the function hard to reason about. The logic boils down to "what namespace are we on?" "is the new treatment enabled?" "are we in desktop mode?" and "should the overlay show all issues?". These questions could be answered with their own function. Also, these questions should be answered before calling createBanner.initPageIssues()
callscreateBanner()
4 times. Instead, it could assemble the required parameters, and callcreateBanner()
once at the end of the function.