aboutsummaryrefslogtreecommitdiffstats
path: root/tests/unit
diff options
context:
space:
mode:
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>2025-03-26 14:36:58 +0100
committerMichał Gołębiowski-Owczarek <m.goleb@gmail.com>2025-03-31 18:51:37 +0200
commit89b0ecaaa3fc1f78e6b9f3d3b95de66f6bd22a2d (patch)
treed138873578eb7b37c474e2ea4418a41eeeaf31e3 /tests/unit
parent53129e9cc7eb1c4f55b44a14adc91da23c7be85b (diff)
downloadjquery-ui-89b0ecaaa3fc1f78e6b9f3d3b95de66f6bd22a2d.tar.gz
jquery-ui-89b0ecaaa3fc1f78e6b9f3d3b95de66f6bd22a2d.zip
Tabs: Properly handle decoded/encoded anchor hashes & panel IDs
Prior to jQuery UI 1.14.1, hashes in anchor hrefs were used directly. In gh-2307, that was changed - by decoding - to support more complex IDs, e.g. containing emojis which are automatically encoded in `anchor.hash`. Unfortunately, that broke cases where the panel ID is decoded as well. It turns out the spec mandates checking both. In the "scrolling to a fragment" section of the HTML spec[^1]. That uses a concept of document's indicated part[^2]. Slightly below there's an algorithm to compute the indicated part[^3]. The interesting parts are steps 4 to 9: 4. Let potentialIndicatedElement be the result of finding a potential indicated element given document and fragment. 5. If potentialIndicatedElement is not null, then return potentialIndicatedElement. 6. Let fragmentBytes be the result of percent-decoding fragment. 7. Let decodedFragment be the result of running UTF-8 decode without BOM on fragmentBytes. 8. Set potentialIndicatedElement to the result of finding a potential indicated element given document and decodedFragment. 9. If potentialIndicatedElement is not null, then return potentialIndicatedElement. First, in steps 4-5, the algorithm tries the hash as-is, without decoding. Then, if one is not found, the same is attempted with a decoded hash. This change replicates this logic by first trying the hash as-is and then decoding it. Fixes gh-2344 Closes gh-2345 Ref gh-2307 [^1]: https://html.spec.whatwg.org/#scrolling-to-a-fragment [^2]: https://html.spec.whatwg.org/#the-indicated-part-of-the-document [^3]: https://html.spec.whatwg.org/#select-the-indicated-part
Diffstat (limited to 'tests/unit')
-rw-r--r--tests/unit/tabs/core.js51
-rw-r--r--tests/unit/tabs/tabs.html29
2 files changed, 80 insertions, 0 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>