@oslokommune/punkt-react 13.11.0 → 13.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-react",
3
- "version": "13.11.0",
3
+ "version": "13.13.0",
4
4
  "description": "React komponentbibliotek til Punkt, et designsystem laget av Oslo Origo",
5
5
  "homepage": "https://punkt.oslo.kommune.no",
6
6
  "author": "Team Designsystem, Oslo Origo",
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@lit-labs/ssr-dom-shim": "^1.2.1",
41
41
  "@lit/react": "^1.0.7",
42
- "@oslokommune/punkt-elements": "^13.11.0",
42
+ "@oslokommune/punkt-elements": "^13.13.0",
43
43
  "classnames": "^2.5.1",
44
44
  "prettier": "^3.3.3",
45
45
  "react-element-to-jsx-string": "^15.0.0",
@@ -49,7 +49,7 @@
49
49
  "devDependencies": {
50
50
  "@babel/plugin-transform-private-property-in-object": "^7.25.9",
51
51
  "@oslokommune/punkt-assets": "^13.11.0",
52
- "@oslokommune/punkt-css": "^13.11.0",
52
+ "@oslokommune/punkt-css": "^13.13.0",
53
53
  "@testing-library/jest-dom": "^6.5.0",
54
54
  "@testing-library/react": "^16.0.1",
55
55
  "@testing-library/user-event": "^14.5.2",
@@ -106,5 +106,5 @@
106
106
  "url": "https://github.com/oslokommune/punkt/issues"
107
107
  },
108
108
  "license": "MIT",
109
- "gitHead": "f25b08c9f4382250d0097d22bc8541cfd459fe73"
109
+ "gitHead": "dfda73999cbdf688f04f4fce0240779bd4fed111"
110
110
  }
@@ -3,7 +3,6 @@ import '@testing-library/jest-dom'
3
3
 
4
4
  import { render } from '@testing-library/react'
5
5
  import { axe, toHaveNoViolations } from 'jest-axe'
6
- import * as React from 'react'
7
6
 
8
7
  import { PktLinkCard } from './LinkCard'
9
8
 
@@ -14,20 +13,17 @@ describe('PktLinkCard', () => {
14
13
  describe('basic rendering', () => {
15
14
  it('renders correctly with only default props', async () => {
16
15
  const { getByText } = render(<PktLinkCard>LinkCard Content</PktLinkCard>)
17
- await window.customElements.whenDefined('pkt-linkcard')
18
16
  expect(getByText('LinkCard Content')).toBeInTheDocument()
19
17
  })
20
18
 
21
19
  it('renders with title', async () => {
22
20
  render(<PktLinkCard title="Link title">Link content</PktLinkCard>)
23
- await window.customElements.whenDefined('pkt-linkcard')
24
21
  const iconElement = document.querySelector('.pkt-linkcard__title')
25
22
  expect(iconElement).toBeInTheDocument()
26
23
  })
27
24
 
28
25
  it('renders without title', async () => {
29
26
  render(<PktLinkCard>Link content</PktLinkCard>)
30
- await window.customElements.whenDefined('pkt-linkcard')
31
27
  const iconElement = document.querySelector('.pkt-linkcard__title')
32
28
  expect(iconElement).not.toBeInTheDocument()
33
29
  })
@@ -39,7 +35,6 @@ describe('PktLinkCard', () => {
39
35
  Link content
40
36
  </PktLinkCard>,
41
37
  )
42
- await window.customElements.whenDefined('pkt-linkcard')
43
38
  const linkElement = container.querySelector('.pkt-linkcard')
44
39
  expect(linkElement).toHaveAttribute('href', href)
45
40
  })
@@ -54,7 +49,6 @@ describe('PktLinkCard', () => {
54
49
  <PktLinkCard title="normal" skin="normal" />
55
50
  </div>,
56
51
  )
57
- await window.customElements.whenDefined('pkt-linkcard')
58
52
  const linkElement = container.querySelector('.pkt-linkcard')
59
53
  expect(linkElement).toHaveClass('pkt-linkcard pkt-linkcard--normal')
60
54
  })
@@ -65,7 +59,6 @@ describe('PktLinkCard', () => {
65
59
  <PktLinkCard title="Blue" skin="blue" />
66
60
  </div>,
67
61
  )
68
- await window.customElements.whenDefined('pkt-linkcard')
69
62
  const linkElement = container.querySelector('.pkt-linkcard')
70
63
  expect(linkElement).toHaveClass('pkt-linkcard pkt-linkcard--blue')
71
64
  })
@@ -76,7 +69,6 @@ describe('PktLinkCard', () => {
76
69
  <PktLinkCard title="Beige" skin="beige" />
77
70
  </div>,
78
71
  )
79
- await window.customElements.whenDefined('pkt-linkcard')
80
72
  const linkElement = container.querySelector('.pkt-linkcard')
81
73
  expect(linkElement).toHaveClass('pkt-linkcard pkt-linkcard--beige')
82
74
  })
