diff options
-rw-r--r-- | tests/unit/tabs/core.js | 51 | ||||
-rw-r--r-- | tests/unit/tabs/tabs.html | 29 | ||||
-rw-r--r-- | ui/widgets/tabs.js | 36 |
3 files changed, 112 insertions, 4 deletions
diff --git a/tests/unit/tabs/core.js b/tests/unit/tabs/core.js index f7515f585..1eac3c268 100644 --- a/tests/unit/tabs/core.js +++ b/tests/unit/tabs/core.js @@ -773,4 +773,55 @@ QUnit.test( "URL-based auth with local tabs (gh-2213)", function( assert ) { } } ); +( function() { + function getVerifyTab( assert, element ) { + return function verifyTab( index ) { + assert.strictEqual( + element.tabs( "option", "active" ), + index, + "should set the active option to " + index ); + assert.strictEqual( + element.find( "[role='tabpanel']:visible" ).text().trim(), + "Tab " + ( index + 1 ), + "should set the panel to 'Tab " + ( index + 1 ) + "'" ); + }; + } + + QUnit.test( "href encoding/decoding (gh-2344)", function( assert ) { + assert.expect( 12 ); + + location.hash = "#tabs-2"; + + var i, + element = $( "#tabs10" ).tabs(), + tabLinks = element.find( "> ul a" ), + verifyTab = getVerifyTab( assert, element ); + + for ( i = 0; i < tabLinks.length; i++ ) { + tabLinks.eq( i ).trigger( "click" ); + verifyTab( i ); + } + + location.hash = ""; + } ); + + QUnit.test( "href encoding/decoding on init (gh-2344)", function( assert ) { + assert.expect( 12 ); + + var i, + element = $( "#tabs10" ), + tabLinks = element.find( "> ul a" ), + verifyTab = getVerifyTab( assert, element ); + + for ( i = 0; i < tabLinks.length; i++ ) { + location.hash = tabLinks.eq( i ).attr( "href" ); + element.tabs(); + verifyTab( i ); + element.tabs( "destroy" ); + } + + location.hash = ""; + } ); +} )(); + } ); diff --git a/tests/unit/tabs/tabs.html b/tests/unit/tabs/tabs.html index cb4e5389f..3f18fa015 100644 --- a/tests/unit/tabs/tabs.html +++ b/tests/unit/tabs/tabs.html @@ -125,6 +125,35 @@ <div id="tabs9-1"></div> </div> +<div id="tabs10"> + <ul> + <li><a href="#tabs-1">1</a></li> + <li><a href="#tabs-2">2</a></li> + <li><a href="#%EF%B8%8F">3</a></li> + <li><a href="#🤗">4</a></li> + <li><a href="#😅">5</a></li> + <li><a href="#%25F0%259F%25A4%25AD">6</a></li> + </ul> + <div id="tabs-1"> + <p>Tab 1</p> + </div> + <div id="tabs-2"> + <p>Tab 2</p> + </div> + <div id="%EF%B8%8F"> + <p>Tab 3</p> + </div> + <div id="🤗"> + <p>Tab 4</p> + </div> + <div id="%F0%9F%98%85"> + <p>Tab 5</p> + </div> + <div id="%F0%9F%A4%AD"> + <p>Tab 6</p> + </div> +</div> + </div> </body> </html> diff --git a/ui/widgets/tabs.js b/ui/widgets/tabs.js index 0a8efd3ca..494e54f22 100644 --- a/ui/widgets/tabs.js +++ b/ui/widgets/tabs.js @@ -114,18 +114,31 @@ $.widget( "ui.tabs", { _initialActive: function() { var active = this.options.active, collapsible = this.options.collapsible, - locationHashDecoded = decodeURIComponent( location.hash.substring( 1 ) ); + locationHash = location.hash.substring( 1 ), + locationHashDecoded = decodeURIComponent( locationHash ); if ( active === null ) { // check the fragment identifier in the URL - if ( locationHashDecoded ) { + if ( locationHash ) { this.tabs.each( function( i, tab ) { - if ( $( tab ).attr( "aria-controls" ) === locationHashDecoded ) { + if ( $( tab ).attr( "aria-controls" ) === locationHash ) { active = i; return false; } } ); + + // If not found, decode the hash & try again. + // See the comment in `_processTabs` under the `_isLocal` check + // for more information. + if ( active === null ) { + this.tabs.each( function( i, tab ) { + if ( $( tab ).attr( "aria-controls" ) === locationHashDecoded ) { + active = i; + return false; + } + } ); + } } // Check for a tab marked active via a class @@ -423,9 +436,24 @@ $.widget( "ui.tabs", { // Inline tab if ( that._isLocal( anchor ) ) { - selector = decodeURIComponent( anchor.hash ); + + // The "scrolling to a fragment" section of the HTML spec: + // https://html.spec.whatwg.org/#scrolling-to-a-fragment + // uses a concept of document's indicated part: + // https://html.spec.whatwg.org/#the-indicated-part-of-the-document + // Slightly below there's an algorithm to compute the indicated + // part: + // https://html.spec.whatwg.org/#the-indicated-part-of-the-document + // First, the algorithm tries the hash as-is, without decoding. + // Then, if one is not found, the same is attempted with a decoded + // hash. Replicate this logic. + selector = anchor.hash; panelId = selector.substring( 1 ); panel = that.element.find( "#" + CSS.escape( panelId ) ); + if ( !panel.length ) { + panelId = decodeURIComponent( panelId ); + panel = that.element.find( "#" + CSS.escape( panelId ) ); + } // remote tab } else { |