Rework the Preferences to prevent FOUC
authorDerk-Jan Hartman <hartman.wiki@gmail.com>
Sun, 1 Nov 2015 00:34:46 +0000 (01:34 +0100)
committerOri.livneh <ori@wikimedia.org>
Sun, 8 Nov 2015 19:42:56 +0000 (19:42 +0000)
New styles modules that is always added, so that all JS specific styling
is guaranteed to load before first paint. Reworked the HTML to generate
the preftoc (hidden when user has no JS).

Set htmlform nolabel class to use !important, so that it doesn't get
overriden by the 20% width rule of labels.

Also requires changes to the skinstyles of Vector preferences, which
is an a separate patch (I59f0f45), and other skins.

Bug: T115692
Change-Id: I24d9b16ed6729fdf0d59adcc2f0ba16f4f621b44

includes/Preferences.php
includes/specials/SpecialPreferences.php
resources/Resources.php
resources/src/mediawiki.special/mediawiki.special.preferences.css [deleted file]
resources/src/mediawiki.special/mediawiki.special.preferences.js
resources/src/mediawiki.special/mediawiki.special.preferences.styles.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.htmlform.css

index 0f8dcc3..096f8e3 100644 (file)
@@ -1655,4 +1655,12 @@ class PreferencesForm extends HTMLForm {
                Hooks::run( 'PreferencesGetLegend', array( $this, $key, &$legend ) );
                return $legend;
        }
+
+       /**
+        * Get the keys of each top level preference section.
+        * @return array of section keys
+        */
+       function getPreferenceSections() {
+               return array_keys( array_filter( $this->mFieldTree, 'is_array' ) );
+       }
 }
index 4b75e5f..3b5b9c9 100644 (file)
@@ -47,6 +47,7 @@ class SpecialPreferences extends SpecialPage {
                }
 
                $out->addModules( 'mediawiki.special.preferences' );
