From 2afbd3412988f2c7c5a83e92c3b5b1332419bf24 Mon Sep 17 00:00:00 2001 From: Nicolai Ehrhardt <245527909+predictor2718@users.noreply.github.com> Date: Wed, 29 Apr 2026 21:15:01 +0200 Subject: [PATCH] Fix #20256 - Restore active preferences tab after page refresh via location.hash Signed-off-by: Nicolai Ehrhardt <245527909+predictor2718@users.noreply.github.com> --- js/src/config.js | 38 ++++++++++++++++++++++++++++ test/selenium/ServerSettingsTest.php | 26 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/js/src/config.js b/js/src/config.js index 093993b36e..d6a1f06477 100644 --- a/js/src/config.js +++ b/js/src/config.js @@ -44,6 +44,7 @@ AJAX.registerTeardown('config.js', function () { $('form.prefs-form').off('change').off('submit'); $(document).off('click', 'div.click-hide-message'); $('#prefs_autoload').find('a').off('click'); + $('form.config-form').off('shown.bs.tab'); }); AJAX.registerOnload('config.js', function () { @@ -52,6 +53,43 @@ AJAX.registerOnload('config.js', function () { $topmenuUpt.find('a:not(.active)').attr('rel', 'newpage'); }); +// ------------------------------------------------------------------ +// Tabbed forms - tab state persistence via location.hash +// + +function setupConfigTabsPersistence () { + var $form = $('form.config-form'); + if (!$form.length) { + return; + } + + // Activate the tab indicated by the URL hash on page load + var hash = window.location.hash; + if (hash && /^#[a-zA-Z0-9_]+$/.test(hash)) { + var $tabLink = $form.find('a[data-bs-toggle="tab"][href="' + hash + '"]'); + if ($tabLink.length) { + bootstrap.Tab.getOrCreateInstance($tabLink[0]).show(); + } + } + + // Keep the URL hash and the tab_hash hidden input in sync when the user switches tabs + $form.on('shown.bs.tab', '[data-bs-toggle="tab"]', function () { + var href = $(this).attr('href'); + if (href) { + window.location.hash = href; + $form.find('input[name="tab_hash"]').val(href.replace(/^#/, '')); + } + }); +} + +AJAX.registerOnload('config.js', function () { + setupConfigTabsPersistence(); +}); + +// +// END: Tabbed forms +// ------------------------------------------------------------------ + // default values for fields var defaultValues = {}; diff --git a/test/selenium/ServerSettingsTest.php b/test/selenium/ServerSettingsTest.php index 2111b6eaa7..edbc3b489c 100644 --- a/test/selenium/ServerSettingsTest.php +++ b/test/selenium/ServerSettingsTest.php @@ -101,6 +101,32 @@ class ServerSettingsTest extends TestBase self::assertTrue($this->byId('Sql_queries')->isDisplayed()); } + /** + * Tests that the active preferences tab is restored after a page refresh + * + * @group large + */ + public function testActiveTabRestoredAfterRefresh(): void + { + $this->byPartialLinkText('Main panel')->click(); + $this->waitAjax(); + + $this->waitForElement('className', 'nav-tabs'); + + // Switch to a non-default tab + $this->byCssSelector("a[href='#Tabs']")->click(); + self::assertTrue($this->byId('Tabs')->isDisplayed()); + self::assertFalse($this->byId('Startup')->isDisplayed()); + + // Reload the page – the hash is preserved in the URL + $this->reloadPage(); + $this->waitAjax(); + + // The previously active tab must still be shown + self::assertTrue($this->byId('Tabs')->isDisplayed()); + self::assertFalse($this->byId('Startup')->isDisplayed()); + } + /** * Tests if hiding the logo works or not *