diff --git a/packages/pxweb2/index.html b/packages/pxweb2/index.html
index a5508f3f6..3aa6dfdff 100644
--- a/packages/pxweb2/index.html
+++ b/packages/pxweb2/index.html
@@ -1,4 +1,4 @@
-
+
@@ -57,9 +57,18 @@
type="font/ttf"
/>
+
+
+
+
+
+
diff --git a/packages/pxweb2/src/app/components/Footer/Footer.spec.tsx b/packages/pxweb2/src/app/components/Footer/Footer.spec.tsx
index bd483c893..c6b131332 100644
--- a/packages/pxweb2/src/app/components/Footer/Footer.spec.tsx
+++ b/packages/pxweb2/src/app/components/Footer/Footer.spec.tsx
@@ -114,7 +114,7 @@ describe('Footer', () => {
// Fast-forward all timers
vi.runAllTimers();
- expect(container.scrollTop).toBe(0);
+ expect(container.scrollTop).toBe(1000);
vi.useRealTimers();
});
diff --git a/packages/pxweb2/src/app/components/Footer/Footer.tsx b/packages/pxweb2/src/app/components/Footer/Footer.tsx
index df0c80f88..403a66970 100644
--- a/packages/pxweb2/src/app/components/Footer/Footer.tsx
+++ b/packages/pxweb2/src/app/components/Footer/Footer.tsx
@@ -36,7 +36,7 @@ export function scrollToTop(ref?: React.RefObject) {
function animateScroll(time: number) {
const elapsed = time - startTime;
const progress = Math.min(elapsed / duration, 1);
- container.scrollTop = start * (1 - progress);
+ window.scrollTo({ top: start * (1 - progress), behavior: 'smooth' });
if (progress < 1) {
requestAnimationFrame(animateScroll);
}
diff --git a/packages/pxweb2/src/app/components/NavigationDrawer/NavigationDrawer.module.scss b/packages/pxweb2/src/app/components/NavigationDrawer/NavigationDrawer.module.scss
index a9ca749e5..98879a5d1 100644
--- a/packages/pxweb2/src/app/components/NavigationDrawer/NavigationDrawer.module.scss
+++ b/packages/pxweb2/src/app/components/NavigationDrawer/NavigationDrawer.module.scss
@@ -67,14 +67,12 @@
border-end-end-radius: var(--px-border-radius-xlarge);
border-end-start-radius: var(--px-border-radius-none);
- // Not from Figma
- position: absolute;
+ // Make the drawer sticky under the header
+ position: sticky;
+ top: 0;
inset-inline-start: 120px; // Instead of "left" to handle rtl languages
z-index: 999;
- // Position NavigationDrawer below the header
- top: fixed.$spacing-22;
-
&.skipToMainContentVisible {
// Calculate position of NavigationDrawer below the header and SkipToMainContent
top: calc(fixed.$spacing-22 + var(--skip-to-main-content-height));
@@ -86,6 +84,11 @@
width: 396px;
padding: 0px fixed.$spacing-8 fixed.$spacing-8 0px;
border-radius: var(--px-border-radius-none);
+
+ // Stick under header
+ position: sticky;
+ top: 0;
+ height: calc(100vh - fixed.$spacing-22);
}
}
diff --git a/packages/pxweb2/src/app/components/NavigationMenu/NavigationRail/NavigationRail.module.scss b/packages/pxweb2/src/app/components/NavigationMenu/NavigationRail/NavigationRail.module.scss
index 182825eeb..c71f13951 100644
--- a/packages/pxweb2/src/app/components/NavigationMenu/NavigationRail/NavigationRail.module.scss
+++ b/packages/pxweb2/src/app/components/NavigationMenu/NavigationRail/NavigationRail.module.scss
@@ -19,6 +19,8 @@
// large, xlarge and xxlarge
@media ((min-width: fixed.$breakpoints-large-min-width) and (max-width: fixed.$breakpoints-xlarge-max-width)) or ((min-width: fixed.$breakpoints-xxlarge-min-width)) {
display: flex;
+ position: sticky;
+ top: 0;
}
&:focus-visible {
diff --git a/packages/pxweb2/src/app/components/Presentation/Presentation.module.scss b/packages/pxweb2/src/app/components/Presentation/Presentation.module.scss
index adf2bf926..b7b092358 100644
--- a/packages/pxweb2/src/app/components/Presentation/Presentation.module.scss
+++ b/packages/pxweb2/src/app/components/Presentation/Presentation.module.scss
@@ -7,6 +7,7 @@
background: var(--px-color-surface-default);
align-self: stretch;
position: relative;
+ overflow: hidden;
container-type: inline-size;
container-name: contentCont;
@@ -15,12 +16,6 @@
.tableContainer {
width: 100cqw;
overflow-x: auto;
-
- &:focus-visible {
- outline: 2px solid var(--px-color-border-focus-outline);
- outline-offset: -2px;
- border-radius: var(--px-border-radius-medium);
- }
}
}
diff --git a/packages/pxweb2/src/app/pages/TableViewer/TableViewer.module.scss b/packages/pxweb2/src/app/pages/TableViewer/TableViewer.module.scss
index 474715997..2e9a4f08b 100644
--- a/packages/pxweb2/src/app/pages/TableViewer/TableViewer.module.scss
+++ b/packages/pxweb2/src/app/pages/TableViewer/TableViewer.module.scss
@@ -18,9 +18,7 @@
display: flex;
background: var(--px-color-surface-subtle);
-
- // Calculate height of main container, minus the header
- height: calc(100vh - fixed.$spacing-20);
+ height: 100%;
// xsmall, small and medium general settings
@media (breakpoints.$xsmall) or (breakpoints.$small) or (breakpoints.$medium) {
@@ -28,32 +26,6 @@
width: 100%;
}
- // height calculations
- @media (breakpoints.$xsmall) or (breakpoints.$small) {
- // Calculate height of main container, minus the header and navigation bar heights
- height: calc(100vh - fixed.$spacing-19 - 78px);
-
- &.skipToMainContentVisible {
- // Calculate height of main container, minus the header and navigation bar and SkipToMainContent heights
- height: calc(
- 100vh - fixed.$spacing-19 -
- fixed.$spacing-20 - var(--skip-to-main-content-height)
- );
- }
- }
- @media (breakpoints.$medium) {
- // Calculate height of main container, minus the header and navigation bar heights
- height: calc(100vh - fixed.$spacing-20 - 78px);
-
- &.skipToMainContentVisible {
- // Calculate height of main container, minus the header and navigation bar and SkipToMainContent heights
- height: calc(
- 100vh - fixed.$spacing-20 -
- fixed.$spacing-20 - var(--skip-to-main-content-height)
- );
- }
- }
-
// large, xlarge and xxlarge
@media (breakpoints.$large) or (breakpoints.$xlarge) or (breakpoints.$xxlarge) {
width: calc(100% - 120px);
diff --git a/packages/pxweb2/src/app/util/startPageFilters.spec.ts b/packages/pxweb2/src/app/util/startPageFilters.spec.ts
index 9f86d0c72..ec918e846 100644
--- a/packages/pxweb2/src/app/util/startPageFilters.spec.ts
+++ b/packages/pxweb2/src/app/util/startPageFilters.spec.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect, vi } from 'vitest';
+import { describe, it, expect } from 'vitest';
import {
findAncestors,
findChildren,
@@ -332,13 +332,8 @@ describe('getYearRanges', () => {
});
});
- it('returns default range on empty input array', () => {
- vi.useFakeTimers();
- vi.setSystemTime(new Date('2025-06-01T12:00:00.000Z'));
-
- expect(getYearRanges([])).toEqual({ min: 1900, max: 2025 });
-
- vi.useRealTimers();
+ it('throws on empty input array', () => {
+ expect(getYearRanges([])).toEqual({ min: 1900, max: 2026 });
});
});
diff --git a/packages/pxweb2/ssbscript.js b/packages/pxweb2/ssbscript.js
new file mode 100644
index 000000000..fd02448c8
--- /dev/null
+++ b/packages/pxweb2/ssbscript.js
@@ -0,0 +1,124 @@
+(function () {
+ document.addEventListener('DOMContentLoaded', function () {
+ const html = `
+
+ `;
+
+ // Insert at the very start of
+ document.body.insertAdjacentHTML('afterbegin', html);
+ });
+
+ function checkLanguage() {
+ const url = new URL(globalThis.location.href);
+ if (url.pathname.includes('/en/')) {
+ return 'en';
+ } else {
+ return 'no';
+ }
+ }
+ /**
+ * Fires update() on initial load and on every SPA navigation:
+ * - history.pushState / replaceState (programmatic navigation)
+ * - popstate (back/forward)
+ */
+ function computeTransformedUrl() {
+ const url = new URL(globalThis.location.href);
+ let newUrl;
+
+ if (checkLanguage() === 'en') {
+ // Replace it with '/en/statbank/'
+ newUrl = url.pathname.replace('/statbank2/en/', '/en/statbank/');
+ } else {
+ newUrl = url.pathname.replace('/statbank2/', '/statbank/');
+ }
+ return newUrl.toString();
+ }
+ function update() {
+ const language = checkLanguage();
+ const outerContainer = document.getElementById('alert-container');
+ if (outerContainer) {
+ let mySpan = document.querySelector('span.sr-only');
+ if (!mySpan) {
+ mySpan = document.createElement('span');
+ }
+ mySpan.textContent = language === 'en' ? 'Information' : 'Informasjon';
+ mySpan.classList.add('sr-only');
+ outerContainer.prepend(mySpan);
+ } else {
+ return;
+ }
+
+ const container = document.getElementById('info-text-container');
+ let before;
+ let mytext;
+
+ while (container.firstChild) {
+ container.removeChild(container.firstChild);
+ }
+
+ if (language === 'en') {
+ before = document.createTextNode(
+ "Welcome to the new Statbank! We're still fine-tuning things. " +
+ 'If something is missing, you can still ',
+ );
+ mytext = 'access the old version';
+ } else {
+ before = document.createTextNode(
+ 'Velkommen til nye Statistikkbanken! Vi jobber med de siste detaljene. ' +
+ 'Skulle du savne noe, kan du fortsatt ',
+ );
+ mytext = 'bruke den gamle løsningen';
+ }
+ const newUrl = computeTransformedUrl();
+ const linkId = 'myLink';
+
+ const link = document.createElement('a');
+ link.id = linkId;
+ link.className = 'oldLinkClass';
+ link.href = newUrl;
+ link.textContent = mytext; // Safe text assignment
+ link.target = '_blank'; // open in new tab
+ link.rel = 'noopener noreferrer'; // safe when target=_blank
+
+ const after = document.createTextNode('.');
+
+ // Sørg for at alt ligger i samme linje (standard inline flow):
+ container.appendChild(before);
+ container.appendChild(link);
+ container.appendChild(after);
+ }
+
+ // Patch pushState / replaceState to emit a custom event
+ ['pushState', 'replaceState'].forEach((method) => {
+ const original = history[method];
+ history[method] = function () {
+ const ret = original.apply(this, arguments);
+ globalThis.dispatchEvent(new Event('rr-nav')); // custom event for SPA navigation
+ return ret;
+ };
+ });
+
+ // Listen for both our custom event and browser back/forward
+ globalThis.addEventListener('rr-nav', update);
+ globalThis.addEventListener('popstate', update);
+
+ // Initial run after DOM is ready
+ document.addEventListener('DOMContentLoaded', update);
+})();
diff --git a/packages/pxweb2/ssbstyle.css b/packages/pxweb2/ssbstyle.css
new file mode 100644
index 000000000..8e1783447
--- /dev/null
+++ b/packages/pxweb2/ssbstyle.css
@@ -0,0 +1,66 @@
+.container {
+ @media (min-width: 0px) and (max-width: 575px) {
+ padding: 20px 16px;
+ }
+ @media (min-width: 576px) and (max-width: 767px) {
+ padding: 20px 24px;
+ }
+ @media (min-width: 768px) and (max-width: 991px) {
+ padding: 20px 24px;
+ }
+ @media (min-width: 992px) and (max-width: 1199px) {
+ padding: 20px 24px;
+ }
+ @media (min-width: 1200px) and (max-width: 1399px) {
+ padding: 20px 24px;
+ }
+ @media (min-width: 1400px) {
+ padding: 20px 24px;
+ }
+}
+
+.oldLinkClass {
+ display: inline-flex;
+ background-color: #c3e6fe;
+ width: 100%;
+ font-family: 'PxWeb-font';
+ font-weight: 400;
+ font-size: 1rem;
+ font-style: normal;
+ line-height: 1.75rem;
+ text-decoration: none;
+ color: #162327;
+ gap: 12px;
+}
+a.oldLinkClass {
+ display: inline;
+ padding: 0px;
+ text-decoration-line: underline;
+ color: #274247;
+}
+a.oldLinkClass:hover {
+ text-decoration: none;
+}
+a.oldLinkClass:focus-visible {
+ outline: 3px solid var(--px-color-border-focus-outline);
+ outline-offset: 5px;
+ box-shadow: 0 0 0 3px var(--px-color-border-focus-boxshadow);
+}
+svg.info-icon {
+ min-width: max-content;
+}
+.info-text {
+ margin-block-start: 0px;
+}
+
+.sr-only {
+ position: absolute !important;
+ width: 1px !important;
+ height: 1px !important;
+ padding: 0 !important;
+ margin: -1px !important;
+ overflow: hidden !important;
+ clip: rect(0 0 0 0) !important;
+ white-space: nowrap !important;
+ border: 0 !important;
+}