@@ -87,7 +79,6 @@ describe('PktLinkCard', () => {
87
79
  <PktLinkCard title="Green" skin="green" />
88
80
  </div>,
89
81
  )
90
- await window.customElements.whenDefined('pkt-linkcard')
91
82
  const linkElement = container.querySelector('.pkt-linkcard')
92
83
  expect(linkElement).toHaveClass('pkt-linkcard pkt-linkcard--green')
93
84
  })
@@ -98,7 +89,6 @@ describe('PktLinkCard', () => {
98
89
  <PktLinkCard title="Beige outline" skin="beige-outline" />
99
90
  </div>,
100
91
  )
101
- await window.customElements.whenDefined('pkt-linkcard')
102
92
  const linkElement = container.querySelector('.pkt-linkcard')
103
93
  expect(linkElement).toHaveClass('pkt-linkcard pkt-linkcard--beige-outline')
104
94
  })
@@ -108,7 +98,6 @@ describe('PktLinkCard', () => {
108
98
  <PktLinkCard title="Gray outline" skin="gray-outline" />
109
99
  </div>,
110
100
  )
111
- await window.customElements.whenDefined('pkt-linkcard')
112
101
  const linkElement = container.querySelector('.pkt-linkcard')
113
102
  expect(linkElement).toHaveClass('pkt-linkcard pkt-linkcard--gray-outline')
114
103
  })
@@ -1,35 +1,48 @@
1
1
  'use client'
2
2
 
3
- import React, { FC, ForwardedRef, forwardRef, ReactElement } from 'react'
4
- import { createComponent } from '@lit/react'
5
- import { PktLinkCard as PktElLinkCard } from '@oslokommune/punkt-elements'
6
- import type { PktElType, PktElConstructor } from '@/interfaces/IPktElements'
3
+ import classNames from 'classnames'
4
+ import { HTMLAttributes, ReactNode } from 'react'
7
5
 