+               $out->addModuleStyles( 'mediawiki.special.preferences.styles' );
 
                if ( $this->getRequest()->getCheck( 'success' ) ) {
                        $out->wrapWikiMsg(
@@ -66,7 +67,37 @@ class SpecialPreferences extends SpecialPage {
 
                $htmlForm = Preferences::getFormObject( $this->getUser(), $this->getContext() );
                $htmlForm->setSubmitCallback( array( 'Preferences', 'tryUISubmit' ) );
+               $sectionTitles = $htmlForm->getPreferenceSections();
+
+               $prefTabs = '';
+               foreach ( $sectionTitles as $key ) {
+                       $prefTabs .= Html::rawElement( 'li',
+                               array(
+                                       'role' => 'presentation',
+                                       'class' => ( $key === 'personal' ) ? 'selected' : null
+                               ),
+                               Html::rawElement( 'a',
+                                       array(
+                                               'id' => 'preftab-' . $key,
+                                               'role' => 'tab',
+                                               'href' => '#mw-prefsection-' . $key,
+                                               'aria-controls' => 'mw-prefsection-' . $key,
+                                               'aria-selected' => ( $key === 'personal' ) ? 'true' : 'false',
+                                               'tabIndex' => ( $key === 'personal' ) ? 0 : -1,
+                                       ),
+                                       $htmlForm->getLegend( $key )
+                               )
+                       );
+               }
 
+               $out->addHTML(
+                       Html::rawElement( 'ul',
+                               array(
+                                       'id' => 'preftoc',
+                                       'role' => 'tablist'
+                               ),
+                               $prefTabs )
+               );
                $htmlForm->show();
        }
 
index b1b1541..08c695d 100644 (file)
@@ -1713,7 +1713,6 @@ return array(
        ),
        'mediawiki.special.preferences' => array(
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.preferences.js',
-               'styles' => 'resources/src/mediawiki.special/mediawiki.special.preferences.css',
                'position' => 'top',
                'messages' => array(
                        'prefs-tabs-navigation-hint',
@@ -1727,6 +1726,10 @@ return array(
                        'mediawiki.notification',
                ),
        ),
+       'mediawiki.special.preferences.styles' => array(
+               'styles' => 'resources/src/mediawiki.special/mediawiki.special.preferences.styles.css',
+               'position' => 'top',
+       ),
        'mediawiki.special.recentchanges' => array(
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.recentchanges.js',
                'dependencies' => 'mediawiki.special',
diff --git a/resources/src/mediawiki.special/mediawiki.special.preferences.css b/resources/src/mediawiki.special/mediawiki.special.preferences.css
deleted file mode 100644 (file)
index 9e5efd3..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Reuses colors from mediawiki.special.changeemail.css */
-.mw-email-not-authenticated .mw-input,
-.mw-email-none .mw-input{
-       border: 1px solid #FF8080;
-       background-color: #FFC0C0;
-       color: black;
-}
-/* Authenticated email field has its own class too. Unstyled by default */
-/*
-.mw-email-authenticated .mw-input { }
-*/
-
-/*
- * Hide, but keep accessible for screen-readers.
- * Like .mw-jump, #jump-to-nav from shared.css
- */
-.mw-navigation-hint {
-       overflow: hidden;
-       height: 0;
-       zoom: 1;
-}
-
-/* When JS is enabled, .mw-preferences-messageboxes are replaced with mw.notifications */
-.mw-preferences-messagebox {
-       display: none;
-}
-
-.prefsection td.mw-label {
-       width: 20%;
-}
-
-.prefsection table {
-       width: 100%;
-}
-
-.prefsection table.mw-htmlform-matrix {
-       width: auto;
-}
index 9ab47b3..f799eaf 100644 (file)
@@ -3,7 +3,7 @@
  */
 ( function ( mw, $ ) {
        $( function () {
-               var $preftoc, $preferences, $fieldsets, $legends,
+               var $preftoc, $preferences, $fieldsets,
                        hash, labelFunc,
                        $tzSelect, $tzTextbox, $localtimeHolder, servertime,
                        allowCloseWindow, notif;
                };
 
                $( '#prefsubmit' ).attr( 'id', 'prefcontrol' );
-               $preftoc = $( '<ul>' )
-                       .attr( {
-                               id: 'preftoc',
-                               role: 'tablist'
-                       } );
-               $preferences = $( '#preferences' )
-                       .addClass( 'jsprefs' )
-                       .before( $preftoc );
+               $preftoc = $( '#preftoc' );
+               $preferences = $( '#preferences' );
+
                $fieldsets = $preferences.children( 'fieldset' )
-                       .hide()
                        .attr( {
                                role: 'tabpanel',
-                               'aria-hidden': 'true',
                                'aria-labelledby': labelFunc
                        } )
-                       .addClass( 'prefsection' );
-               $legends = $fieldsets
-                       .children( 'legend' )
-                       .addClass( 'mainLegend' );
+                       .not( '#mw-prefsection-personal' )
+                               .hide()
+                               .attr( 'aria-hidden', 'true' );
+
+               // T115692: The following is kept for backwards compatibility with older skins
+               $preferences.addClass( 'jsprefs' );
+               $fieldsets.addClass( 'prefsection' );
+               $fieldsets.children( 'legend' ).addClass( 'mainLegend' );
 
                // Make sure the accessibility tip is selectable so that screen reader users take notice,
                // but hide it per default to reduce interface clutter. Also make sure it becomes visible
                        }
                }
 
-               // Populate the prefToc
-               $legends.each( function ( i, legend ) {
-                       var $legend = $( legend ),
-                               ident, $li, $a;
-                       if ( i === 0 ) {
-                               $legend.parent().show();
-                       }
-                       ident = $legend.parent().attr( 'id' );
-
-                       $li = $( '<li>' )
-                               .attr( 'role', 'presentation' )
-                               .addClass( i === 0 ? 'selected' : '' );
-                       $a = $( '<a>' )
-                               .attr( {
-                                       id: ident.replace( 'mw-prefsection', 'preftab' ),
-                                       href: '#' + ident,
-                                       role: 'tab',
-                                       tabIndex: i === 0 ? 0 : -1,
-                                       'aria-selected': i === 0 ? 'true' : 'false',
-                                       'aria-controls': ident
-                               } )
-                               .text( $legend.text() );
-                       $li.append( $a );
-                       $preftoc.append( $li );
-               } );
-
                // Disable the button to save preferences unless preferences have changed
                $( '#prefcontrol' ).prop( 'disabled', true );
-               $( '.prefsection' ).one( 'change keydown mousedown', function () {
+               $( '#preferences > fieldset' ).one( 'change keydown mousedown', function () {
                        $( '#prefcontrol' ).prop( 'disabled', false );
                } );
 
diff --git a/resources/src/mediawiki.special/mediawiki.special.preferences.styles.css b/resources/src/mediawiki.special/mediawiki.special.preferences.styles.css
new file mode 100644 (file)
index 0000000..e6785b4
--- /dev/null
@@ -0,0 +1,48 @@
+/* Reuses colors from mediawiki.special.changeemail.css */
+.mw-email-not-authenticated .mw-input,
+.mw-email-none .mw-input{
+       border: 1px solid #FF8080;
+       background-color: #FFC0C0;
+       color: black;
+}
+/* Authenticated email field has its own class too. Unstyled by default */
+/*
+.mw-email-authenticated .mw-input { }
+*/
+/* This breaks due to nolabel styling */
+#preferences > fieldset td.mw-label {
+       width: 20%;
+}
+
+#preferences > fieldset table {
+       width: 100%;
+}
+#preferences > fieldset table.mw-htmlform-matrix {
+       width: auto;
+}
+
+/* The CSS below is also for JS enabled version, because we want to prevent FOUC */
+
+/*
+ * Hide, but keep accessible for screen-readers.
+ * Like .mw-jump, #jump-to-nav from shared.css
+ */
+.client-js .mw-navigation-hint {
+       overflow: hidden;
+       height: 0;
+       zoom: 1;
+}
+
+.client-nojs #preftoc {
+       display: none;
+}
+
+.client-js #preferences > fieldset {
+       display: none;
+}
+
+/* Only the 1st tab is shown by default in JS mode */
+.client-js #preferences #mw-prefsection-personal {
+       display: block;
+}
+
index e41248c..b598211 100644 (file)
@@ -1,7 +1,7 @@
 /* HTMLForm styles */
 
 table.mw-htmlform-nolabel td.mw-label {
-       width: 1px;
+       width: 1px !important;
 }
 
 .mw-htmlform-invalid-input td.mw-input input {