diff --git a/packages/pxweb2/index.html b/packages/pxweb2/index.html index a5508f3f6..e882cd56a 100644 --- a/packages/pxweb2/index.html +++ b/packages/pxweb2/index.html @@ -1,8 +1,8 @@ - + - Pxweb2 + Statistikkbanken – SSB @@ -14,6 +14,7 @@ /> + + + + + + +
diff --git a/packages/pxweb2/public/config/config.js b/packages/pxweb2/public/config/config.js index 87ec89910..d42ff0a0b 100644 --- a/packages/pxweb2/public/config/config.js +++ b/packages/pxweb2/public/config/config.js @@ -1,21 +1,36 @@ -globalThis.PxWeb2Config = { +window.PxWeb2Config = { language: { supportedLanguages: [ + { shorthand: 'no', languageName: 'Norsk' }, { shorthand: 'en', languageName: 'English' }, - { shorthand: 'sv', languageName: 'Svenska' }, ], - defaultLanguage: 'en', + defaultLanguage: 'no', fallbackLanguage: 'en', - showDefaultLanguageInPath: true, - positionInPath: 'after', + showDefaultLanguageInPath: false, + positionInPath: 'before', }, baseApplicationPath: '/', - apiUrl: 'https://api.scb.se/OV0104/v2beta/api/v2', + apiUrl: 'https://data.ssb.no/api/pxwebapi/v2', maxDataCells: 150000, - useDynamicContentInTitle: false, - showBreadCrumbOnStartPage: false, + showBreadCrumbOnStartPage: true, specialCharacters: ['.', '..', ':', '-', '...', '*'], variableFilterExclusionList: { + no: [ + 'statistikkvariabel', + 'år', + 'kvartal', + 'måned', + 'uke', + 'driftsår', + 'enkeltår', + 'intervall (år)', + 'halvår', + 'kvartal (u)', + 'termin', + 'toårlig', + 'fireårlig', + 'femårlig', + ], en: [ 'observations', 'year', @@ -23,6 +38,18 @@ globalThis.PxWeb2Config = { 'month', 'every other year', 'every fifth year', + 'contents', + 'half year', + 'quarter (u)', + 'termin', + 'week', + 'single years', + 'biannual', + 'every 4th year', + 'every 5th year', + 'interval (year)', + 'driftsår', + 'every ten years', ], sv: [ 'tabellinnehåll', @@ -35,7 +62,7 @@ globalThis.PxWeb2Config = { ], }, homePage: { - sv: '', // Set to your Swedish homepage URL - en: '', // Set to your English homepage URL + no: '/', + en: '/en/', }, }; diff --git a/packages/pxweb2/ssbscript.js b/packages/pxweb2/ssbscript.js new file mode 100644 index 000000000..a53ee3e8d --- /dev/null +++ b/packages/pxweb2/ssbscript.js @@ -0,0 +1,148 @@ +(function () { + const WIP_STATUS_TEXT = { + en: { + before: + "Welcome to the new Statbank! We're still fine-tuning things. If something is missing, you can still ", + linkText: 'access the old version', + }, + no: { + before: + 'Velkommen til nye Statistikkbanken! Vi jobber med de siste detaljene. Skulle du savne noe, kan du fortsatt ', + linkText: 'bruke den gamle løsningen', + }, + }; + + 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); + const newUrl = url.pathname.replace('/statbank', '/statbank1'); + return newUrl.toString(); + } + // --------------------------------------------- + // Track last language to detect language changes + // --------------------------------------------- + let lastLang = null; + + function update() { + const bodyLong = document.querySelector( + '#wip-status-message div[class*="bodylong"]', + ); + if (!bodyLong) { + return; + } + + const language = checkLanguage(); + const content = WIP_STATUS_TEXT[language] || WIP_STATUS_TEXT.no; + const before = document.createTextNode(content.before); + const newUrl = computeTransformedUrl(); + const linkId = 'myLink'; + + const link = document.createElement('a'); + link.id = linkId; + link.className = 'oldLinkClass'; + link.href = newUrl; + link.textContent = content.linkText; + link.target = '_blank'; // open in new tab + link.rel = 'noopener noreferrer'; // safe when target=_blank + + const after = document.createTextNode('.'); + bodyLong.textContent = ''; + + bodyLong.appendChild(before); + bodyLong.appendChild(link); + bodyLong.appendChild(after); + // --------------------------------------------- + // Cookie Banner Language Sync + // --------------------------------------------- + if (language !== lastLang) { + // Language changed => reinitialize banner content + const content = bannerContent[language]; + initCookieConsent(content); + lastLang = language; + } + } + + function scheduleUpdate() { + update(); + requestAnimationFrame(update); + setTimeout(update, 400); + } + + // 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; + }; + }); + + // --- Persistent link binder setup --- + let boundResetLink = null; + + function onResetConsentClick(e) { + e.preventDefault(); + if (typeof resetCookieConsent === 'function') { + resetCookieConsent(); + } + } + + function bindResetConsentLink() { + const language = checkLanguage(); + const link = document.querySelector( + `a[href$="change-cookie-consent-${language}"]`, + ); + + if (boundResetLink && !boundResetLink.isConnected) { + boundResetLink = null; + } + + if (link && link !== boundResetLink) { + boundResetLink = link; + link.addEventListener('click', onResetConsentClick); + } + + if (!link) { + boundResetLink = null; + } + } + + // --- No observer: rebind on known lifecycle/navigation points --- + function scheduleBindResetConsentLink() { + // immediate attempt + bindResetConsentLink(); + // after paint (for SPA updates that render slightly later) + requestAnimationFrame(bindResetConsentLink); + // delayed fallback (for async content loading) + setTimeout(bindResetConsentLink, 600); + } + + function onNavigationUpdate() { + scheduleBindResetConsentLink(); + scheduleUpdate(); + } + + // --- Bind on initial DOM load --- + document.addEventListener('DOMContentLoaded', () => { + scheduleBindResetConsentLink(); + scheduleUpdate(); + }); + + // --- Bind on SPA navigation & back/forward --- + globalThis.addEventListener('rr-nav', onNavigationUpdate); + + globalThis.addEventListener('popstate', onNavigationUpdate); +})(); diff --git a/packages/pxweb2/ssbstyle.css b/packages/pxweb2/ssbstyle.css new file mode 100644 index 000000000..d78c8389c --- /dev/null +++ b/packages/pxweb2/ssbstyle.css @@ -0,0 +1,249 @@ +.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; +} + +/* Cookie banner styling from CMS */ + +.cookie-banner { + position: fixed; + bottom: 1.56rem; + left: 1.56rem; + width: 41.5rem; + max-width: calc(100vw - 3.2rem); + padding: 1.75rem; + background: #00824d; + border-radius: 0.5rem; + z-index: 2147483647; + font-family: PxWeb-font, sans-serif; +} + +.cookie-banner:focus { + outline: none; +} + +.cookie-banner-title, +.cookie-banner-text { + color: #fff; +} + +.cookie-banner-title { + margin-top: 0; + margin-bottom: 0.5rem; + font-size: 1.25rem; + line-height: 2rem; +} + +.cookie-banner-text { + margin-top: 0; + margin-bottom: 0.63rem; + font-size: 1rem; + font-stretch: normal; + font-style: normal; + font-weight: 400; + letter-spacing: normal; + line-height: 1.7; +} + +.cookie-banner-buttons { + display: flex; + gap: 1rem; + margin-top: 1.25rem; + color: #fff; +} + +.cookie-banner-buttons .cookie-button-accept, +.cookie-banner-buttons .cookie-button-decline { + background: #fff; + color: #00824d; + margin: 0.12rem 0.12rem 0.12rem 0; + border: none; + font-size: 1rem; + min-height: 2.5rem; +} + +.cookie-banner-buttons + :is(.cookie-button-accept, .cookie-button-decline):is(:hover, :focus) { + outline-color: #fff; + text-decoration: underline; +} + +.cookie-banner-buttons + :is(.cookie-button-accept, .cookie-button-decline):hover { + cursor: pointer; +} + +.cookie-banner .ssb-link.negative { + border-bottom-color: #fff !important; + background-image: linear-gradient(120deg, #fff 0%, #fff 100%) !important; + color: #b6e8b8; +} + +.cookie-banner .ssb-link.negative .link-text { + font-size: 1rem; + color: #fff !important; +} + +.cookie-banner .ssb-link.negative:hover, +.cookie-banner .ssb-link.negative:focus { + border-bottom-color: transparent; + color: #274247; + outline: 0; + background-size: 100% 100%; +} + +.cookie-banner .ssb-link.negative:hover .link-text, +.cookie-banner .ssb-link.negative:focus .link-text { + color: #00824d !important; +} + +.ssb-btn { + background-color: transparent; + border: 2px solid #00824d; + border-radius: 2px; + color: #00824d; + font-family: inherit; + font-size: 16px; + font-weight: 700; + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; + line-height: 1.25; + min-height: 40px; + padding: 12px 20px; + text-align: center; + text-underline-position: under; + transition: + background 0.18s, + color 0.18s; + white-space: nowrap; +} + +.ssb-btn:focus { + outline: 2px solid #9272fc; + outline-offset: 2px; +} + +.ssb-link { + font-stretch: normal; + font-weight: 400; + background-image: linear-gradient(120deg, #00824d, #00824d); + background-position: 0 100%; + background-repeat: no-repeat; + background-size: 100% 0; + border-bottom: 1px solid #00824d; + color: #00824d; + cursor: pointer; + display: inline; + font-size: 16px; + line-height: 1.7; + margin-top: -3px; + margin-bottom: -2px; + padding: 0 1px 2px; + position: relative; + text-decoration: none; + transition: + background-size 0.2s ease-in, + color 0.1s; +} + +@media (max-width: 768px) { + .cookie-banner { + right: 1rem; + left: 1rem; + bottom: 1rem; + min-width: calc(100vw - 2rem); + max-height: calc(100vh - 2rem); + flex-direction: column; + overflow-y: auto; + } + + .cookie-banner-title, + .cookie-banner-text, + .cookie-banner-link { + width: fit-content; + } + + .cookie-banner-text { + margin-bottom: 1.25rem; + } + + .cookie-banner-link { + margin-bottom: 1.75rem; + } + + .cookie-banner-buttons { + flex-direction: column; + gap: 0.75rem; + } + + .cookie-banner-buttons .cookie-button-accept, + .cookie-banner-buttons .cookie-button-decline { + display: flex; + align-items: center; + justify-content: center; + text-align: center; + } +}