8
- export interface IPktLinkCard extends PktElType {
6
+ import { PktIcon } from '..'
7
+
8
+ export interface IPktLinkCard extends Omit<HTMLAttributes<HTMLAnchorElement>, 'title'> {
9
9
  skin?: 'normal' | 'no-padding' | 'blue' | 'beige' | 'green' | 'gray' | 'beige-outline' | 'gray-outline'
10
10
  title?: string
11
11
  href?: string
12
12
  iconName?: string
13
13
  openInNewTab?: boolean
14
14
  external?: boolean
15
+ children?: ReactNode
15
16
  }
16
17
 
17
- const LitComponent: FC<IPktLinkCard> = createComponent({
18
- tagName: 'pkt-linkcard',
19
- elementClass: PktElLinkCard as PktElConstructor<HTMLElement>,
20
- react: React,
21
- displayName: 'PktLinkCard',
22
- events: {},
23
- })
18
+ export const PktLinkCard = ({
19
+ children,
20
+ skin,
21
+ title,
22
+ href,
23
+ iconName,
24
+ openInNewTab,
25
+ external,
26
+ className,
27
+ ...props
28
+ }: IPktLinkCard) => {
29
+ const classes = ['pkt-linkcard', skin && `pkt-linkcard--${skin}`].filter(Boolean).join(' ')
30
+
31
+ const titleClasses = ['pkt-linkcard__title', external && 'pkt-link pkt-link--external'].filter(Boolean).join(' ')
24
32
 
25
- export const PktLinkCard: FC<IPktLinkCard> = forwardRef(
26
- ({ children, ...props }: IPktLinkCard, ref: ForwardedRef<HTMLElement>): ReactElement => {
27
- return (
28
- <LitComponent ref={ref} {...props}>
29
- <div className="pkt-contents">{children}</div>
30
- </LitComponent>
31
- )
32
- },
33
- )
33
+ return (
34
+ <a
35
+ {...props}
36
+ href={href}
37
+ className={classNames(classes, className)}
38
+ target={openInNewTab ? '_blank' : '_self'}
39
+ rel={openInNewTab ? 'noopener noreferrer' : undefined}
40
+ >
41
+ {iconName && <PktIcon className="pkt-link__icon" name={iconName} />}
42
+ {title && <div className={classNames(titleClasses)}>{title}</div>}
43
+ <div className="pkt-linkcard__text">{children}</div>
44
+ </a>
45
+ )
46
+ }
34
47
 
35
48
  PktLinkCard.displayName = 'PktLinkCard'
@@ -0,0 +1,63 @@
1
+ import { fireEvent, render as originalRender } from '@testing-library/react'
2
+ import { ReactNode } from 'react'
3
+
4
+ import { PktMessagebox } from './Messagebox'
5
+
6
+ describe('Messagebox', () => {
7
+ const render = (jsx: ReactNode) => {
8
+ const renderResult = originalRender(jsx)
9
+ const messageBox = renderResult.container.children[0]
10
+ return { ...renderResult, messageBox }
11
+ }
12
+
13
+ it('render with defaults', async () => {
14
+ const { messageBox } = render(<PktMessagebox />)
15
+
16
+ expect(messageBox.className).toEqual('pkt-messagebox')
17
+ })
18
+
19
+ it('render with skin', async () => {
20
+ const { messageBox } = render(<PktMessagebox skin={'green'} />)
21
+
22
+ expect(messageBox.classList).toContain('pkt-messagebox--green')
23
+ })
24
+
25
+ it('render compact', async () => {
26
+ const { messageBox } = render(<PktMessagebox compact />)
27
+
28
+ expect(messageBox.classList).toContain('pkt-messagebox--compact')
29
+ })
30
+
31
+ it('render simple title', async () => {
32
+ const { getByText } = render(<PktMessagebox title={'Messagebox title'} />)
33
+ const titleElement = getByText('Messagebox title')
34
+ expect(titleElement.classList).toContain('pkt-messagebox__title')
35
+ })
36
+ it('render complex title', async () => {
37
+ const { messageBox } = render(
38
+ <PktMessagebox
39
+ title={
40
+ <>
41
+ Message<strong>box</strong> title
42
+ </>
43
+ }
44
+ />,
45
+ )
46
+ const titleElement = messageBox.querySelector('.pkt-messagebox__title')!
47
+ expect(titleElement.innerHTML).toEqual('Message<strong>box</strong> title')
48
+ })
49
+
50
+ it('should render empty title element when no title specified', async () => {
51
+ const { messageBox } = render(<PktMessagebox title={''} />)
52
+ expect(messageBox.querySelector('.pkt-messagebox__title')).toBeInTheDocument()
53
+ })
54
+
55
+ it('closable', async () => {
56
+ const { getByRole, messageBox } = render(<PktMessagebox closable />)
57
+ const closeButton = getByRole('button', { name: 'Lukk' })
58
+
59
+ fireEvent.click(closeButton)
60
+
61
+ expect(messageBox.classList).toContain('pkt-hide')
62
+ })
63
+ })
@@ -1,36 +1,66 @@
1
1
  'use client'
2
2
 
3
- import React, { FC, ForwardedRef, forwardRef, ReactElement } from 'react'
4
- import { createComponent, EventName } from '@lit/react'
5
- import { PktMessagebox as PktElMessagebox } from '@oslokommune/punkt-elements'
6
- import type { PktElType, PktElConstructor } from '@/interfaces/IPktElements'
7
-
8
- export interface IPktMessagebox extends PktElType {
9
- skin?: 'beige' | 'red' | 'green' | 'blue'
10
- title?: string
3
+ import classNames from 'classnames'
4
+ import { createRef, FC, HTMLAttributes, ReactNode, useCallback, useState } from 'react'
5
+
6
+ import { PktIcon } from '..'
7
+
8
+ export type TMessageboxSkin = 'beige' | 'blue' | 'red' | 'green'
9
+
10
+ export interface IPktMessagebox extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
11
+ skin?: TMessageboxSkin
12
+ title?: ReactNode
11
13
  compact?: boolean
12
14
  closable?: boolean
13
- onClose?: (e: CustomEvent) => void
15
+ onClose?: () => void
14
16
  }
15
17
 
16
- const LitComponent: FC<IPktMessagebox> = createComponent({
17
- tagName: 'pkt-messagebox',
18
- elementClass: PktElMessagebox as PktElConstructor<HTMLElement>,
19
- react: React,
20
- displayName: 'PktMessagebox',
21
- events: {
22
- onClose: 'close' as EventName<CustomEvent>,
23
- },
24
- })
25
-
26
- export const PktMessagebox: FC<IPktMessagebox> = forwardRef(
27
- ({ children, ...props }: IPktMessagebox, ref: ForwardedRef<HTMLElement>): ReactElement => {
28
- return (
29
- <LitComponent {...props} ref={ref}>
30
- <div className="pkt-contents">{children}</div>
31
- </LitComponent>
32
- )
33
- },
34
- )
18
+ export const PktMessagebox: FC<IPktMessagebox> = ({
19
+ children,
20
+ className,
21
+ skin,
22
+ title,
23
+ compact,
24
+ closable,
25
+ onClose,
26
+ ...props
27
+ }: IPktMessagebox) => {
28
+ const [closed, setClosed] = useState(false)
29
+
30
+ const classes = {
31
+ 'pkt-messagebox': true,
32
+ 'pkt-messagebox--compact': compact,
33
+ [`pkt-messagebox--${skin}`]: skin,
34
+ 'pkt-messagebox--closable': closable,
35
+ 'pkt-hide': closed,
36
+ }
37
+
38
+ const componentRootRef = createRef<HTMLDivElement>()
39
+
40
+ const close = useCallback(() => {
41
+ setClosed(true)
42
+ if (onClose) {
43
+ onClose()
44
+ }
45
+ }, [setClosed, onClose])
46
+
47
+ return (
48
+ <div {...props} className={classNames(classes, className)} ref={componentRootRef}>
49
+ {closable && (
50
+ <div className={'"pkt-messagebox__close"'}>
51
+ <button
52
+ onClick={close}
53
+ className={'pkt-btn pkt-btn--tertiary pkt-btn--small pkt-btn--icon-only'}
54
+ aria-label={'Lukk'}
55
+ >
56
+ <PktIcon name="close" className="pkt-link__icon" />
57
+ </button>
58
+ </div>
59
+ )}
60
+ <div className="pkt-messagebox__title">{title}</div>
61
+ <div className={'pkt-messagebox__text'}>{children}</div>
62
+ </div>
63
+ )
64
+ }
35
65
 
36
66
  PktMessagebox.displayName = 'PktMessagebox'