mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
fix(settings): Remote nav now scrolls to the right section
The Remote Control nav item carried id: '@iptvnator/ui/remote-control' — the NX library name pasted in place of the section's HTML id. The settings-section-scroll directive resolves the target with document.getElementById(), which returned null for that string, so clicking "Remote" silently no-op'd. The active-state binding ([class.settings-group--active]="activeSection() === 'remote-control'") also never lit up because the nav reported a different id than the section template uses. Change the nav id to 'remote-control' so it matches the section's id="remote-control" attribute. Added a focused regression spec (settings-options.spec.ts) that: - asserts the desktop-only items light up only when isDesktop is true, - reads each section component's root <section id="…"> at test time and cross-checks the nav id set against it, so any future drift between the two (renamed section, new section without nav entry, copy-paste of a library path into the id) fails the build. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
115911d2a6
commit
5a1f699379
2 files changed
+84
-1
No files matched your search
@@ -0,0 +1,78 @@
|
||||
import { readFileSync, readdirSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
|
||||
import { buildSettingsSectionNavItems } from './settings-options';
|
||||
|
||||
/**
|
||||
* Every nav-item id must match the `id="..."` attribute on its
|
||||
* corresponding section component template, otherwise clicking the nav
|
||||
* link silently no-ops (the scroll directive runs `document.getElementById`
|
||||
* and falls through when no element is found).
|
||||
*
|
||||
* This bug bit the Remote section once already — its nav id was set to
|
||||
* the Nx library name (`@iptvnator/ui/remote-control`) instead of the
|
||||
* template's `id="remote-control"`. The guard below catches future
|
||||
* rename/copy-paste regressions before they ship.
|
||||
*/
|
||||
describe('buildSettingsSectionNavItems', () => {
|
||||
// Anchor on the Nx workspace root (Jest runs from there); avoids
|
||||
// depending on __dirname which isn't defined under the project's
|
||||
// ESM Jest preset.
|
||||
const settingsDir = resolve(
|
||||
process.cwd(),
|
||||
'apps/web/src/app/settings'
|
||||
);
|
||||
|
||||
function collectSectionTemplateIds(): Set<string> {
|
||||
const ids = new Set<string>();
|
||||
for (const fileName of readdirSync(settingsDir)) {
|
||||
if (!/^settings-.+-section\.component\.html$/.test(fileName)) {
|
||||
continue;
|
||||
}
|
||||
const html = readFileSync(
|
||||
resolve(settingsDir, fileName),
|
||||
'utf-8'
|
||||
);
|
||||
// Match the FIRST `id="…"` on the <section> root only —
|
||||
// descendant elements (form controls, anchors) also use id=
|
||||
// attributes and would pollute the set.
|
||||
const rootMatch = /<section[^>]*\sid="([^"]+)"/i.exec(html);
|
||||
if (rootMatch) {
|
||||
ids.add(rootMatch[1]);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
it('exposes desktop-only items only when isDesktop is true', () => {
|
||||
const desktopItems = buildSettingsSectionNavItems(true);
|
||||
const pwaItems = buildSettingsSectionNavItems(false);
|
||||
|
||||
expect(desktopItems.map((item) => item.id)).toEqual(
|
||||
expect.arrayContaining(['epg', 'remote-control'])
|
||||
);
|
||||
expect(pwaItems.find((item) => item.id === 'epg')?.visible).toBe(
|
||||
false
|
||||
);
|
||||
expect(
|
||||
pwaItems.find((item) => item.id === 'remote-control')?.visible
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('every nav id matches an existing section template id (regression: remote-control nav no longer maps to the Nx lib name)', () => {
|
||||
const navIds = new Set(
|
||||
buildSettingsSectionNavItems(true).map((item) => item.id)
|
||||
);
|
||||
const templateIds = collectSectionTemplateIds();
|
||||
|
||||
// Every nav id must exist as a section root id.
|
||||
const orphans = [...navIds].filter((id) => !templateIds.has(id));
|
||||
expect(orphans).toEqual([]);
|
||||
|
||||
// And every section template id must be reachable from the nav
|
||||
// (catches the opposite drift — a new section added without a nav
|
||||
// entry would never get scrolled to).
|
||||
const unreachable = [...templateIds].filter((id) => !navIds.has(id));
|
||||
expect(unreachable).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -108,7 +108,12 @@ export function buildSettingsSectionNavItems(
|
||||
visible: isDesktop,
|
||||
},
|
||||
{
|
||||
id: '@iptvnator/ui/remote-control',
|
||||
// Must match the section's HTML id (`remote-control`) so the
|
||||
// settings-section-scroll directive can resolve the anchor.
|
||||
// Was previously '@iptvnator/ui/remote-control' (the NX lib
|
||||
// name), which meant clicking the nav item silently no-op'd
|
||||
// because document.getElementById of that string returned null.
|
||||
id: 'remote-control',
|
||||
label: 'SETTINGS.NAV_REMOTE',
|
||||
icon: 'smartphone',
|
||||
visible: isDesktop,
|
||||
|
||||
Reference in new issue
Block a user