diff --git a/includes/MinervaHooks.php b/includes/MinervaHooks.php index 2df6164..6583c0c 100644 --- a/includes/MinervaHooks.php +++ b/includes/MinervaHooks.php @@ -75,24 +75,26 @@ class MinervaHooks { 'mobile.startup', 'skins.minerva.notifications.badge', 'mediawiki.user', - 'mediawiki.experiments', - 'mobile.issues' + 'mediawiki.experiments' ], 'localBasePath' => dirname( __DIR__ ), 'remoteSkinPath' => 'MinervaNeue', 'targets' => [ 'mobile', 'desktop' ], 'scripts' => [ // additional scaffolding (minus initialisation scripts) - 'resources/skins.minerva.scripts/pageIssueLogger.js', - 'resources/skins.minerva.scripts/pageIssueParser.js', + 'tests/qunit/skins.minerva.scripts/stubs.js', + + 'resources/skins.minerva.scripts/pageIssuesLogger.js', + 'resources/skins.minerva.scripts/pageIssuesParser.js', 'resources/skins.minerva.scripts/DownloadIcon.js', 'resources/skins.minerva.scripts/AB.js', - 'resources/skins.minerva.scripts/cleanuptemplates.js', + 'resources/skins.minerva.scripts/PageIssuesOverlay.js', + 'resources/skins.minerva.scripts/pageIssues.js', // test files 'tests/qunit/skins.minerva.scripts/test_DownloadIcon.js', - 'tests/qunit/skins.minerva.scripts/test_pageIssueParser.js', + 'tests/qunit/skins.minerva.scripts/test_pageIssuesParser.js', 'tests/qunit/skins.minerva.scripts/test_AB.js', - 'tests/qunit/skins.minerva.scripts/test_cleanuptemplates.js', + 'tests/qunit/skins.minerva.scripts/test_pageIssues.js', 'tests/qunit/skins.minerva.notifications.badge/test_NotificationBadge.js' ], ]; diff --git a/resources/skins.minerva.editor/init.js b/resources/skins.minerva.editor/init.js index efc27ee..59f56fb 100644 --- a/resources/skins.minerva.editor/init.js +++ b/resources/skins.minerva.editor/init.js @@ -6,7 +6,7 @@ isEditable = !isReadOnly && mw.config.get( 'wgIsProbablyEditable' ), blockInfo = mw.config.get( 'wgMinervaUserBlockInfo', false ), router = require( 'mediawiki.router' ), - issues = M.require( 'skins.minerva.scripts/cleanuptemplates' ), + issues = M.require( 'skins.minerva.scripts/pageIssues' ), overlayManager = M.require( 'skins.minerva.scripts/overlayManager' ), loader = M.require( 'mobile.startup/rlModuleLoader' ), Icon = M.require( 'mobile.startup/Icon' ), diff --git a/resources/skins.minerva.scripts/PageIssuesOverlay.js b/resources/skins.minerva.scripts/PageIssuesOverlay.js new file mode 100644 index 0000000..4143a15 --- /dev/null +++ b/resources/skins.minerva.scripts/PageIssuesOverlay.js @@ -0,0 +1,94 @@ +( function ( M ) { + var Overlay = M.require( 'mobile.startup/Overlay' ), + util = M.require( 'mobile.startup/util' ); + + /** + * @typedef {Object} PageIssue + * @property {string} icon html associated with Icon component + * @property {string} text html explaining the details of the issue + * @property {string} severity associated with the issue + */ + + /** + * Obtain severity associated with a given $target node + * by looking at associated parent node (defined by template) + * + * @param {jQuery.Object} $target + * @return {string} severity as defined in associated PageIssue + */ + function parseSeverity( $target ) { + return $target.parents( '.issue-notice' ).data( 'severity' ); + } + + /** + * Overlay for displaying page issues + * @class PageIssuesOverlay + * @extends Overlay + * + * @param {Object} options Configuration options + * @param {string} options.headingText + * @param {PageIssue[]} options.issues list of page issues for display + * @fires PageIssuesOverlay#link-edit-click + * @fires PageIssuesOverlay#link-internal-click + */ + function PageIssuesOverlay( options ) { + options.heading = '' + options.headingText + ''; + Overlay.call( this, options ); + } + + OO.mfExtend( PageIssuesOverlay, Overlay, { + /** + * @memberof PageIssuesOverlay + * @instance + */ + className: 'overlay overlay-issues', + /** + * @memberof PageIssuesOverlay + * @instance + */ + events: util.extend( {}, Overlay.prototype.events, { + 'click a:not(.external):not([href*=edit])': 'onInternalClick', + 'click a[href*="edit"]': 'onEditClick' + } ), + /** + * @memberof PageIssuesOverlay + * @instance + */ + templatePartials: util.extend( {}, Overlay.prototype.templatePartials, { + content: mw.template.get( 'skins.minerva.scripts', 'PageIssuesOverlayContent.hogan' ) + } ), + /** + * Event that is triggered when an internal link inside the overlay is clicked + * This event will not be triggered if the link contains the edit keyword, in which + * case onEditClick will be fired + * This is primarily used for instrumenting page issues + * (see https://meta.wikimedia.org/wiki/Schema:PageIssues) + * @param {jQuery.Event} ev + * @memberof PageIssuesOverlay + * @instance + */ + onInternalClick: function ( ev ) { + /** + * @event PageIssuesOverlay#link-internal-click + * @param {string} severity + */ + this.emit( 'link-internal-click', parseSeverity( this.$( ev.target ) ) ); + }, + /** + * Event that is triggered when an edit link inside the overlay is clicked + * This is primarily used for instrumenting page issues + * (see https://meta.wikimedia.org/wiki/Schema:PageIssues) + * @param {jQuery.Event} ev + * @memberof PageIssuesOverlay + * @instance + */ + onEditClick: function ( ev ) { + /** + * @event PageIssuesOverlay#link-edit-click + * @param {string} severity + */ + this.emit( 'link-edit-click', parseSeverity( this.$( ev.target ) ) ); + } + } ); + M.define( 'skins.minerva.scripts/PageIssuesOverlay', PageIssuesOverlay ); +}( mw.mobileFrontend ) ); diff --git a/resources/skins.minerva.scripts/PageIssuesOverlay.less b/resources/skins.minerva.scripts/PageIssuesOverlay.less new file mode 100644 index 0000000..7d3613a --- /dev/null +++ b/resources/skins.minerva.scripts/PageIssuesOverlay.less @@ -0,0 +1,54 @@ +@import '../../minerva.less/minerva.variables'; + +@smallIconSize: 24px; +@largeIconSize: 50px; + +.mw-mf-cleanup { + display: block; + margin: 0; + padding: 0; + font-size: 0.8em; + color: @colorGray7; +} + +// overlay styles +.overlay-issues { + + .cleanup { + > li { + border-bottom: solid 1px @grayLight; + .issue-notice { + padding: @smallIconSize @smallIconSize @smallIconSize 0; + .mw-ui-icon { + float: left; + } + } + + small, + .hide-when-compact { + font-size: 0.8em; + } + + .hide-when-compact { + display: block; + margin: 8px 0; + } + } + } + + .issue-details { + // align the first line to the top of the element + // but line-height should be normal for text that follows (T190469) + > :first-line { + line-height: 1; + } + + // This should match the icon width for the overlay close icon + // which is derived from these two variables: + padding-left: @iconSize + ( 2 * @iconGutterWidth ); + // assume date + small i { + color: @colorGray7; + } + } +} diff --git a/resources/skins.minerva.scripts/PageIssuesOverlayContent.hogan b/resources/skins.minerva.scripts/PageIssuesOverlayContent.hogan new file mode 100644 index 0000000..67f2a01 --- /dev/null +++ b/resources/skins.minerva.scripts/PageIssuesOverlayContent.hogan @@ -0,0 +1,10 @@ + diff --git a/resources/skins.minerva.scripts/init.js b/resources/skins.minerva.scripts/init.js index 0adcd23..4a04751 100644 --- a/resources/skins.minerva.scripts/init.js +++ b/resources/skins.minerva.scripts/init.js @@ -3,7 +3,7 @@ toast = M.require( 'mobile.startup/toast' ), time = M.require( 'mobile.startup/time' ), skin = M.require( 'mobile.init/skin' ), - issues = M.require( 'skins.minerva.scripts/cleanuptemplates' ), + issues = M.require( 'skins.minerva.scripts/pageIssues' ), DownloadIcon = M.require( 'skins.minerva.scripts/DownloadIcon' ), browser = M.require( 'mobile.startup/Browser' ).getSingleton(), loader = M.require( 'mobile.startup/rlModuleLoader' ), diff --git a/resources/skins.minerva.scripts/cleanuptemplates.js b/resources/skins.minerva.scripts/pageIssues.js similarity index 92% rename from resources/skins.minerva.scripts/cleanuptemplates.js rename to resources/skins.minerva.scripts/pageIssues.js index c10254d..c552d89 100644 --- a/resources/skins.minerva.scripts/cleanuptemplates.js +++ b/resources/skins.minerva.scripts/pageIssues.js @@ -11,9 +11,9 @@ CURRENT_NS = config.get( 'wgNamespaceNumber' ), allPageIssuesSeverity, Icon = M.require( 'mobile.startup/Icon' ), - pageIssueLogger = M.require( 'skins.minerva.scripts/pageIssueLogger' ), - pageIssueParser = M.require( 'skins.minerva.scripts/pageIssueParser' ), - CleanupOverlay = M.require( 'mobile.issues/CleanupOverlay' ), + pageIssuesLogger = M.require( 'skins.minerva.scripts/pageIssuesLogger' ), + pageIssuesParser = M.require( 'skins.minerva.scripts/pageIssuesParser' ), + PageIssuesOverlay = M.require( 'skins.minerva.scripts/PageIssuesOverlay' ), // setup ab test abTest = new AB( { testName: 'WME.PageIssuesAB', @@ -64,7 +64,7 @@ } } ); - pageIssue = pageIssueParser.parse( $box.get( 0 ) ); + pageIssue = pageIssuesParser.parse( $box.get( 0 ) ); return { severity: pageIssue.severity, @@ -138,12 +138,12 @@ allIssues[section] = issues; if ( $metadata.length && inline ) { - severity = pageIssueParser.maxSeverity( + severity = pageIssuesParser.maxSeverity( issues.map( function ( issue ) { return issue.severity; } ) ); new Icon( { glyphPrefix: 'minerva', - name: pageIssueParser.iconName( $metadata.get( 0 ), severity ) + name: pageIssuesParser.iconName( $metadata.get( 0 ), severity ) } ).prependTo( $metadata.find( '.mbox-text' ) ); $learnMore = $( '' ) .addClass( 'ambox-learn-more' ) @@ -156,8 +156,8 @@ $learnMore.appendTo( $metadata.find( '.mbox-text' ) ); } $metadata.click( function () { - var pageIssue = pageIssueParser.parse( this ); - pageIssueLogger.log( { + var pageIssue = pageIssuesParser.parse( this ); + pageIssuesLogger.log( { action: 'issueClicked', issuesSeverity: [ pageIssue.severity ] } ); @@ -169,7 +169,7 @@ // In group B, we link to all issues no matter where the banner is. $link.attr( 'href', '#/issues/' + KEYWORD_ALL_SECTIONS ); $link.click( function () { - pageIssueLogger.log( { + pageIssuesLogger.log( { action: 'issueClicked', // empty array is passed for 'old' treatment. issuesSeverity: [] @@ -267,9 +267,9 @@ if ( isLoggingRequired( getIssues( KEYWORD_ALL_SECTIONS ) ) ) { // Enable logging. - pageIssueLogger.subscribe( + pageIssuesLogger.subscribe( newTreatmentEnabled, - pageIssueLogger.newPageIssueSchemaData( + pageIssuesLogger.newPageIssueSchemaData( newTreatmentEnabled, CURRENT_NS, getIssues( KEYWORD_ALL_SECTIONS ).map( formatPageIssuesSeverity ) @@ -279,7 +279,7 @@ // Setup the overlay route. overlayManager.add( new RegExp( '^/issues/(\\d+|' + KEYWORD_ALL_SECTIONS + ')$' ), function ( section ) { - var overlay = new CleanupOverlay( { + var overlay = new PageIssuesOverlay( { issues: getIssues( section ), // Note only the main namespace is expected to make use of section issues, so the heading will always be // minerva-meta-data-issues-section-header regardless of namespace @@ -288,19 +288,19 @@ } ); // Tracking overlay close event. overlay.on( 'Overlay-exit', function () { - pageIssueLogger.log( { + pageIssuesLogger.log( { action: 'modalClose', issuesSeverity: getIssues( section ).map( formatPageIssuesSeverity ) } ); } ); overlay.on( 'link-edit-click', function ( severity ) { - pageIssueLogger.log( { + pageIssuesLogger.log( { action: 'modalEditClicked', issuesSeverity: [ severity ] } ); } ); overlay.on( 'link-internal-click', function ( severity ) { - pageIssueLogger.log( { + pageIssuesLogger.log( { action: 'modalInternalClicked', issuesSeverity: [ severity ] } ); @@ -309,18 +309,18 @@ } ); // Tracking pageLoaded event (technically, "issues" loaded). - pageIssueLogger.log( { + pageIssuesLogger.log( { action: 'pageLoaded', issuesSeverity: allPageIssuesSeverity } ); } - M.define( 'skins.minerva.scripts/cleanuptemplates', { + M.define( 'skins.minerva.scripts/pageIssues', { init: initPageIssues, // The logger requires initialization (subscription). Ideally, the logger would be initialized // and passed to initPageIssues() by the client. Since it's not, expose a log method and hide // the subscription call in cleanuptemplates. - log: pageIssueLogger.log, + log: pageIssuesLogger.log, test: { createBanner: createBanner } diff --git a/resources/skins.minerva.scripts/pageIssueLogger.js b/resources/skins.minerva.scripts/pageIssuesLogger.js similarity index 98% rename from resources/skins.minerva.scripts/pageIssueLogger.js rename to resources/skins.minerva.scripts/pageIssuesLogger.js index 5cea5c4..4a6db0b 100644 --- a/resources/skins.minerva.scripts/pageIssueLogger.js +++ b/resources/skins.minerva.scripts/pageIssuesLogger.js @@ -89,7 +89,7 @@ mwTrack( EVENT_PAGE_ISSUE_LOG, data ); } - M.define( 'skins.minerva.scripts/pageIssueLogger', { + M.define( 'skins.minerva.scripts/pageIssuesLogger', { newPageIssueSchemaData: newPageIssueSchemaData, subscribe: subscribe, log: log diff --git a/resources/skins.minerva.scripts/pageIssueParser.js b/resources/skins.minerva.scripts/pageIssuesParser.js similarity index 98% rename from resources/skins.minerva.scripts/pageIssueParser.js rename to resources/skins.minerva.scripts/pageIssuesParser.js index d924715..01f23ab 100644 --- a/resources/skins.minerva.scripts/pageIssueParser.js +++ b/resources/skins.minerva.scripts/pageIssuesParser.js @@ -156,7 +156,7 @@ /** * @module skins.minerva.scripts/utils */ - M.define( 'skins.minerva.scripts/pageIssueParser', { + M.define( 'skins.minerva.scripts/pageIssuesParser', { /** * Extract an icon for use with the issue. * @param {JQuery.Object} $box element to extract the icon from diff --git a/skin.json b/skin.json index 4d10eff..5234d80 100644 --- a/skin.json +++ b/skin.json @@ -393,7 +393,6 @@ "mediawiki.Title", "mobile.startup", "skins.minerva.mainMenu", - "mobile.issues", "mobile.search.api", "mobile.search", "mobile.references", @@ -416,15 +415,20 @@ "mobile-frontend-redirected-from" ], "styles": [ - "resources/skins.minerva.scripts/styles.less" + "resources/skins.minerva.scripts/styles.less", + "resources/skins.minerva.scripts/PageIssuesOverlay.less" ], + "templates": { + "PageIssuesOverlayContent.hogan": "resources/skins.minerva.scripts/PageIssuesOverlayContent.hogan" + }, "scripts": [ "resources/skins.minerva.scripts/preInit.js", "resources/skins.minerva.scripts/DownloadIcon.js", - "resources/skins.minerva.scripts/pageIssueLogger.js", - "resources/skins.minerva.scripts/pageIssueParser.js", + "resources/skins.minerva.scripts/pageIssuesLogger.js", + "resources/skins.minerva.scripts/pageIssuesParser.js", "resources/skins.minerva.scripts/AB.js", - "resources/skins.minerva.scripts/cleanuptemplates.js", + "resources/skins.minerva.scripts/PageIssuesOverlay.js", + "resources/skins.minerva.scripts/pageIssues.js", "resources/skins.minerva.scripts/init.js", "resources/skins.minerva.scripts/initLogging.js", "resources/skins.minerva.scripts/mobileRedirect.js", diff --git a/tests/qunit/skins.minerva.scripts/stubs.js b/tests/qunit/skins.minerva.scripts/stubs.js new file mode 100644 index 0000000..47bec9d --- /dev/null +++ b/tests/qunit/skins.minerva.scripts/stubs.js @@ -0,0 +1 @@ +mw.template.add( 'skins.minerva.scripts', 'PageIssuesOverlayContent.hogan', '' ); diff --git a/tests/qunit/skins.minerva.scripts/test_cleanuptemplates.js b/tests/qunit/skins.minerva.scripts/test_pageIssues.js similarity index 94% rename from tests/qunit/skins.minerva.scripts/test_cleanuptemplates.js rename to tests/qunit/skins.minerva.scripts/test_pageIssues.js index be92ea6..4f83341 100644 --- a/tests/qunit/skins.minerva.scripts/test_cleanuptemplates.js +++ b/tests/qunit/skins.minerva.scripts/test_pageIssues.js @@ -1,5 +1,5 @@ ( function ( M ) { - var createBanner = M.require( 'skins.minerva.scripts/cleanuptemplates' ).test.createBanner, + var createBanner = M.require( 'skins.minerva.scripts/pageIssues' ).test.createBanner, OverlayManager = M.require( 'mobile.startup/OverlayManager' ), Page = M.require( 'mobile.startup/Page' ), overlayManager = new OverlayManager( require( 'mediawiki.router' ) ), diff --git a/tests/qunit/skins.minerva.scripts/test_pageIssueParser.js b/tests/qunit/skins.minerva.scripts/test_pageIssuesParser.js similarity index 91% rename from tests/qunit/skins.minerva.scripts/test_pageIssueParser.js rename to tests/qunit/skins.minerva.scripts/test_pageIssuesParser.js index 36d71b9..0b020f0 100644 --- a/tests/qunit/skins.minerva.scripts/test_pageIssueParser.js +++ b/tests/qunit/skins.minerva.scripts/test_pageIssuesParser.js @@ -1,7 +1,7 @@ ( function ( M ) { - var pageIssueParser = M.require( 'skins.minerva.scripts/pageIssueParser' ); + var pageIssuesParser = M.require( 'skins.minerva.scripts/pageIssuesParser' ); - QUnit.module( 'Minerva pageIssueParser' ); + QUnit.module( 'Minerva pageIssuesParser' ); /** * @param {string} className @@ -34,7 +34,7 @@ test = params[2], box = newBox( className ); assert.strictEqual( - pageIssueParser.test.parseSeverity( box ), + pageIssuesParser.test.parseSeverity( box ), expect, 'Result should be the correct severity; case ' + i + ': ' + test + '.' ); @@ -63,7 +63,7 @@ test = params[3], box = newBox( className ); assert.propEqual( - pageIssueParser.test.parseType( box, severity ), + pageIssuesParser.test.parseType( box, severity ), expect, 'Result should be the correct icon type; case ' + i + ': ' + test + '.' ); @@ -90,7 +90,7 @@ expect = params[2], box = newBox( className ); assert.strictEqual( - pageIssueParser.iconName( box, severity ), + pageIssuesParser.iconName( box, severity ), expect, 'Result should be the correct ResourceLoader icon name; case ' + i + ': ' + severity + '.' ); @@ -112,7 +112,7 @@ expect = params[1]; assert.strictEqual( - pageIssueParser.maxSeverity( severities ), + pageIssuesParser.maxSeverity( severities ), expect, 'Result should be the highest severity in the array; case ' + i + '.' );