From bf57db04d6f1cf90fb82f386a8f16a7ab97f9640 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Fri, 12 Dec 2025 12:28:28 +0100 Subject: [PATCH 01/21] Split Alert into LocalAlert and GlobalAlert --- .../GlobalAlert/GlobalAlert.module.scss | 276 ++++++++++++++++++ .../GlobalAlert/GlobalAlert.spec.tsx | 32 ++ .../GlobalAlert/GlobalAlert.stories.tsx | 53 ++++ .../components/GlobalAlert/GlobalAlert.tsx | 152 ++++++++++ .../LocalAlert/LocalAlert.module.scss | 276 ++++++++++++++++++ .../components/LocalAlert/LocalAlert.spec.tsx | 32 ++ .../LocalAlert/LocalAlert.stories.tsx | 204 +++++++++++++ .../lib/components/LocalAlert/LocalAlert.tsx | 252 ++++++++++++++++ packages/pxweb2/public/config/config.js | 6 +- 9 files changed, 1280 insertions(+), 3 deletions(-) create mode 100644 packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss create mode 100644 packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.spec.tsx create mode 100644 packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.stories.tsx create mode 100644 packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx create mode 100644 packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.module.scss create mode 100644 packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.spec.tsx create mode 100644 packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.stories.tsx create mode 100644 packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss new file mode 100644 index 000000000..97ab2a7a7 --- /dev/null +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss @@ -0,0 +1,276 @@ +@use '../../../../style-dictionary/dist/scss/fixed-variables.scss' as fixvar; +@use '../../text-styles.scss'; + +.alert-medium { + display: flex; + padding: fixvar.$spacing-5 fixvar.$spacing-4; + align-items: flex-start; + gap: fixvar.$spacing-3; + // border-radius: var(--px-border-radius-large); +} +.alert-small { + display: flex; + padding: fixvar.$spacing-4; + align-items: flex-start; + gap: fixvar.$spacing-2; + // border-radius: var(--px-border-radius-medium); +} + +.info { + background: var(--px-color-surface-info-moderate); +} + +.info-clickable { + &:hover { + box-shadow: inset 0px 0px 0px 2px var(--px-color-border-info); + h2 { + @extend .heading-underline; + } + } + &:hover .alert-arrow-wrapper { + padding-inline-start: 8px; + transition: 100ms; + } + &:focus-visible { + outline: 3px solid var(--px-color-border-focus-outline); + outline-offset: 3px; + box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); + } +} + +.success { + background: var(--px-color-surface-success-moderate); +} +.success-clickable { + &:hover { + box-shadow: inset 0px 0px 0px 2px var(--px-color-border-success); + h2 { + @extend .heading-underline; + } + } + &:hover .alert-arrow-wrapper { + padding-inline-start: 8px; + transition: 100ms; + } + &:focus-visible { + outline: 3px solid var(--px-color-border-focus-outline); + outline-offset: 3px; + box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); + } +} +.warning { + background: var(--px-color-surface-warning-moderate); +} +.warning-clickable { + &:hover { + box-shadow: inset 0px 0px 0px 2px var(--px-color-border-warning); + h2 { + @extend .heading-underline; + } + } + &:hover .alert-arrow-wrapper { + padding-inline-start: 8px; + transition: 100ms; + } + &:focus-visible { + outline: 3px solid var(--px-color-border-focus-outline); + outline-offset: 3px; + box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); + } +} +.error { + background: var(--px-color-surface-error-moderate); +} +.error-clickable { + &:hover { + box-shadow: inset 0px 0px 0px 2px var(--px-color-border-error); + h2 { + @extend .heading-underline; + } + } + &:hover .alert-arrow-wrapper { + padding-inline-start: 8px; + transition: 100ms; + } + &:focus-visible { + outline: 3px solid var(--px-color-border-focus-outline); + outline-offset: 3px; + box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); + } +} + +.alert-section-left-medium { + display: flex; + height: fixvar.$spacing-6; + margin-top: 0.25rem; + flex-direction: column; + justify-content: center; + align-items: flex-start; + flex-shrink: 0; +} + +.alert-section-left-small { + display: flex; + height: fixvar.$spacing-6; + padding-top: fixvar.$spacing-1; + flex-direction: column; + align-items: flex-start; + flex-shrink: 0; +} + +.alert-section-middle-medium { + display: flex; + flex-direction: column; + padding-top: 2px; + row-gap: fixvar.$spacing-2; +} +.alert-section-middle-medium:has(ol) { + row-gap: fixvar.$spacing-4; +} +.alert-section-middle-medium:has(ul) { + row-gap: fixvar.$spacing-4; +} + +.alert-section-middle-small { + display: flex; + flex-direction: column; + padding-top: 2px; + // row-gap: fixvar.$spacing-1; +} + +.alert-heading { + display: flex; + flex-direction: column; + align-items: flex-start; + padding-top: 1px; + flex: 1 0 0; +} + +.alert-body-small { + display: flex; + align-self: stretch; + padding-top: 2px; + overflow-wrap: anywhere; + word-break: break-word; + hyphens: auto; +} + +.alert-body-medium { + display: flex; + align-self: stretch; + overflow-wrap: anywhere; + word-break: break-word; + hyphens: auto; +} + +.truncate-text-small { + max-height: fixvar.$spacing-12; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + text-overflow: ellipsis; +} + +.truncate-text-medium { + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + text-overflow: ellipsis; +} + +.alert-section-right { + display: flex; + align-self: center; +} + +.alert-section-right-close-medium { + display: flex; + align-self: flex-start; + margin-inline-start: auto; + height: 32px; + align-items: center; + gap: 6px; +} + +.alert-section-right-close-small { + display: flex; + align-self: flex-start; + margin-inline-start: auto; + height: 32px; + align-items: center; + gap: 6px; +} + +.alert-arrow { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border: none; + background-color: transparent; + cursor: pointer; +} + +.alert-section-right-continue { + display: flex; + margin-inline-start: auto; + width: 44px; + align-self: center; + align-items: center; +} +.alert-arrow-wrapper { + display: flex; + width: 44px; + justify-content: center; + flex-shrink: 0; + align-self: center; + margin-inline-start: auto; +} + +.alert-xmark { + display: flex; + justify-content: flex-end; + align-items: flex-start; + align-self: stretch; + margin-inline-start: auto; +} + +.alert-xmark-wrapper { + display: flex; + justify-content: center; + align-items: center; + gap: var(--spacing-2); +} + +.alert-icon-info { + --px-icon-color: var(--px-color-icon-info); + flex-shrink: 0; +} + +.alert-icon-success { + --px-icon-color: var(--px-color-icon-success); +} + +.alert-icon-warning { + --px-icon-color: var(--px-color-icon-warning); +} + +.alert-icon-error { + --px-icon-color: var(--px-color-icon-error); +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.spec.tsx b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.spec.tsx new file mode 100644 index 000000000..a703c6967 --- /dev/null +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.spec.tsx @@ -0,0 +1,32 @@ +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom/vitest'; + +import GlobalAlert, { GlobalAlertProps } from './GlobalAlert'; + +describe('GlobalAlert', () => { + it('should render successfully', () => { + const { baseElement } = render(); + expect(baseElement).toBeTruthy(); + }); +}); +it.each([ + ['polite', 'info'], + ['polite', 'success'], + ['assertive', 'warning'], + ['assertive', 'error'], +])( + 'should set aria-live="%s" for variant "%s"', + (expectedAriaLive, variant) => { + const headingText = `Test heading for ${variant}`; + const { getByText } = render( + , + ); + const heading = getByText(headingText); + const headingContainer = heading.closest('div[aria-live]'); + expect(headingContainer).not.toBeNull(); + expect(headingContainer).toHaveAttribute('aria-live', expectedAriaLive); + }, +); diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.stories.tsx b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.stories.tsx new file mode 100644 index 000000000..3ef645e44 --- /dev/null +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.stories.tsx @@ -0,0 +1,53 @@ +import type { Meta, StoryFn } from '@storybook/react-vite'; + +import { GlobalAlert } from './GlobalAlert'; +import { Link } from '../Link/Link'; + +const meta: Meta = { + component: GlobalAlert, + title: 'Components/GlobalAlert', + parameters: { + layout: 'padded', + }, +}; +export default meta; + +export const VariantL = { + args: { + variant: 'info', + heading: 'Welcome', + children: + "Welcome to the new PxWeb 2.0! We're still improving to help you find and use the numbers you need", + }, +}; + +export const WithLink: StoryFn = () => { + return ( + <> +
+
+ + + SSB + + +
+ + ); +}; + +export const WithTextAndLink: StoryFn = () => { + return ( + <> +
+
+ + Would you like to se this table in the old user interface:{' '} + + Old table + + +
+ + ); +}; diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx new file mode 100644 index 000000000..1548e06fa --- /dev/null +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx @@ -0,0 +1,152 @@ +import React, { useState } from 'react'; +import cl from 'clsx'; + +import { useTranslation } from 'react-i18next'; +import classes from './GlobalAlert.module.scss'; +import BodyLong from '../Typography/BodyLong/BodyLong'; +import Heading from '../Typography/Heading/Heading'; +import { Icon, IconProps } from '../Icon/Icon'; +import Button from '../Button/Button'; +import BodyShort from '../Typography/BodyShort/BodyShort'; + +export interface GlobalAlertProps { + readonly size?: 'small' | 'medium'; + readonly variant: 'info' | 'success' | 'warning' | 'error'; + readonly closeButton?: boolean; + readonly heading?: string; + readonly headingLevel?: '1' | '2' | '3' | '4' | '5' | '6'; + readonly onDismissed?: () => void; + readonly className?: string; + readonly children?: string | React.ReactNode; + readonly alertAriaLabel?: string; + readonly isRoleAlert?: boolean; + ref?: React.Ref; + id?: string; +} + +export function GlobalAlert({ + size = 'medium', + variant = 'info', + closeButton = false, + heading = '', + headingLevel = '2', + onDismissed, + className = '', + children, + alertAriaLabel, + isRoleAlert = true, + ref, + id, +}: Readonly) { + const cssClasses = className.length > 0 ? ' ' + className : ''; + const { t } = useTranslation(); + const [isVisible, setIsVisible] = useState(true); + const HandleClose = () => { + setIsVisible(false); + onDismissed?.(); + }; + if (!isVisible) { + return null; + } + const hasheading = Boolean(heading); + const iconClose = 'XMark'; + let variantIcon: IconProps['iconName']; + // let variantAriaLive: 'polite' | 'assertive'; + switch (variant) { + case 'info': + variantIcon = 'InformationCircleFilled'; + // variantAriaLive = 'polite'; + break; + case 'success': + variantIcon = 'CheckMarkCircleFilled'; + // variantAriaLive = 'polite'; + break; + case 'warning': + variantIcon = 'ExclamationMarkFilled'; + // variantAriaLive = 'assertive'; + break; + case 'error': + variantIcon = 'XMarkCircleFilled'; + // variantAriaLive = 'assertive'; + break; + } + let headingSize: 'small' | 'xsmall'; + const bodySize = 'medium'; + + switch (size) { + case 'small': + headingSize = 'xsmall'; + break; + case 'medium': + headingSize = 'small'; + break; + default: + headingSize = 'small'; + } + + return ( +
+
+ {!hasheading && ( + + {t(`common.alert.${variant}`)} + + )} + +
+
+ {hasheading && ( +
+ + {t(`common.alert.${variant}`)} + + + {heading} + +
+ )} +
+ {size === 'small' ? ( + {children} + ) : ( + + {children} + + )} +
+
+
+ {closeButton && ( +
+
+ )} +
+
+ ); +} + +export default GlobalAlert; diff --git a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.module.scss new file mode 100644 index 000000000..c251460a9 --- /dev/null +++ b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.module.scss @@ -0,0 +1,276 @@ +@use '../../../../style-dictionary/dist/scss/fixed-variables.scss' as fixvar; +@use '../../text-styles.scss'; + +.alert-medium { + display: flex; + padding: fixvar.$spacing-5 fixvar.$spacing-6; + align-items: flex-start; + gap: fixvar.$spacing-3; + border-radius: var(--px-border-radius-large); +} +.alert-small { + display: flex; + padding: fixvar.$spacing-4; + align-items: flex-start; + gap: fixvar.$spacing-2; + border-radius: var(--px-border-radius-medium); +} + +.info { + background: var(--px-color-surface-info-moderate); +} + +.info-clickable { + &:hover { + box-shadow: inset 0px 0px 0px 2px var(--px-color-border-info); + h2 { + @extend .heading-underline; + } + } + &:hover .alert-arrow-wrapper { + padding-inline-start: 8px; + transition: 100ms; + } + &:focus-visible { + outline: 3px solid var(--px-color-border-focus-outline); + outline-offset: 3px; + box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); + } +} + +.success { + background: var(--px-color-surface-success-moderate); +} +.success-clickable { + &:hover { + box-shadow: inset 0px 0px 0px 2px var(--px-color-border-success); + h2 { + @extend .heading-underline; + } + } + &:hover .alert-arrow-wrapper { + padding-inline-start: 8px; + transition: 100ms; + } + &:focus-visible { + outline: 3px solid var(--px-color-border-focus-outline); + outline-offset: 3px; + box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); + } +} +.warning { + background: var(--px-color-surface-warning-moderate); +} +.warning-clickable { + &:hover { + box-shadow: inset 0px 0px 0px 2px var(--px-color-border-warning); + h2 { + @extend .heading-underline; + } + } + &:hover .alert-arrow-wrapper { + padding-inline-start: 8px; + transition: 100ms; + } + &:focus-visible { + outline: 3px solid var(--px-color-border-focus-outline); + outline-offset: 3px; + box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); + } +} +.error { + background: var(--px-color-surface-error-moderate); +} +.error-clickable { + &:hover { + box-shadow: inset 0px 0px 0px 2px var(--px-color-border-error); + h2 { + @extend .heading-underline; + } + } + &:hover .alert-arrow-wrapper { + padding-inline-start: 8px; + transition: 100ms; + } + &:focus-visible { + outline: 3px solid var(--px-color-border-focus-outline); + outline-offset: 3px; + box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); + } +} + +.alert-section-left-medium { + display: flex; + height: fixvar.$spacing-6; + margin-top: 0.25rem; + flex-direction: column; + justify-content: center; + align-items: flex-start; + flex-shrink: 0; +} + +.alert-section-left-small { + display: flex; + height: fixvar.$spacing-6; + padding-top: fixvar.$spacing-1; + flex-direction: column; + align-items: flex-start; + flex-shrink: 0; +} + +.alert-section-middle-medium { + display: flex; + flex-direction: column; + padding-top: 2px; + row-gap: fixvar.$spacing-2; +} +.alert-section-middle-medium:has(ol) { + row-gap: fixvar.$spacing-4; +} +.alert-section-middle-medium:has(ul) { + row-gap: fixvar.$spacing-4; +} + +.alert-section-middle-small { + display: flex; + flex-direction: column; + padding-top: 2px; + // row-gap: fixvar.$spacing-1; +} + +.alert-heading { + display: flex; + flex-direction: column; + align-items: flex-start; + padding-top: 1px; + flex: 1 0 0; +} + +.alert-body-small { + display: flex; + align-self: stretch; + padding-top: 2px; + overflow-wrap: anywhere; + word-break: break-word; + hyphens: auto; +} + +.alert-body-medium { + display: flex; + align-self: stretch; + overflow-wrap: anywhere; + word-break: break-word; + hyphens: auto; +} + +.truncate-text-small { + max-height: fixvar.$spacing-12; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + text-overflow: ellipsis; +} + +.truncate-text-medium { + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + text-overflow: ellipsis; +} + +.alert-section-right { + display: flex; + align-self: center; +} + +.alert-section-right-close-medium { + display: flex; + align-self: flex-start; + margin-inline-start: auto; + height: 32px; + align-items: center; + gap: 6px; +} + +.alert-section-right-close-small { + display: flex; + align-self: flex-start; + margin-inline-start: auto; + height: 32px; + align-items: center; + gap: 6px; +} + +.alert-arrow { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border: none; + background-color: transparent; + cursor: pointer; +} + +.alert-section-right-continue { + display: flex; + margin-inline-start: auto; + width: 44px; + align-self: center; + align-items: center; +} +.alert-arrow-wrapper { + display: flex; + width: 44px; + justify-content: center; + flex-shrink: 0; + align-self: center; + margin-inline-start: auto; +} + +.alert-xmark { + display: flex; + justify-content: flex-end; + align-items: flex-start; + align-self: stretch; + margin-inline-start: auto; +} + +.alert-xmark-wrapper { + display: flex; + justify-content: center; + align-items: center; + gap: var(--spacing-2); +} + +.alert-icon-info { + --px-icon-color: var(--px-color-icon-info); + flex-shrink: 0; +} + +.alert-icon-success { + --px-icon-color: var(--px-color-icon-success); +} + +.alert-icon-warning { + --px-icon-color: var(--px-color-icon-warning); +} + +.alert-icon-error { + --px-icon-color: var(--px-color-icon-error); +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} diff --git a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.spec.tsx b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.spec.tsx new file mode 100644 index 000000000..870769cc1 --- /dev/null +++ b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.spec.tsx @@ -0,0 +1,32 @@ +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom/vitest'; + +import LocalAlert, { LocalAlertProps } from './LocalAlert'; + +describe('Alert', () => { + it('should render successfully', () => { + const { baseElement } = render(); + expect(baseElement).toBeTruthy(); + }); +}); +it.each([ + ['polite', 'info'], + ['polite', 'success'], + ['assertive', 'warning'], + ['assertive', 'error'], +])( + 'should set aria-live="%s" for variant "%s"', + (expectedAriaLive, variant) => { + const headingText = `Test heading for ${variant}`; + const { getByText } = render( + , + ); + const heading = getByText(headingText); + const headingContainer = heading.closest('div[aria-live]'); + expect(headingContainer).not.toBeNull(); + expect(headingContainer).toHaveAttribute('aria-live', expectedAriaLive); + }, +); diff --git a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.stories.tsx b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.stories.tsx new file mode 100644 index 000000000..85b5c45f8 --- /dev/null +++ b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.stories.tsx @@ -0,0 +1,204 @@ +import type { Meta, StoryFn } from '@storybook/react-vite'; + +import { LocalAlert } from './LocalAlert'; +import { Link } from '../Link/Link'; +import List from '../List/List'; +import ListItem from '../List/ListItem'; + +const meta: Meta = { + component: LocalAlert, + title: 'Components/LocalAlert', + parameters: { + layout: 'padded', + }, +}; +export default meta; + +export const VariantL = { + args: { + variant: 'info', + heading: 'Vi beklager', + children: + 'Statistikkbanken er for øyeblikket nede. Vi jobber med feilen og forventer å være opp igjen i løpet av ei uke eller 2, beklager så mye', + onClick: () => { + alert( + 'Statistikkbanken er for øyeblikket nede. Vi jobber med feilen og forventer å være opp igjen i løpet av ei uke eller 2, beklager så mye ', + ); + }, + }, +}; + +export const WithLink: StoryFn = () => { + return ( + <> +
+
+ + + SSB + + +
+ + ); +}; + +export const WithTextAndLink: StoryFn = () => { + return ( + <> +
+
+ + Det finnes mer metadata om emnet. Dette kan du lese mer om her:{' '} + + SSB + + +
+ + ); +}; + +export const WithOLList: StoryFn = () => { + return ( + + + + {' '} + + Se liste over endringene i de regionale inndelingene + + + {/* note b + note c */} + + + ); +}; +export const WithULList: StoryFn = () => { + return ( + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad + minim veniam, quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum + dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua + + + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. + +
  • Note c
  • +
    +
    + ); +}; + +export const WithULListClickable: StoryFn = () => { + return ( + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad + minim veniam, quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum + dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua + + + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. + +
  • Note c
  • +
    +
    + ); +}; + +export const WithListgroupClickable: StoryFn = () => { + return ( + + + + + Se liste over endringene i de regionale inndelingene + + + Tidligere: Vestbredden/Gazastripen (2001-2012) + Vi later som denne har to + {' '} + + + Variable note 2 very very long and maybe even longer. + + Variable note 3. + + + Ble kalt Hviterussland fram til 2022. + + + + + Ble kalt Swaziland før 2018. + Ble kalt Swaziland før 2018. + + + + + Tidligere: Vestbredden/Gazastripen (2001-2012) + Vi later som denne har to fotnoter + Vi later som denne har tre fotnoter + Vi later som denne har fire fotnoter + + + + + ); +}; + +export const Test: StoryFn = () => { + return ( + + + + Gå til SSB + + + + Fotnote 11 + Fotnote 12 + Fotnote 13 + Fotnote 14 + + + Fotnote 21 + Fotnote 22 + Fotnote 23 + Fotnote 24 + + + + + ); +}; diff --git a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx new file mode 100644 index 000000000..e8deb9d12 --- /dev/null +++ b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx @@ -0,0 +1,252 @@ +import React, { useState } from 'react'; +import cl from 'clsx'; + +import { useTranslation } from 'react-i18next'; +import classes from './LocalAlert.module.scss'; +import BodyLong from '../Typography/BodyLong/BodyLong'; +import Heading from '../Typography/Heading/Heading'; +import { Icon, IconProps } from '../Icon/Icon'; +import Button from '../Button/Button'; +import BodyShort from '../Typography/BodyShort/BodyShort'; +import List, { ListProps } from '../List/List'; +import { getIconDirection } from '../../util/util'; + +export interface LocalAlertProps { + readonly size?: 'small' | 'medium'; + readonly variant: 'info' | 'success' | 'warning' | 'error'; + readonly clickable?: boolean; + readonly closeButton?: boolean; + readonly heading?: string; + readonly headingLevel?: '1' | '2' | '3' | '4' | '5' | '6'; + readonly onClick?: () => void; + readonly onDismissed?: () => void; + readonly languageDirection?: 'ltr' | 'rtl'; + readonly className?: string; + readonly children?: string | React.ReactNode; + readonly alertAriaLabel?: string; + readonly ariaHasPopup?: + | 'false' + | 'true' + | 'menu' + | 'listbox' + | 'tree' + | 'grid' + | 'dialog'; + readonly role?: React.AriaRole; + ref?: React.Ref; + id?: string; +} + +export function LocalAlert({ + size = 'medium', + variant = 'info', + clickable = false, + closeButton = false, + heading = '', + headingLevel = '2', + onClick, + onDismissed, + languageDirection = 'ltr', + className = '', + children, + alertAriaLabel, + ariaHasPopup = 'false', + role, + ref, + id, +}: Readonly) { + const cssClasses = className.length > 0 ? ' ' + className : ''; + const { t } = useTranslation(); + const [isVisible, setIsVisible] = useState(true); + const HandleClose = () => { + setIsVisible(false); + onDismissed?.(); + }; + if (!isVisible) { + return null; + } + const handleKeyDown = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + event.preventDefault(); + onClick && onClick(); + } + }; + const hasheading = Boolean(heading); + const iconArrow = getIconDirection( + languageDirection, + 'ArrowRight', + 'ArrowLeft', + ); + const iconClose = 'XMark'; + let variantIcon: IconProps['iconName']; + let variantAriaLive: 'polite' | 'assertive'; + switch (variant) { + case 'info': + variantIcon = 'InformationCircleFilled'; + variantAriaLive = 'polite'; + break; + case 'success': + variantIcon = 'CheckMarkCircleFilled'; + variantAriaLive = 'polite'; + break; + case 'warning': + variantIcon = 'ExclamationMarkFilled'; + variantAriaLive = 'assertive'; + break; + case 'error': + variantIcon = 'XMarkCircleFilled'; + variantAriaLive = 'assertive'; + break; + } + let headingSize: 'small' | 'xsmall'; + const bodySize = 'medium'; + + switch (size) { + case 'small': + headingSize = 'xsmall'; + break; + case 'medium': + headingSize = 'small'; + break; + default: + headingSize = 'small'; + } + if (clickable) { + closeButton = false; + } + + const childIsList = (node: React.ReactNode): boolean => { + if (React.isValidElement(node)) { + if (node.type === List) { + return true; + } + } + return false; + }; + + const extractTextFromChildren = (children: React.ReactNode): string => { + let textContent = ''; + + React.Children.forEach(children, (child) => { + if (React.isValidElement(child)) { + // If the child is a valid React element, check its children recursively + if (React.isValidElement(child) && child.type === List) { + textContent += ' ' + (child.props as ListProps)?.subHeading + ': '; + } + if ( + typeof child.props === 'object' && + child.props !== null && + 'children' in child.props + ) { + textContent += extractTextFromChildren( + child.props.children as React.ReactNode, + ); + } + } else if (typeof child === 'string' || typeof child === 'number') { + // If the child is a string or number, add it to the text content + textContent += ' ' + child.toString(); + } + }); + + return textContent; + }; + + if (childIsList(children) && clickable) { + let extractedText = ''; + if (React.isValidElement(children) && children.type === List) { + const listProps = children.props as ListProps; + extractedText = extractTextFromChildren(listProps.children); + } + children = extractedText; + } + + return ( +
    +
    + {!hasheading && ( + + {t(`common.alert.${variant}`)} + + )} + +
    +
    + {hasheading && ( +
    + + {t(`common.alert.${variant}`)} + + + {heading} + +
    + )} +
    + {size === 'small' ? ( + {children} + ) : ( + + {children} + + )} +
    +
    +
    + {closeButton && ( +
    +
    + )} + {clickable && ( +
    + +
    + )} +
    +
    + ); +} + +export default LocalAlert; diff --git a/packages/pxweb2/public/config/config.js b/packages/pxweb2/public/config/config.js index 54e2b9c70..f5f0d88a0 100644 --- a/packages/pxweb2/public/config/config.js +++ b/packages/pxweb2/public/config/config.js @@ -5,12 +5,12 @@ window.PxWeb2Config = { { shorthand: 'sv', languageName: 'Svenska' }, { shorthand: 'no', languageName: 'Norsk' }, ], - defaultLanguage: 'en', + defaultLanguage: 'no', fallbackLanguage: 'en', showDefaultLanguageInPath: true, }, baseApplicationPath: '/', - apiUrl: 'https://api.scb.se/OV0104/v2beta/api/v2', + apiUrl: 'https://statistikkbanken-api.nav.no/', maxDataCells: 150000, showBreadCrumbOnStartPage: false, specialCharacters: ['.', '..', ':', '-', '...', '*'], @@ -54,4 +54,4 @@ window.PxWeb2Config = { sv: '', // Set to your Swedish homepage URL en: '', // Set to your English homepage URL }, -}; \ No newline at end of file +}; From a71343ef71564de2e32d4511e38e669f1835ca7a Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Fri, 12 Dec 2025 14:06:50 +0100 Subject: [PATCH 02/21] Removed test --- .../GlobalAlert/GlobalAlert.spec.tsx | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.spec.tsx diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.spec.tsx b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.spec.tsx deleted file mode 100644 index a703c6967..000000000 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.spec.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { render } from '@testing-library/react'; -import '@testing-library/jest-dom/vitest'; - -import GlobalAlert, { GlobalAlertProps } from './GlobalAlert'; - -describe('GlobalAlert', () => { - it('should render successfully', () => { - const { baseElement } = render(); - expect(baseElement).toBeTruthy(); - }); -}); -it.each([ - ['polite', 'info'], - ['polite', 'success'], - ['assertive', 'warning'], - ['assertive', 'error'], -])( - 'should set aria-live="%s" for variant "%s"', - (expectedAriaLive, variant) => { - const headingText = `Test heading for ${variant}`; - const { getByText } = render( - , - ); - const heading = getByText(headingText); - const headingContainer = heading.closest('div[aria-live]'); - expect(headingContainer).not.toBeNull(); - expect(headingContainer).toHaveAttribute('aria-live', expectedAriaLive); - }, -); From c991dd51995a10104d2b04f867e05a9c23e2905f Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Mon, 15 Dec 2025 15:47:11 +0100 Subject: [PATCH 03/21] Added div and added calsses --- packages/pxweb2-ui/src/index.ts | 2 + .../GlobalAlert/GlobalAlert.module.scss | 43 ++++++- .../components/GlobalAlert/GlobalAlert.tsx | 116 +++++++++--------- 3 files changed, 105 insertions(+), 56 deletions(-) diff --git a/packages/pxweb2-ui/src/index.ts b/packages/pxweb2-ui/src/index.ts index b0b989f0c..39769ff4d 100644 --- a/packages/pxweb2-ui/src/index.ts +++ b/packages/pxweb2-ui/src/index.ts @@ -14,11 +14,13 @@ export * from './lib/components/ContentBox/ContentBox'; export * from './lib/components/DetailsSection/DetailsSection'; export * from './lib/components/EmptyState/EmptyState'; export * from './lib/components/FilterCategory/FilterCategory'; +export * from './lib/components/GlobalAlert/GlobalAlert'; export * from './lib/components/Icon/Icon'; export * from './lib/components/Icon/ActionItemIcon'; export * from './lib/components/InformationCard/InformationCard'; export * from './lib/components/Link/Link'; export * from './lib/components/List'; +export * from './lib/components/LocalAlert/LocalAlert'; export * from './lib/components/Notes/MandatoryNotes'; export * from './lib/components/Notes/MandatoryTableNotes'; export * from './lib/components/Notes/MandatoryVariableNotes'; diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss index 97ab2a7a7..189872ca1 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss @@ -1,9 +1,10 @@ @use '../../../../style-dictionary/dist/scss/fixed-variables.scss' as fixvar; @use '../../text-styles.scss'; +@use '$ui/src/lib/breakpoints.scss' as breakpoint; .alert-medium { display: flex; - padding: fixvar.$spacing-5 fixvar.$spacing-4; + padding: fixvar.$spacing-5 0px; align-items: flex-start; gap: fixvar.$spacing-3; // border-radius: var(--px-border-radius-large); @@ -16,6 +17,46 @@ // border-radius: var(--px-border-radius-medium); } +.container { + display: block; + margin-left: auto; + margin-right: auto; + + @media #{breakpoint.$xsmall} { + margin-left: 1rem; + margin-right: 1rem; + } + + @media #{breakpoint.$small} { + max-width: 508px; + } + + @media #{breakpoint.$medium} { + max-width: 688px; + } + + @media #{breakpoint.$large} { + max-width: 928px; + } + + @media #{breakpoint.$xlarge} { + max-width: 1108px; + } + + @media #{breakpoint.$xxlarge} { + max-width: 1288px; + } +} + +// .alert-container-medium { +// display: block; +// justify-content: center; +// max-width: 1288px; +// margin-left: auto; +// margin-right: auto; +// // border-radius: var(--px-border-radius-large); +// } + .info { background: var(--px-color-surface-info-moderate); } diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx index 1548e06fa..6fb51a370 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx @@ -85,66 +85,72 @@ export function GlobalAlert({ } return ( -
    -
    - {!hasheading && ( - - {t(`common.alert.${variant}`)} - - )} - -
    -
    - {hasheading && ( +
    +
    +
    +
    + {!hasheading && ( + + {t(`common.alert.${variant}`)} + + )} + +
    +
    + {hasheading && ( +
    + + {t(`common.alert.${variant}`)} + + + {heading} + +
    + )} +
    + {size === 'small' ? ( + {children} + ) : ( + + {children} + + )} +
    +
    - - {t(`common.alert.${variant}`)} - - - {heading} - + {closeButton && ( +
    +
    + )}
    - )} -
    - {size === 'small' ? ( - {children} - ) : ( - - {children} - - )}
    -
    - {closeButton && ( -
    -
    - )} -
    ); } From 2e76a3578e21a1d450d51ebc0396ad236a28ff4b Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 07:48:53 +0100 Subject: [PATCH 04/21] Use GlobaLaert in WipStatusMessage --- .../src/app/components/Banners/WipStatusMessage.spec.tsx | 2 +- .../pxweb2/src/app/components/Banners/WipStatusMessage.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/pxweb2/src/app/components/Banners/WipStatusMessage.spec.tsx b/packages/pxweb2/src/app/components/Banners/WipStatusMessage.spec.tsx index c14b68513..4c25b1d57 100644 --- a/packages/pxweb2/src/app/components/Banners/WipStatusMessage.spec.tsx +++ b/packages/pxweb2/src/app/components/Banners/WipStatusMessage.spec.tsx @@ -13,7 +13,7 @@ vi.mock('react-i18next', () => ({ // Mock Alert component vi.mock('@pxweb2/pxweb2-ui', () => ({ - Alert: ({ + GlobalAlert: ({ children, onDismissed, closeButton, diff --git a/packages/pxweb2/src/app/components/Banners/WipStatusMessage.tsx b/packages/pxweb2/src/app/components/Banners/WipStatusMessage.tsx index 361fdf156..df6226721 100644 --- a/packages/pxweb2/src/app/components/Banners/WipStatusMessage.tsx +++ b/packages/pxweb2/src/app/components/Banners/WipStatusMessage.tsx @@ -1,7 +1,7 @@ import { useEffect, useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { Alert } from '@pxweb2/pxweb2-ui'; +import { GlobalAlert } from '@pxweb2/pxweb2-ui'; import classes from './WipStatusMessage.module.scss'; const SESSION_STORAGE_KEY = 'pxweb2.wip_status_message_dismissed'; @@ -28,13 +28,13 @@ export default function WipStatusMessage() { } return ( - {t('common.status_messages.welcome')} - + ); } From 20adaf18312b5dfd038cb12f58c49116d8923270 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 07:56:23 +0100 Subject: [PATCH 05/21] Use relative import --- .../src/lib/components/GlobalAlert/GlobalAlert.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss index 189872ca1..c0eceb76b 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss @@ -1,6 +1,6 @@ @use '../../../../style-dictionary/dist/scss/fixed-variables.scss' as fixvar; @use '../../text-styles.scss'; -@use '$ui/src/lib/breakpoints.scss' as breakpoint; +@use '../../breakpoints.scss' as breakpoint; .alert-medium { display: flex; From 0fc6b8dfa90f41d259449cb47993ed858c65776a Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 09:55:42 +0100 Subject: [PATCH 06/21] Original config --- packages/pxweb2/public/config/config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pxweb2/public/config/config.js b/packages/pxweb2/public/config/config.js index f5f0d88a0..d848546b6 100644 --- a/packages/pxweb2/public/config/config.js +++ b/packages/pxweb2/public/config/config.js @@ -5,12 +5,12 @@ window.PxWeb2Config = { { shorthand: 'sv', languageName: 'Svenska' }, { shorthand: 'no', languageName: 'Norsk' }, ], - defaultLanguage: 'no', + defaultLanguage: 'en', fallbackLanguage: 'en', showDefaultLanguageInPath: true, }, baseApplicationPath: '/', - apiUrl: 'https://statistikkbanken-api.nav.no/', + apiUrl: 'https://api.scb.se/OV0104/v2beta/api/v2', maxDataCells: 150000, showBreadCrumbOnStartPage: false, specialCharacters: ['.', '..', ':', '-', '...', '*'], From 00b99d31f259d5a7224cda8735e75ac5f9a79f9f Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 09:58:31 +0100 Subject: [PATCH 07/21] Code cleanup --- .../GlobalAlert/GlobalAlert.module.scss | 127 ------------------ 1 file changed, 127 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss index c0eceb76b..bbfde3217 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss @@ -7,14 +7,12 @@ padding: fixvar.$spacing-5 0px; align-items: flex-start; gap: fixvar.$spacing-3; - // border-radius: var(--px-border-radius-large); } .alert-small { display: flex; padding: fixvar.$spacing-4; align-items: flex-start; gap: fixvar.$spacing-2; - // border-radius: var(--px-border-radius-medium); } .container { @@ -48,97 +46,19 @@ } } -// .alert-container-medium { -// display: block; -// justify-content: center; -// max-width: 1288px; -// margin-left: auto; -// margin-right: auto; -// // border-radius: var(--px-border-radius-large); -// } - .info { background: var(--px-color-surface-info-moderate); } -.info-clickable { - &:hover { - box-shadow: inset 0px 0px 0px 2px var(--px-color-border-info); - h2 { - @extend .heading-underline; - } - } - &:hover .alert-arrow-wrapper { - padding-inline-start: 8px; - transition: 100ms; - } - &:focus-visible { - outline: 3px solid var(--px-color-border-focus-outline); - outline-offset: 3px; - box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); - } -} - .success { background: var(--px-color-surface-success-moderate); } -.success-clickable { - &:hover { - box-shadow: inset 0px 0px 0px 2px var(--px-color-border-success); - h2 { - @extend .heading-underline; - } - } - &:hover .alert-arrow-wrapper { - padding-inline-start: 8px; - transition: 100ms; - } - &:focus-visible { - outline: 3px solid var(--px-color-border-focus-outline); - outline-offset: 3px; - box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); - } -} .warning { background: var(--px-color-surface-warning-moderate); } -.warning-clickable { - &:hover { - box-shadow: inset 0px 0px 0px 2px var(--px-color-border-warning); - h2 { - @extend .heading-underline; - } - } - &:hover .alert-arrow-wrapper { - padding-inline-start: 8px; - transition: 100ms; - } - &:focus-visible { - outline: 3px solid var(--px-color-border-focus-outline); - outline-offset: 3px; - box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); - } -} .error { background: var(--px-color-surface-error-moderate); } -.error-clickable { - &:hover { - box-shadow: inset 0px 0px 0px 2px var(--px-color-border-error); - h2 { - @extend .heading-underline; - } - } - &:hover .alert-arrow-wrapper { - padding-inline-start: 8px; - transition: 100ms; - } - &:focus-visible { - outline: 3px solid var(--px-color-border-focus-outline); - outline-offset: 3px; - box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); - } -} .alert-section-left-medium { display: flex; @@ -176,7 +96,6 @@ display: flex; flex-direction: column; padding-top: 2px; - // row-gap: fixvar.$spacing-1; } .alert-heading { @@ -204,25 +123,6 @@ hyphens: auto; } -.truncate-text-small { - max-height: fixvar.$spacing-12; - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 2; - line-clamp: 2; - -webkit-box-orient: vertical; - text-overflow: ellipsis; -} - -.truncate-text-medium { - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 2; - line-clamp: 2; - -webkit-box-orient: vertical; - text-overflow: ellipsis; -} - .alert-section-right { display: flex; align-self: center; @@ -246,33 +146,6 @@ gap: 6px; } -.alert-arrow { - display: flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - border: none; - background-color: transparent; - cursor: pointer; -} - -.alert-section-right-continue { - display: flex; - margin-inline-start: auto; - width: 44px; - align-self: center; - align-items: center; -} -.alert-arrow-wrapper { - display: flex; - width: 44px; - justify-content: center; - flex-shrink: 0; - align-self: center; - margin-inline-start: auto; -} - .alert-xmark { display: flex; justify-content: flex-end; From fb0e35532f9223cb7290525227a7cc66e1c99113 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 10:00:44 +0100 Subject: [PATCH 08/21] Use LocalAlert instead og Alert --- .gitignore | 3 +++ .../components/MarkdownRenderer/MarkdownRenderer.tsx | 2 -- .../src/lib/components/Notes/MandatoryTableNotes.tsx | 6 +++--- .../lib/components/Notes/MandatoryVariableNotes.tsx | 6 +++--- .../VariableBoxHeader/VariableBoxHeader.tsx | 6 +++--- .../src/app/components/ContentTop/ContentTop.tsx | 6 +++--- .../NavigationDrawer/Drawers/DrawerEdit.tsx | 11 ++++++++--- .../NavigationDrawer/Drawers/DrawerHelp.tsx | 6 +++--- .../NavigationDrawer/Drawers/DrawerView.tsx | 6 +++--- .../src/app/components/Presentation/Presentation.tsx | 6 +++--- .../components/TableInformation/TableInformation.tsx | 6 +++--- 11 files changed, 35 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 2245e061a..a7638499f 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,6 @@ vite.config.*.timestamp* # AI .qodo + +# Snyk Security Extension - AI Rules (auto-generated) +.github/instructions/snyk_rules.instructions.md diff --git a/packages/pxweb2-ui/src/lib/components/MarkdownRenderer/MarkdownRenderer.tsx b/packages/pxweb2-ui/src/lib/components/MarkdownRenderer/MarkdownRenderer.tsx index a91bd3e9d..4263b570e 100644 --- a/packages/pxweb2-ui/src/lib/components/MarkdownRenderer/MarkdownRenderer.tsx +++ b/packages/pxweb2-ui/src/lib/components/MarkdownRenderer/MarkdownRenderer.tsx @@ -10,13 +10,11 @@ type MdProps = { type LinkProps = { href?: string; children?: React.ReactNode }; type UnwantedMdRenderProps = { children?: React.ReactNode }; - function escapeDecimalLikeLabels(markdown: string): string { // Lines like "4.1 some text" → "4\.1 some text" return markdown.replace(/^(\d+)\.(?=\s)/gm, '$1\\.'); } - const LinkRenderer = ({ href = '', children }: LinkProps) => ( {children} diff --git a/packages/pxweb2-ui/src/lib/components/Notes/MandatoryTableNotes.tsx b/packages/pxweb2-ui/src/lib/components/Notes/MandatoryTableNotes.tsx index 44bdab0b9..e1c906f92 100644 --- a/packages/pxweb2-ui/src/lib/components/Notes/MandatoryTableNotes.tsx +++ b/packages/pxweb2-ui/src/lib/components/Notes/MandatoryTableNotes.tsx @@ -4,7 +4,7 @@ import cl from 'clsx'; import classes from './Notes.module.scss'; import List from '../List/List'; import ListItem from '../List/ListItem'; -import Alert from '../Alert/Alert'; +import LocalAlert from '../LocalAlert/LocalAlert'; import MarkdownRenderer from '../MarkdownRenderer/MarkdownRenderer'; export type MandatoryTableNotesProps = { @@ -25,7 +25,7 @@ export function MandatoryTableNotes({ notes }: MandatoryTableNotesProps) { ); return ( - )} - + ); } diff --git a/packages/pxweb2-ui/src/lib/components/Notes/MandatoryVariableNotes.tsx b/packages/pxweb2-ui/src/lib/components/Notes/MandatoryVariableNotes.tsx index a46c3267f..026490d2a 100644 --- a/packages/pxweb2-ui/src/lib/components/Notes/MandatoryVariableNotes.tsx +++ b/packages/pxweb2-ui/src/lib/components/Notes/MandatoryVariableNotes.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next'; import cl from 'clsx'; import classes from './Notes.module.scss'; -import Alert from '../Alert/Alert'; +import LocalAlert from '../LocalAlert/LocalAlert'; import { variableNotes } from './noteCollection'; import { VariableNotes } from './VariableNotes'; @@ -24,13 +24,13 @@ export function MandatoryVariableNotes({ variableNotes.variableName; return ( - - + ); } diff --git a/packages/pxweb2-ui/src/lib/components/VariableBox/VariableBoxHeader/VariableBoxHeader.tsx b/packages/pxweb2-ui/src/lib/components/VariableBox/VariableBoxHeader/VariableBoxHeader.tsx index 37324b9aa..ce04cf8e9 100644 --- a/packages/pxweb2-ui/src/lib/components/VariableBox/VariableBoxHeader/VariableBoxHeader.tsx +++ b/packages/pxweb2-ui/src/lib/components/VariableBox/VariableBoxHeader/VariableBoxHeader.tsx @@ -5,7 +5,7 @@ import classes from './VariableBoxHeader.module.scss'; import { Icon } from '../../Icon/Icon'; import Tag from '../../Tag/Tag'; import { VariableBoxProps } from '../VariableBox'; -import Alert from '../../Alert/Alert'; +import LocalAlert from '../../LocalAlert/LocalAlert'; import Heading from '../../Typography/Heading/Heading'; type VariableBoxPropsToHeader = Pick; @@ -113,11 +113,11 @@ export function VariableBoxHeader({ {isMissingMandatoryValues && (
    - + {t( 'presentation_page.side_menu.selection.variablebox.header.alert_no_mandatory_values', )} - +
    )}
    diff --git a/packages/pxweb2/src/app/components/ContentTop/ContentTop.tsx b/packages/pxweb2/src/app/components/ContentTop/ContentTop.tsx index 536ccbad6..72c6d9daf 100644 --- a/packages/pxweb2/src/app/components/ContentTop/ContentTop.tsx +++ b/packages/pxweb2/src/app/components/ContentTop/ContentTop.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { useState, useContext, useEffect, useRef } from 'react'; import classes from './ContentTop.module.scss'; import { - Alert, + LocalAlert, BodyShort, Breadcrumbs, Button, @@ -252,7 +252,7 @@ export function ContentTop({
    {noteMessage && (
    - {noteMessage.message} - +
    )} {isTableInformationOpen && ( diff --git a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerEdit.tsx b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerEdit.tsx index 49a84e304..eb4a41d88 100644 --- a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerEdit.tsx +++ b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerEdit.tsx @@ -1,7 +1,12 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { ActionItem, ContentBox, Variable, Alert } from '@pxweb2/pxweb2-ui'; +import { + ActionItem, + ContentBox, + Variable, + LocalAlert, +} from '@pxweb2/pxweb2-ui'; import useTableData from '../../../context/useTableData'; import classes from './DrawerEdit.module.scss'; import { PivotType } from '../../../context/PivotType'; @@ -135,9 +140,9 @@ export function DrawerEdit() { /> )}
    - + {t('common.status_messages.drawer_edit')} - +
    ); } diff --git a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerHelp.tsx b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerHelp.tsx index d5d59e806..b0b8f88b9 100644 --- a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerHelp.tsx +++ b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerHelp.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; -import { ContentBox, Alert } from '@pxweb2/pxweb2-ui'; +import { ContentBox, LocalAlert } from '@pxweb2/pxweb2-ui'; import classes from './DrawerHelp.module.scss'; export function DrawerHelp() { @@ -8,9 +8,9 @@ export function DrawerHelp() { return ( - + {t('common.status_messages.drawer_help')} - +
    ); } diff --git a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerView.tsx b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerView.tsx index 606ff7d76..f9a0810aa 100644 --- a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerView.tsx +++ b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerView.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; -import { ContentBox, Alert } from '@pxweb2/pxweb2-ui'; +import { ContentBox, LocalAlert } from '@pxweb2/pxweb2-ui'; import classes from './DrawerView.module.scss'; export function DrawerView() { @@ -8,9 +8,9 @@ export function DrawerView() { return ( - + {t('common.status_messages.drawer_view')} - + ); } diff --git a/packages/pxweb2/src/app/components/Presentation/Presentation.tsx b/packages/pxweb2/src/app/components/Presentation/Presentation.tsx index 7597bed8b..959c447e7 100644 --- a/packages/pxweb2/src/app/components/Presentation/Presentation.tsx +++ b/packages/pxweb2/src/app/components/Presentation/Presentation.tsx @@ -6,7 +6,7 @@ import isEqual from 'lodash/isEqual'; import classes from './Presentation.module.scss'; import useApp from '../../context/useApp'; import { ContentTop } from '../ContentTop/ContentTop'; -import { Table, EmptyState, PxTable, Alert } from '@pxweb2/pxweb2-ui'; +import { Table, EmptyState, PxTable, LocalAlert } from '@pxweb2/pxweb2-ui'; import useTableData from '../../context/useTableData'; import useVariables from '../../context/useVariables'; import { useDebounce } from '@uidotdev/usehooks'; @@ -221,7 +221,7 @@ export function Presentation({ width: '100%', }} > - + )} diff --git a/packages/pxweb2/src/app/components/TableInformation/TableInformation.tsx b/packages/pxweb2/src/app/components/TableInformation/TableInformation.tsx index 45348f40c..04f5d97fd 100644 --- a/packages/pxweb2/src/app/components/TableInformation/TableInformation.tsx +++ b/packages/pxweb2/src/app/components/TableInformation/TableInformation.tsx @@ -15,7 +15,7 @@ import { Tabs, Tab, TabPanel, - Alert, + LocalAlert, } from '@pxweb2/pxweb2-ui'; import { NotesTab } from './Notes/NotesTab'; @@ -108,9 +108,9 @@ export function TableInformation({ - + {t('common.status_messages.tab_definitions')} - + {tableData.data?.metadata && ( From 299a12a4f3c4ecd760867390b643ee8d91e82e92 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 10:01:16 +0100 Subject: [PATCH 09/21] Code cleanup --- .../pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx index 6fb51a370..7144f5c2d 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx @@ -55,19 +55,15 @@ export function GlobalAlert({ switch (variant) { case 'info': variantIcon = 'InformationCircleFilled'; - // variantAriaLive = 'polite'; break; case 'success': variantIcon = 'CheckMarkCircleFilled'; - // variantAriaLive = 'polite'; break; case 'warning': variantIcon = 'ExclamationMarkFilled'; - // variantAriaLive = 'assertive'; break; case 'error': variantIcon = 'XMarkCircleFilled'; - // variantAriaLive = 'assertive'; break; } let headingSize: 'small' | 'xsmall'; From 94cc324ae3cd0333ce3e994af3cc8973a1c31a12 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 10:17:51 +0100 Subject: [PATCH 10/21] Use LocalAlert instead of Alert --- .../NavigationDrawer/Drawers/DrawerEdit.spec.tsx | 2 +- .../NavigationDrawer/Drawers/DrawerSave.tsx | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerEdit.spec.tsx b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerEdit.spec.tsx index 27345badf..306bf97b9 100644 --- a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerEdit.spec.tsx +++ b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerEdit.spec.tsx @@ -62,7 +62,7 @@ vi.mock('@pxweb2/pxweb2-ui', () => ({ {label} ), - Alert: ({ children }: { children: React.ReactNode }) => ( + LocalAlert: ({ children }: { children: React.ReactNode }) => (
    {children}
    ), })); diff --git a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerSave.tsx b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerSave.tsx index 1f88a5e1d..e32eb8067 100644 --- a/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerSave.tsx +++ b/packages/pxweb2/src/app/components/NavigationDrawer/Drawers/DrawerSave.tsx @@ -13,7 +13,7 @@ import { Radio, RadioOption, VartypeEnum, - Alert, + LocalAlert, } from '@pxweb2/pxweb2-ui'; import { ApiError, @@ -500,9 +500,12 @@ export function DrawerSave({ tableId }: DrawerSaveProps) { ))} - + {t('common.status_messages.drawer_save_file')} - +
    @@ -549,9 +552,9 @@ export function DrawerSave({ tableId }: DrawerSaveProps) {
    - + {t('common.status_messages.drawer_save_api')} - + ); From a004dbd4a04d8b1e6e24e7fd13ba0416e7c2e755 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 10:27:29 +0100 Subject: [PATCH 11/21] Remove Alert component and related files --- packages/pxweb2-ui/src/index.ts | 1 - .../lib/components/Alert/Alert.module.scss | 276 ------------------ .../src/lib/components/Alert/Alert.spec.tsx | 32 -- .../lib/components/Alert/Alert.stories.tsx | 204 ------------- .../src/lib/components/Alert/Alert.tsx | 252 ---------------- 5 files changed, 765 deletions(-) delete mode 100644 packages/pxweb2-ui/src/lib/components/Alert/Alert.module.scss delete mode 100644 packages/pxweb2-ui/src/lib/components/Alert/Alert.spec.tsx delete mode 100644 packages/pxweb2-ui/src/lib/components/Alert/Alert.stories.tsx delete mode 100644 packages/pxweb2-ui/src/lib/components/Alert/Alert.tsx diff --git a/packages/pxweb2-ui/src/index.ts b/packages/pxweb2-ui/src/index.ts index 39769ff4d..9865f54df 100644 --- a/packages/pxweb2-ui/src/index.ts +++ b/packages/pxweb2-ui/src/index.ts @@ -1,7 +1,6 @@ export * from './../style-dictionary/dist/js/css-variables'; export * from './../style-dictionary/dist/js/fixed-variables'; export * from './lib/components/ActionItem/ActionItem'; -export * from './lib/components/Alert/Alert'; export * from './lib/components/BottomSheet/BottomSheet'; export * from './lib/components/Breadcrumbs/Breadcrumbs'; export * from './lib/components/Button/Button'; diff --git a/packages/pxweb2-ui/src/lib/components/Alert/Alert.module.scss b/packages/pxweb2-ui/src/lib/components/Alert/Alert.module.scss deleted file mode 100644 index 1077f6f47..000000000 --- a/packages/pxweb2-ui/src/lib/components/Alert/Alert.module.scss +++ /dev/null @@ -1,276 +0,0 @@ -@use '../../../../style-dictionary/dist/scss/fixed-variables.scss' as fixvar; -@use '../../text-styles.scss'; - -.alert-medium { - display: flex; - padding: fixvar.$spacing-5 fixvar.$spacing-6; - align-items: flex-start; - gap: fixvar.$spacing-3; - border-radius: var(--px-border-radius-large); -} -.alert-small { - display: flex; - padding: fixvar.$spacing-3; - align-items: flex-start; - gap: fixvar.$spacing-2; - border-radius: var(--px-border-radius-medium); -} - -.info { - background: var(--px-color-surface-info-moderate); -} - -.info-clickable { - &:hover { - box-shadow: inset 0px 0px 0px 2px var(--px-color-border-info); - h2 { - @extend .heading-underline; - } - } - &:hover .alert-arrow-wrapper { - padding-inline-start: 8px; - transition: 100ms; - } - &:focus-visible { - outline: 3px solid var(--px-color-border-focus-outline); - outline-offset: 3px; - box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); - } -} - -.success { - background: var(--px-color-surface-success-moderate); -} -.success-clickable { - &:hover { - box-shadow: inset 0px 0px 0px 2px var(--px-color-border-success); - h2 { - @extend .heading-underline; - } - } - &:hover .alert-arrow-wrapper { - padding-inline-start: 8px; - transition: 100ms; - } - &:focus-visible { - outline: 3px solid var(--px-color-border-focus-outline); - outline-offset: 3px; - box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); - } -} -.warning { - background: var(--px-color-surface-warning-moderate); -} -.warning-clickable { - &:hover { - box-shadow: inset 0px 0px 0px 2px var(--px-color-border-warning); - h2 { - @extend .heading-underline; - } - } - &:hover .alert-arrow-wrapper { - padding-inline-start: 8px; - transition: 100ms; - } - &:focus-visible { - outline: 3px solid var(--px-color-border-focus-outline); - outline-offset: 3px; - box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); - } -} -.error { - background: var(--px-color-surface-error-moderate); -} -.error-clickable { - &:hover { - box-shadow: inset 0px 0px 0px 2px var(--px-color-border-error); - h2 { - @extend .heading-underline; - } - } - &:hover .alert-arrow-wrapper { - padding-inline-start: 8px; - transition: 100ms; - } - &:focus-visible { - outline: 3px solid var(--px-color-border-focus-outline); - outline-offset: 3px; - box-shadow: inset 0 0 0 3px var(--px-color-border-focus-boxshadow); - } -} - -.alert-section-left-medium { - display: flex; - height: fixvar.$spacing-6; - margin-top: 0.25rem; - flex-direction: column; - justify-content: center; - align-items: flex-start; - flex-shrink: 0; -} - -.alert-section-left-small { - display: flex; - height: fixvar.$spacing-6; - padding-top: fixvar.$spacing-1; - flex-direction: column; - align-items: flex-start; - flex-shrink: 0; -} - -.alert-section-middle-medium { - display: flex; - flex-direction: column; - padding-top: 2px; - row-gap: fixvar.$spacing-2; -} -.alert-section-middle-medium:has(ol) { - row-gap: fixvar.$spacing-4; -} -.alert-section-middle-medium:has(ul) { - row-gap: fixvar.$spacing-4; -} - -.alert-section-middle-small { - display: flex; - flex-direction: column; - padding-top: 2px; - row-gap: fixvar.$spacing-1; -} - -.alert-heading { - display: flex; - flex-direction: column; - align-items: flex-start; - padding-top: 1px; - flex: 1 0 0; -} - -.alert-body-small { - display: flex; - align-self: stretch; - padding-top: 2px; - overflow-wrap: anywhere; - word-break: break-word; - hyphens: auto; -} - -.alert-body-medium { - display: flex; - align-self: stretch; - overflow-wrap: anywhere; - word-break: break-word; - hyphens: auto; -} - -.truncate-text-small { - max-height: fixvar.$spacing-12; - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 2; - line-clamp: 2; - -webkit-box-orient: vertical; - text-overflow: ellipsis; -} - -.truncate-text-medium { - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 2; - line-clamp: 2; - -webkit-box-orient: vertical; - text-overflow: ellipsis; -} - -.alert-section-right { - display: flex; - align-self: center; -} - -.alert-section-right-close-medium { - display: flex; - align-self: flex-start; - margin-inline-start: auto; - height: 32px; - align-items: center; - gap: 6px; -} - -.alert-section-right-close-small { - display: flex; - align-self: flex-start; - margin-inline-start: auto; - height: 32px; - align-items: center; - gap: 6px; -} - -.alert-arrow { - display: flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - border: none; - background-color: transparent; - cursor: pointer; -} - -.alert-section-right-continue { - display: flex; - margin-inline-start: auto; - width: 44px; - align-self: center; - align-items: center; -} -.alert-arrow-wrapper { - display: flex; - width: 44px; - justify-content: center; - flex-shrink: 0; - align-self: center; - margin-inline-start: auto; -} - -.alert-xmark { - display: flex; - justify-content: flex-end; - align-items: flex-start; - align-self: stretch; - margin-inline-start: auto; -} - -.alert-xmark-wrapper { - display: flex; - justify-content: center; - align-items: center; - gap: var(--spacing-2); -} - -.alert-icon-info { - --px-icon-color: var(--px-color-icon-info); - flex-shrink: 0; -} - -.alert-icon-success { - --px-icon-color: var(--px-color-icon-success); -} - -.alert-icon-warning { - --px-icon-color: var(--px-color-icon-warning); -} - -.alert-icon-error { - --px-icon-color: var(--px-color-icon-error); -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} diff --git a/packages/pxweb2-ui/src/lib/components/Alert/Alert.spec.tsx b/packages/pxweb2-ui/src/lib/components/Alert/Alert.spec.tsx deleted file mode 100644 index 37cf2e8e0..000000000 --- a/packages/pxweb2-ui/src/lib/components/Alert/Alert.spec.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { render } from '@testing-library/react'; -import '@testing-library/jest-dom/vitest'; - -import Alert, { AlertProps } from './Alert'; - -describe('Alert', () => { - it('should render successfully', () => { - const { baseElement } = render(); - expect(baseElement).toBeTruthy(); - }); -}); -it.each([ - ['polite', 'info'], - ['polite', 'success'], - ['assertive', 'warning'], - ['assertive', 'error'], -])( - 'should set aria-live="%s" for variant "%s"', - (expectedAriaLive, variant) => { - const headingText = `Test heading for ${variant}`; - const { getByText } = render( - , - ); - const heading = getByText(headingText); - const headingContainer = heading.closest('div[aria-live]'); - expect(headingContainer).not.toBeNull(); - expect(headingContainer).toHaveAttribute('aria-live', expectedAriaLive); - }, -); diff --git a/packages/pxweb2-ui/src/lib/components/Alert/Alert.stories.tsx b/packages/pxweb2-ui/src/lib/components/Alert/Alert.stories.tsx deleted file mode 100644 index 7136893c6..000000000 --- a/packages/pxweb2-ui/src/lib/components/Alert/Alert.stories.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import type { Meta, StoryFn } from '@storybook/react-vite'; - -import { Alert } from './Alert'; -import { Link } from '../Link/Link'; -import List from '../List/List'; -import ListItem from '../List/ListItem'; - -const meta: Meta = { - component: Alert, - title: 'Components/Alert', - parameters: { - layout: 'padded', - }, -}; -export default meta; - -export const Variant = { - args: { - variant: 'info', - heading: 'Vi beklager', - children: - 'Statistikkbanken er for øyeblikket nede. Vi jobber med feilen og forventer å være opp igjen i løpet av ei uke eller 2, beklager så mye', - onClick: () => { - alert( - 'Statistikkbanken er for øyeblikket nede. Vi jobber med feilen og forventer å være opp igjen i løpet av ei uke eller 2, beklager så mye ', - ); - }, - }, -}; - -export const WithLink: StoryFn = () => { - return ( - <> -
    -
    - - - SSB - - -
    - - ); -}; - -export const WithTextAndLink: StoryFn = () => { - return ( - <> -
    -
    - - Det finnes mer metadata om emnet. Dette kan du lese mer om her:{' '} - - SSB - - -
    - - ); -}; - -export const WithOLList: StoryFn = () => { - return ( - - - - {' '} - - Se liste over endringene i de regionale inndelingene - - - {/* note b - note c */} - - - ); -}; -export const WithULList: StoryFn = () => { - return ( - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad - minim veniam, quis nostrud exercitation ullamco laboris nisi ut - aliquip ex ea commodo consequat. Duis aute irure dolor in - reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in - culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum - dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna aliqua - - - Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris - nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in - reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in - culpa qui officia deserunt mollit anim id est laborum. - -
  • Note c
  • -
    -
    - ); -}; - -export const WithULListClickable: StoryFn = () => { - return ( - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad - minim veniam, quis nostrud exercitation ullamco laboris nisi ut - aliquip ex ea commodo consequat. Duis aute irure dolor in - reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in - culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum - dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna aliqua - - - Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris - nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in - reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in - culpa qui officia deserunt mollit anim id est laborum. - -
  • Note c
  • -
    -
    - ); -}; - -export const WithListgroupClickable: StoryFn = () => { - return ( - - - - - Se liste over endringene i de regionale inndelingene - - - Tidligere: Vestbredden/Gazastripen (2001-2012) - Vi later som denne har to - {' '} - - - Variable note 2 very very long and maybe even longer. - - Variable note 3. - - - Ble kalt Hviterussland fram til 2022. - - - - - Ble kalt Swaziland før 2018. - Ble kalt Swaziland før 2018. - - - - - Tidligere: Vestbredden/Gazastripen (2001-2012) - Vi later som denne har to fotnoter - Vi later som denne har tre fotnoter - Vi later som denne har fire fotnoter - - - - - ); -}; - -export const Test: StoryFn = () => { - return ( - - - - Gå til SSB - - - - Fotnote 11 - Fotnote 12 - Fotnote 13 - Fotnote 14 - - - Fotnote 21 - Fotnote 22 - Fotnote 23 - Fotnote 24 - - - - - ); -}; diff --git a/packages/pxweb2-ui/src/lib/components/Alert/Alert.tsx b/packages/pxweb2-ui/src/lib/components/Alert/Alert.tsx deleted file mode 100644 index 799bb76e8..000000000 --- a/packages/pxweb2-ui/src/lib/components/Alert/Alert.tsx +++ /dev/null @@ -1,252 +0,0 @@ -import React, { useState } from 'react'; -import cl from 'clsx'; - -import { useTranslation } from 'react-i18next'; -import classes from './Alert.module.scss'; -import BodyLong from '../Typography/BodyLong/BodyLong'; -import Heading from '../Typography/Heading/Heading'; -import { Icon, IconProps } from '../Icon/Icon'; -import Button from '../Button/Button'; -import BodyShort from '../Typography/BodyShort/BodyShort'; -import List, { ListProps } from '../List/List'; -import { getIconDirection } from '../../util/util'; - -export interface AlertProps { - readonly size?: 'small' | 'medium'; - readonly variant: 'info' | 'success' | 'warning' | 'error'; - readonly clickable?: boolean; - readonly closeButton?: boolean; - readonly heading?: string; - readonly headingLevel?: '1' | '2' | '3' | '4' | '5' | '6'; - readonly onClick?: () => void; - readonly onDismissed?: () => void; - readonly languageDirection?: 'ltr' | 'rtl'; - readonly className?: string; - readonly children?: string | React.ReactNode; - readonly alertAriaLabel?: string; - readonly ariaHasPopup?: - | 'false' - | 'true' - | 'menu' - | 'listbox' - | 'tree' - | 'grid' - | 'dialog'; - readonly role?: React.AriaRole; - ref?: React.Ref; - id?: string; -} - -export function Alert({ - size = 'medium', - variant = 'info', - clickable = false, - closeButton = false, - heading = '', - headingLevel = '2', - onClick, - onDismissed, - languageDirection = 'ltr', - className = '', - children, - alertAriaLabel, - ariaHasPopup = 'false', - role, - ref, - id, -}: Readonly) { - const cssClasses = className.length > 0 ? ' ' + className : ''; - const { t } = useTranslation(); - const [isVisible, setIsVisible] = useState(true); - const HandleClose = () => { - setIsVisible(false); - onDismissed?.(); - }; - if (!isVisible) { - return null; - } - const handleKeyDown = (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { - event.preventDefault(); - onClick && onClick(); - } - }; - const hasheading = Boolean(heading); - const iconArrow = getIconDirection( - languageDirection, - 'ArrowRight', - 'ArrowLeft', - ); - const iconClose = 'XMark'; - let variantIcon: IconProps['iconName']; - let variantAriaLive: 'polite' | 'assertive'; - switch (variant) { - case 'info': - variantIcon = 'InformationCircleFilled'; - variantAriaLive = 'polite'; - break; - case 'success': - variantIcon = 'CheckMarkCircleFilled'; - variantAriaLive = 'polite'; - break; - case 'warning': - variantIcon = 'ExclamationMarkFilled'; - variantAriaLive = 'assertive'; - break; - case 'error': - variantIcon = 'XMarkCircleFilled'; - variantAriaLive = 'assertive'; - break; - } - let headingSize: 'small' | 'xsmall'; - const bodySize = 'medium'; - - switch (size) { - case 'small': - headingSize = 'xsmall'; - break; - case 'medium': - headingSize = 'small'; - break; - default: - headingSize = 'small'; - } - if (clickable) { - closeButton = false; - } - - const childIsList = (node: React.ReactNode): boolean => { - if (React.isValidElement(node)) { - if (node.type === List) { - return true; - } - } - return false; - }; - - const extractTextFromChildren = (children: React.ReactNode): string => { - let textContent = ''; - - React.Children.forEach(children, (child) => { - if (React.isValidElement(child)) { - // If the child is a valid React element, check its children recursively - if (React.isValidElement(child) && child.type === List) { - textContent += ' ' + (child.props as ListProps)?.subHeading + ': '; - } - if ( - typeof child.props === 'object' && - child.props !== null && - 'children' in child.props - ) { - textContent += extractTextFromChildren( - child.props.children as React.ReactNode, - ); - } - } else if (typeof child === 'string' || typeof child === 'number') { - // If the child is a string or number, add it to the text content - textContent += ' ' + child.toString(); - } - }); - - return textContent; - }; - - if (childIsList(children) && clickable) { - let extractedText = ''; - if (React.isValidElement(children) && children.type === List) { - const listProps = children.props as ListProps; - extractedText = extractTextFromChildren(listProps.children); - } - children = extractedText; - } - - return ( -
    -
    - {!hasheading && ( - - {t(`common.alert.${variant}`)} - - )} - -
    -
    - {hasheading && ( -
    - - {t(`common.alert.${variant}`)} - - - {heading} - -
    - )} -
    - {size === 'small' ? ( - {children} - ) : ( - - {children} - - )} -
    -
    -
    - {closeButton && ( -
    -
    - )} - {clickable && ( -
    - -
    - )} -
    -
    - ); -} - -export default Alert; From a7ac842608d945670f71aad0dbca36d2a1d8f0d0 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 10:53:05 +0100 Subject: [PATCH 12/21] Remove unused code and comments from GlobalAlert and LocalAlert styles --- .../pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx | 1 - .../src/lib/components/LocalAlert/LocalAlert.module.scss | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx index 7144f5c2d..66866caee 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.tsx @@ -51,7 +51,6 @@ export function GlobalAlert({ const hasheading = Boolean(heading); const iconClose = 'XMark'; let variantIcon: IconProps['iconName']; - // let variantAriaLive: 'polite' | 'assertive'; switch (variant) { case 'info': variantIcon = 'InformationCircleFilled'; diff --git a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.module.scss index c251460a9..78acaf571 100644 --- a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.module.scss +++ b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.module.scss @@ -135,7 +135,6 @@ display: flex; flex-direction: column; padding-top: 2px; - // row-gap: fixvar.$spacing-1; } .alert-heading { From ffb761f08660d4235927e7357cb03bb8b303caa1 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Tue, 16 Dec 2025 11:17:50 +0100 Subject: [PATCH 13/21] Remove unused List-related code from LocalAlert component --- .../lib/components/LocalAlert/LocalAlert.tsx | 47 +------------------ 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx index e8deb9d12..46d1e7b96 100644 --- a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx +++ b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx @@ -8,7 +8,7 @@ import Heading from '../Typography/Heading/Heading'; import { Icon, IconProps } from '../Icon/Icon'; import Button from '../Button/Button'; import BodyShort from '../Typography/BodyShort/BodyShort'; -import List, { ListProps } from '../List/List'; +// import List, { ListProps } from '../List/List'; import { getIconDirection } from '../../util/util'; export interface LocalAlertProps { @@ -115,51 +115,6 @@ export function LocalAlert({ closeButton = false; } - const childIsList = (node: React.ReactNode): boolean => { - if (React.isValidElement(node)) { - if (node.type === List) { - return true; - } - } - return false; - }; - - const extractTextFromChildren = (children: React.ReactNode): string => { - let textContent = ''; - - React.Children.forEach(children, (child) => { - if (React.isValidElement(child)) { - // If the child is a valid React element, check its children recursively - if (React.isValidElement(child) && child.type === List) { - textContent += ' ' + (child.props as ListProps)?.subHeading + ': '; - } - if ( - typeof child.props === 'object' && - child.props !== null && - 'children' in child.props - ) { - textContent += extractTextFromChildren( - child.props.children as React.ReactNode, - ); - } - } else if (typeof child === 'string' || typeof child === 'number') { - // If the child is a string or number, add it to the text content - textContent += ' ' + child.toString(); - } - }); - - return textContent; - }; - - if (childIsList(children) && clickable) { - let extractedText = ''; - if (React.isValidElement(children) && children.type === List) { - const listProps = children.props as ListProps; - extractedText = extractTextFromChildren(listProps.children); - } - children = extractedText; - } - return (
    Date: Tue, 16 Dec 2025 14:05:08 +0100 Subject: [PATCH 14/21] Remove unused List import from LocalAlert component --- packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx index 46d1e7b96..d11a0b847 100644 --- a/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx +++ b/packages/pxweb2-ui/src/lib/components/LocalAlert/LocalAlert.tsx @@ -8,7 +8,6 @@ import Heading from '../Typography/Heading/Heading'; import { Icon, IconProps } from '../Icon/Icon'; import Button from '../Button/Button'; import BodyShort from '../Typography/BodyShort/BodyShort'; -// import List, { ListProps } from '../List/List'; import { getIconDirection } from '../../util/util'; export interface LocalAlertProps { From 9d8fb77addd45aed369b4a6ae92aa7444211f5bc Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Wed, 17 Dec 2025 08:54:57 +0100 Subject: [PATCH 15/21] Fix padding in alert-medium and comment out unused container styles Ment for temporary solution --- .../GlobalAlert/GlobalAlert.module.scss | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss index bbfde3217..af37baad1 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss @@ -4,7 +4,7 @@ .alert-medium { display: flex; - padding: fixvar.$spacing-5 0px; + padding: fixvar.$spacing-5 fixvar.$spacing-5; align-items: flex-start; gap: fixvar.$spacing-3; } @@ -17,33 +17,33 @@ .container { display: block; - margin-left: auto; - margin-right: auto; + // margin-left: auto; + // margin-right: auto; - @media #{breakpoint.$xsmall} { - margin-left: 1rem; - margin-right: 1rem; - } + // @media #{breakpoint.$xsmall} { + // margin-left: 1rem; + // margin-right: 1rem; + // } - @media #{breakpoint.$small} { - max-width: 508px; - } + // @media #{breakpoint.$small} { + // max-width: 508px; + // } - @media #{breakpoint.$medium} { - max-width: 688px; - } + // @media #{breakpoint.$medium} { + // max-width: 688px; + // } - @media #{breakpoint.$large} { - max-width: 928px; - } + // @media #{breakpoint.$large} { + // max-width: 928px; + // } - @media #{breakpoint.$xlarge} { - max-width: 1108px; - } + // @media #{breakpoint.$xlarge} { + // max-width: 1108px; + // } - @media #{breakpoint.$xxlarge} { - max-width: 1288px; - } + // @media #{breakpoint.$xxlarge} { + // max-width: 1288px; + // } } .info { From 5740888c08e2c1660ec4cdd4b69d5e4e170e7a7a Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Wed, 17 Dec 2025 10:08:04 +0100 Subject: [PATCH 16/21] Fix padding in alert-medium and update responsive styles for container --- .../GlobalAlert/GlobalAlert.module.scss | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss index af37baad1..f5a9c85ae 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss @@ -4,7 +4,7 @@ .alert-medium { display: flex; - padding: fixvar.$spacing-5 fixvar.$spacing-5; + padding: fixvar.$spacing-5 0px; align-items: flex-start; gap: fixvar.$spacing-3; } @@ -20,30 +20,27 @@ // margin-left: auto; // margin-right: auto; - // @media #{breakpoint.$xsmall} { - // margin-left: 1rem; - // margin-right: 1rem; - // } - - // @media #{breakpoint.$small} { - // max-width: 508px; - // } - - // @media #{breakpoint.$medium} { - // max-width: 688px; - // } - - // @media #{breakpoint.$large} { - // max-width: 928px; - // } - - // @media #{breakpoint.$xlarge} { - // max-width: 1108px; - // } - - // @media #{breakpoint.$xxlarge} { - // max-width: 1288px; - // } + @media #{breakpoint.$xsmall} { + padding: 0 fixvar.$spacing-4; + } + @media #{breakpoint.$small} { + padding: 0 fixvar.$spacing-6; + } + @media #{breakpoint.$medium} { + padding: 0 fixvar.$spacing-6; + } + + @media #{breakpoint.$large} { + padding: 0 fixvar.$spacing-6; + } + + @media #{breakpoint.$xlarge} { + padding: 0 fixvar.$spacing-6; + } + + @media #{breakpoint.$xxlarge} { + padding: 0 fixvar.$spacing-6; + } } .info { From e6a6d002a643532797b5842bf558ab3f4c2089aa Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Wed, 17 Dec 2025 10:26:08 +0100 Subject: [PATCH 17/21] Remove commented-out margin styles from container in GlobalAlert --- .../src/lib/components/GlobalAlert/GlobalAlert.module.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss index f5a9c85ae..7949c0fa0 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss @@ -17,8 +17,6 @@ .container { display: block; - // margin-left: auto; - // margin-right: auto; @media #{breakpoint.$xsmall} { padding: 0 fixvar.$spacing-4; From fb2dbfbbdb9f34d73a3f1fe2e4cdf55789d6888f Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Wed, 17 Dec 2025 11:48:50 +0100 Subject: [PATCH 18/21] Remove unnecessary blank lines in GlobalAlert styles --- .../src/lib/components/GlobalAlert/GlobalAlert.module.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss index 7949c0fa0..2d42fe484 100644 --- a/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss +++ b/packages/pxweb2-ui/src/lib/components/GlobalAlert/GlobalAlert.module.scss @@ -17,7 +17,6 @@ .container { display: block; - @media #{breakpoint.$xsmall} { padding: 0 fixvar.$spacing-4; } @@ -27,15 +26,12 @@ @media #{breakpoint.$medium} { padding: 0 fixvar.$spacing-6; } - @media #{breakpoint.$large} { padding: 0 fixvar.$spacing-6; } - @media #{breakpoint.$xlarge} { padding: 0 fixvar.$spacing-6; } - @media #{breakpoint.$xxlarge} { padding: 0 fixvar.$spacing-6; } @@ -44,7 +40,6 @@ .info { background: var(--px-color-surface-info-moderate); } - .success { background: var(--px-color-surface-success-moderate); } From 7d4443e6a8e5925c216b002e39c404325b27fe13 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Wed, 17 Dec 2025 11:50:37 +0100 Subject: [PATCH 19/21] Remove MAINTAINERS.md and update .gitignore and Snyk rules documentation --- .gitignore | 5 +- MAINTAINERS.md | 128 ------------------------------------------------- 2 files changed, 1 insertion(+), 132 deletions(-) delete mode 100644 MAINTAINERS.md diff --git a/.gitignore b/.gitignore index a7638499f..945e19a2a 100644 --- a/.gitignore +++ b/.gitignore @@ -51,7 +51,4 @@ vite.config.*.timestamp* *storybook.log # AI -.qodo - -# Snyk Security Extension - AI Rules (auto-generated) -.github/instructions/snyk_rules.instructions.md +.qodo \ No newline at end of file diff --git a/MAINTAINERS.md b/MAINTAINERS.md deleted file mode 100644 index 3421fd1f6..000000000 --- a/MAINTAINERS.md +++ /dev/null @@ -1,128 +0,0 @@ -# Maintainers Guide - -This guide is intended for maintainers - those who are maintaining PxWeb by providing or reviewing pull requests. - -## Getting Started - -1. Familiarize yourself with the project structure and our [CONTRIBUTING.md](./CONTRIBUTING.md) guide as well as this guide. -2. Setup the project locally and make sure that you can run PxWeb. - -## Development Tools we use - -We recommend the following tools for our development environment: - -- **Code Editor**: [Visual Studio Code (VS Code)](https://code.visualstudio.com/) is our preferred code editor. It provides a rich set of features, extensions, and integrations that enhance productivity and make development easier. - -- **Build & Development Environment**: [Vite](https://vitejs.dev/) is our preferred build and development environment. It is a fast and lightweight build tool that optimizes the development experience by providing instant server startup, hot module replacement, and efficient bundling. - -- **Accessibility**: [Axe](https://www.deque.com/axe/) is our preferred tool to help us comply with wcag - -By using these tools, we ensure a streamlined and efficient development process, enabling us to deliver high-quality code. - -## Technical debt - -An overview of the current technical debt in the project: - -### Long running tasks - -1. Updating the packages used in the project. This includes both Dependabot PRs and other outdated packages. - - For security reasons, we want to wait atleast 9 days before updating packages. - Use NPM '--before' or npm-check-updates '--cooldown' flags. - E.g: npm-check-updates --format group --deep --cooldown 9 -iu -2. Fix [SonarQube issues/codesmells](https://sonarcloud.io/project/overview?id=PxTools_PxWeb2) -3. Fix warnings when running `npm run lint` - -### Onetime tasks - -1. The package we use to generate the api-client is no longer maintained, and needs to be updated. It recommends a fork that is activly maintained. -2. We should split up the Selection.tsx component a bit. That would make it easier to write unit tests for the code. - -## Git Branching Strategy - -We use a simple and effective Git branching strategy to keep our project organized and maintainable. - -1. **Main Branch**: The `main` branch is the primary branch where the source code of HEAD always reflects a "production-ready" state. - -2. **Feature Branches**: For new features and non-emergency bug fixes, we create a new branch off of `main`. Name your branch descriptively, indicating the purpose of your changes. Branch names should be lowercased and should be prefixed with `feature/`. - - Example: `feature/PXWEB2-1-new-login-screen` - -3. **Fix Branches**: For bug fixes, we create a new branch off of `main`. Name your branch descriptively, indicating the purpose of your changes. Branch names should be lowercased and prefixed with `fix/`. - - Example: `fix/PXWEB2-1-login-screen-bug` - -4. **Patch Branches**: For minor changes and patches, we create a new branch off of `main`. Name your branch descriptively, indicating the purpose of your changes. Branch names should be lowercased and prefixed with `patch/`. - - Example: `patch/PXWEB2-1-update-login-screen-docs` - -5. **Pull Requests**: After making your changes in these branches, merge them back into `main` via a pull request. The pull request should be reviewed and tested before it is merged. - -> Make sure that the JIRA id is included in the branch name and that this should be in capital letters. -> -> We prefer hyphens instead of underscores for word separator in branch names. - -This strategy ensures that our `main` branch always has "production-ready" code that has been reviewed and tested. - -## Create Pull Requests - -To get started with contributing code, please follow these steps: - -1. Clone the repository to your local machine. -2. Create a new branch from the `main` branch. Name your branch descriptively, indicating the purpose of your changes. -3. Make your changes and ensure that the code follows the project's coding conventions. -4. Test your changes thoroughly to ensure they work as expected. -5. Commit your changes with a clear and descriptive commit message. -6. Push your changes. -7. Open a pull request against the `main` branch and make sure that it has: - - A descriptive title. - - A clear and concise description of the changes, including the purpose and benefits of the change. - - A reference to the related issue(s) or ticket(s). - - Screenshots or gifs if the changes include UI/UX changes. - - New tests that cover the added features or bug fixes. - - Documentation updates if necessary. - - Passed all the continuous integration checks (like build, linting and tests). - - > Your PR will have automatic deploys to Cloudflare Pages that can be used by the reviewers - -8. Reviewers will be automatically assigned from [CODEOWNERS](CODEOWNERS) to your PR and once approved by at least one reviewer you can continue with one of the following options - - Create a merge commit - - Squash and merge (recomended for most/smaller changes) - - Rebase and merge - -> [!IMPORTANT] -> We use [Automatically generated release notes](https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes) -> so a descriptive PR title is essential for the release notes. - -## Reviewing Pull Requests - -1. Ensure that the PR is related to a filed issue. -2. Check that the code follows the project's coding standards. -3. Test the code locally to ensure it works as expected. -4. Review the tests included with the PR to ensure they cover the new functionality or bug fix. - -## Communicating - -1. Be respectful and kind to the contributors. -2. Provide clear, constructive feedback on PRs. -3. Promote a collaborative and learning environment. - -Remember, this project and everyone participating in it is governed by the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. - -## 3rd Party Components - -While developing for this project, we aim to minimize the number of dependencies. This is to keep the project lightweight, maintainable, and to reduce potential security risks. - -Before adding a new 3rd party component or library, please consider the following: - -1. Is the component necessary? Can the functionality be achieved without it? -2. Is the component actively maintained? An unmaintained component can pose a security risk and may lack support. -3. Does the component have a large community? Larger communities often mean more resources for troubleshooting and learning. -4. Is the component's license compatible with our project? -5. Consult with another maintainer if it is the right component for the job. -6. Consult [snyk Advisor](https://snyk.io/advisor/) to get a summary for the component. - -For existing third-party component dependencies, we will utilize Dependabot to help us stay up to date. Dependabot is an automated dependency management tool that regularly checks for updates to our dependencies and creates pull requests to update them. This helps us ensure that we are using the latest versions of our dependencies, which can include important bug fixes, security patches, and new features. - -We are committed to continuously updating our dependencies to ensure that we are using the latest versions. However, in some cases, we may encounter conflicts with NX plugins that prevent us from updating to the latest versions. In such situations, we carefully evaluate the impact and risks before deciding on the appropriate course of action. - -In the event of a security vulnerability being discovered in any of our dependencies, we will prioritize attending to it urgently. Our team will take immediate action to mitigate the risk and apply necessary patches or updates. From 26bddbe22a45db74cb90445a2c226d0b6ecaecb1 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Wed, 17 Dec 2025 11:51:22 +0100 Subject: [PATCH 20/21] Add Snyk security rules and best practices documentation --- .github/instructions/snyk_rules.instructions.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/instructions/snyk_rules.instructions.md diff --git a/.github/instructions/snyk_rules.instructions.md b/.github/instructions/snyk_rules.instructions.md new file mode 100644 index 000000000..eef063410 --- /dev/null +++ b/.github/instructions/snyk_rules.instructions.md @@ -0,0 +1,14 @@ +--- +alwaysApply: true +always_on: true +trigger: always_on +applyTo: "**" +description: Snyk Security At Inception +--- + +# Project security best practices + +- Always run snyk_code_scan tool for new first party code that is generated in a Snyk-supported language. +- If any security issues are found based on newly introduced or modified code or dependencies, attempt to fix the issues using the results context from Snyk. +- Rescan the code after fixing the issues to ensure that the issues were fixed and that there are no newly introduced issues. +- Repeat this process until no new issues are found. From 7a420577abec09abde79728f489e1297b2a5daa0 Mon Sep 17 00:00:00 2001 From: PerIngeVaaje Date: Wed, 17 Dec 2025 12:04:58 +0100 Subject: [PATCH 21/21] Remove Snyk rules instructions file --- .github/instructions/snyk_rules.instructions.md | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .github/instructions/snyk_rules.instructions.md diff --git a/.github/instructions/snyk_rules.instructions.md b/.github/instructions/snyk_rules.instructions.md deleted file mode 100644 index eef063410..000000000 --- a/.github/instructions/snyk_rules.instructions.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -alwaysApply: true -always_on: true -trigger: always_on -applyTo: "**" -description: Snyk Security At Inception ---- - -# Project security best practices - -- Always run snyk_code_scan tool for new first party code that is generated in a Snyk-supported language. -- If any security issues are found based on newly introduced or modified code or dependencies, attempt to fix the issues using the results context from Snyk. -- Rescan the code after fixing the issues to ensure that the issues were fixed and that there are no newly introduced issues. -- Repeat this process until no new issues are found.