aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/unit/tabs/core.js51
-rw-r--r--tests/unit/tabs/tabs.html29
-rw-r--r--ui/widgets/tabs.js36
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 {