@oslokommune/punkt-react 12.17.2 → 12.18.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": "12.17.2",
3
+ "version": "12.18.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",
@@ -38,7 +38,7 @@
38
38
  "dependencies": {
39
39
  "@lit-labs/ssr-dom-shim": "^1.2.1",
40
40
  "@lit/react": "^1.0.5",
41
- "@oslokommune/punkt-elements": "^12.17.2",
41
+ "@oslokommune/punkt-elements": "^12.18.0",
42
42
  "angular-html-parser": "^6.0.2",
43
43
  "html-format": "^1.1.7",
44
44
  "prettier": "^3.3.3",
@@ -111,5 +111,5 @@
111
111
  "url": "https://github.com/oslokommune/punkt/issues"
112
112
  },
113
113
  "license": "MIT",
114
- "gitHead": "998cf96281840a47cba5ba3e0c76e6fa1d2ed10e"
114
+ "gitHead": "ceaac8e8aac119c09de9e77927150022aa6e316d"
115
115
  }
@@ -163,7 +163,9 @@ export const PktHeader = forwardRef(
163
163
  ></PktIcon>
164
164
  </button>
165
165
  )}
166
- <span className="pkt-header__logo-service">{serviceName}</span>
166
+ <span className="pkt-header__logo-service" translate="no">
167
+ {serviceName}
168
+ </span>
167
169
  </div>
168
170
  <nav className="pkt-header__actions">
169
171
  <ul className="pkt-header__actions-row">
@@ -195,8 +197,12 @@ export const PktHeader = forwardRef(
195
197
  onClick={openUserMenu}
196
198
  >
197
199
  <PktIcon name="user" className="pkt-btn__icon" />
198
- <span className="pkt-header__user-fullname">{representing?.name || user?.name}</span>
199
- <span className="pkt-header__user-shortname">{representing?.shortname || user?.shortname}</span>
200
+ <span className="pkt-header__user-fullname" translate="no">
201
+ {representing?.name || user?.name}
202
+ </span>
203
+ <span className="pkt-header__user-shortname" translate="no">
204
+ {representing?.shortname || user?.shortname}
205
+ </span>
200
206
  <PktIcon name="chevron-thin-down" className="pkt-btn--closed" />
201
207
  <PktIcon name="chevron-thin-up" className="pkt-btn--open" />
202
208
  </button>
@@ -204,7 +210,9 @@ export const PktHeader = forwardRef(
204
210
  {user && (
205
211
  <li>
206
212
  <div className="pkt-user-menu__label">Pålogget som</div>
207
- <div className="pkt-user-menu__name">{user.name}</div>
213
+ <div className="pkt-user-menu__name" translate="no">
214
+ {user.name}
215
+ </div>
208
216
  {user.lastLoggedIn && (
209
217
  <div className="pkt-user-menu__last-logged-in">
210
218
  Sist pålogget: <time>{lastLoggedIn}</time>
@@ -238,7 +246,9 @@ export const PktHeader = forwardRef(
238
246
  {representing && (
239
247
  <>
240
248
  <div className="pkt-user-menu__label">Representerer</div>
241
- <div className="pkt-user-menu__name">{representing.name}</div>
249
+ <div className="pkt-user-menu__name" translate="no">
250
+ {representing.name}
251
+ </div>
242
252
  {representing.orgNumber && (
243
253
  <div className="pkt-user-menu__org-number">Org.nr. {representing.orgNumber}</div>
244
254
  )}
@@ -3,109 +3,155 @@ import { render, screen } from '@testing-library/react'
3
3
  import '@testing-library/jest-dom'
4
4
  import { axe, toHaveNoViolations } from 'jest-axe'
5
5
  import { PktProgressbar } from './Progressbar'
6
+ import exp from 'constants'
6
7
 
7
8
  expect.extend(toHaveNoViolations)
8
9
 
9
10
  describe('PktProgressbar', () => {
10
11
  describe('rendering', () => {
11
- it('renders with default props', () => {
12
- render(<PktProgressbar id="progress1" valueCurrent={50} />)
13
- const progressbar = screen.getByRole('progressbar')
12
+ test('renders with default props', async () => {
13
+ const { container } = render(<PktProgressbar id="progress1" valueCurrent={50} />)
14
+ await window.customElements.whenDefined('pkt-progressbar')
15
+
16
+ const progressbar = container.querySelector('pkt-progressbar')
17
+
14
18
  expect(progressbar).toBeInTheDocument()
15
- expect(progressbar).toHaveAttribute('aria-valuemin', '0')
16
- expect(progressbar).toHaveAttribute('aria-valuemax', '100')
17
- expect(progressbar).toHaveAttribute('aria-valuenow', '50')
18
- const bar = progressbar.querySelector('.pkt-progressbar__bar--dark-blue')
19
+ expect(progressbar).toHaveAttribute('id', 'progress1')
20
+ expect(progressbar).toHaveAttribute('valuemin', '0')
21
+ expect(progressbar).toHaveAttribute('valuemax', '100')
22
+ expect(progressbar).toHaveAttribute('valuecurrent', '50')
23
+
24
+ const bar = progressbar?.querySelector('.pkt-progressbar__bar--dark-blue')
19
25
  expect(bar).toBeInTheDocument()
20
26
  })
21
27
 
22
- it('renders with custom skin', () => {
23
- render(<PktProgressbar id="progress2" valueCurrent={50} skin="red" />)
24
- const bar = screen.getByRole('progressbar').querySelector('.pkt-progressbar__bar--red')
28
+ test('renders with custom skin', async () => {
29
+ const { container } = render(<PktProgressbar id="progress2" valueCurrent={50} skin="red" />)
30
+ await window.customElements.whenDefined('pkt-progressbar')
31
+
32
+ const progressbar = container.querySelector('pkt-progressbar')
33
+ const bar = progressbar?.querySelector('.pkt-progressbar__bar--red')
25
34
  expect(bar).toBeInTheDocument()
26
35
  })
27
36
 
28
- it('renders title correctly', () => {
29
- render(<PktProgressbar id="progress3" valueCurrent={50} title="Progress" />)
30
- const title = screen.getByText('Progress')
37
+ test('renders title correctly', async () => {
38
+ const { container, getByText } = render(<PktProgressbar id="progress3" valueCurrent={50} title="Progress" />)
39
+ await window.customElements.whenDefined('pkt-progressbar')
40
+
41
+ const progressbar = container.querySelector('pkt-progressbar')
42
+
43
+ // Select by class
44
+ const title = progressbar?.querySelector('.pkt-progressbar__title')
31
45
  expect(title).toBeInTheDocument()
32
- expect(title).toHaveClass('pkt-progressbar__title')
46
+
47
+ // Select by text
48
+ const titleText = getByText('Progress')
49
+ expect(titleText).toBeInTheDocument()
33
50
  })
34
51
 
35
- it('renders title with center position', () => {
36
- render(<PktProgressbar id="progress4" valueCurrent={50} title="Progress" titlePosition="center" />)
37
- const title = screen.getByText('Progress')
38
- expect(title).toHaveClass('pkt-progressbar__title-center')
52
+ test('renders title with center position', async () => {
53
+ const { container } = render(
54
+ <PktProgressbar id="progress4" valueCurrent={50} title="Progress" titlePosition="center" />,
55
+ )
56
+ await window.customElements.whenDefined('pkt-progressbar')
57
+ const title = container.querySelector('.pkt-progressbar__title-center')
58
+ expect(title).toBeInTheDocument()
39
59
  })
40
60
 
41
- it('renders status as percentage', () => {
42
- render(<PktProgressbar id="progress5" valueCurrent={50} statusType="percentage" />)
43
- const status = screen.getByText('50%').parentElement
61
+ test('renders status as percentage', async () => {
62
+ const { container } = render(<PktProgressbar id="progress5" valueCurrent={50} statusType="percentage" />)
63
+ await window.customElements.whenDefined('pkt-progressbar')
64
+ const status = container.querySelector('.pkt-progressbar__status')
65
+ console.log(status)
44
66
  expect(status).toBeInTheDocument()
45
- expect(status).toHaveClass('pkt-progressbar__status')
67
+ expect(status).toHaveTextContent('50%')
46
68
  })
47
69
 
48
- it('renders status as fraction', () => {
49
- render(<PktProgressbar id="progress6" valueCurrent={50} valueMax={200} statusType="fraction" />)
50
- const status = screen.getByText('50 av 200').parentElement
70
+ test('renders status as fraction', async () => {
71
+ const { container } = render(
72
+ <PktProgressbar id="progress6" valueCurrent={50} valueMax={200} statusType="fraction" />,
73
+ )
74
+ await window.customElements.whenDefined('pkt-progressbar')
75
+ const status = container.querySelector('.pkt-progressbar__status')
51
76
  expect(status).toBeInTheDocument()
52
- expect(status).toHaveClass('pkt-progressbar__status')
77
+ expect(status).toHaveTextContent('50 av 200')
53
78
  })
54
79
 
55
- it('renders status with center placement', () => {
56
- render(<PktProgressbar id="progress7" valueCurrent={50} statusType="percentage" statusPlacement="center" />)
57
- const status = screen.getByText('50%').parentElement
58
- expect(status).toHaveClass('pkt-progressbar__status--center')
80
+ test('renders status with center placement', async () => {
81
+ const { container } = render(
82
+ <PktProgressbar id="progress7" valueCurrent={50} statusType="percentage" statusPlacement="center" />,
83
+ )
84
+ await window.customElements.whenDefined('pkt-progressbar')
85
+ const status = container.querySelector('.pkt-progressbar__status--center')
86
+ expect(status).toBeInTheDocument()
59
87
  })
60
88
 
61
- it('renders status with following placement', () => {
62
- render(<PktProgressbar id="progress8" valueCurrent={50} statusType="percentage" statusPlacement="following" />)
63
- const status = screen.getByText('50%')
64
- expect(status).toHaveClass('pkt-progressbar__status-placement--following')
89
+ test('renders status with following placement', async () => {
90
+ const { container } = render(
91
+ <PktProgressbar id="progress8" valueCurrent={50} statusType="percentage" statusPlacement="following" />,
92
+ )
93
+ await window.customElements.whenDefined('pkt-progressbar')
94
+ const status = container.querySelector('.pkt-progressbar__status-placement--following')
95
+ expect(status).toBeInTheDocument()
65
96
  })
66
97
 
67
- it('handles aria-label correctly', () => {
68
- render(<PktProgressbar id="progress9" valueCurrent={50} ariaLabel="Progress bar" />)
69
- const progressbar = screen.getByRole('progressbar')
70
- expect(progressbar).toHaveAttribute('aria-label', 'Progress bar')
71
- expect(progressbar).not.toHaveAttribute('aria-labelledby')
98
+ test('handles aria-label correctly', async () => {
99
+ const { container } = render(<PktProgressbar id="progress9" valueCurrent={50} ariaLabel="Progress bar" />)
100
+ await window.customElements.whenDefined('pkt-progressbar')
101
+ const progressbar = container.querySelector('pkt-progressbar')
102
+ expect(progressbar).toHaveAttribute('arialabel', 'Progress bar')
103
+ expect(progressbar).toHaveAttribute('arialabelledby', '')
72
104
  })
73
105
 
74
- it('handles role meter correctly', () => {
75
- render(<PktProgressbar id="progress10" role="meter" valueCurrent={50} ariaLabel="Meter bar" />)
76
- const progressbar = screen.getByRole('meter')
77
- expect(progressbar).toHaveAttribute('aria-label', 'Meter bar')
106
+ test('handles role meter correctly', async () => {
107
+ const { container } = render(
108
+ <PktProgressbar id="progress10" role="meter" valueCurrent={50} ariaLabel="Meter bar" />,
109
+ )
110
+ await window.customElements.whenDefined('pkt-progressbar')
111
+ const progressbar = container.querySelector('pkt-progressbar')
112
+ expect(progressbar).toHaveAttribute('role', 'meter')
78
113
  })
79
114
 
80
- it('handles id for title and aria-labelledby correctly', () => {
81
- render(<PktProgressbar valueCurrent={50} title="Progress" id="progresstitle" />)
82
- const title = screen.getByText('Progress')
83
- expect(title).toHaveAttribute('id', 'progresstitle-title')
84
- const progressbar = screen.getByRole('progressbar')
85
- expect(progressbar).toHaveAttribute('aria-labelledby', 'progresstitle-title')
115
+ test('handles id for title and aria-labelledby correctly', async () => {
116
+ const { container } = render(<PktProgressbar valueCurrent={50} title="Progress" id="progressId" />)
117
+ await window.customElements.whenDefined('pkt-progressbar')
118
+ const title = container.querySelector('.pkt-progressbar__title')
119
+ expect(title).toHaveAttribute('id', 'progressId-title')
120
+
121
+ const progressbarDiv = container.querySelector('.pkt-progressbar__bar-wrapper')
122
+ expect(progressbarDiv).toBeInTheDocument()
123
+ expect(progressbarDiv).toHaveAttribute('aria-labelledby', 'progressId-title')
86
124
  })
87
125
 
88
- it('generates id if none is provided', () => {
89
- render(<PktProgressbar valueCurrent={51} />)
126
+ test('generates id if none is provided', async () => {
90
127
  const { container } = render(<PktProgressbar valueCurrent={50} />)
91
- const outerDiv = container.firstChild as HTMLElement
92
- const id = outerDiv.getAttribute('id')
128
+ await window.customElements.whenDefined('pkt-progressbar')
129
+
130
+ const outerDiv = container.querySelector('.pkt-progressbar__container')
131
+ const id = outerDiv?.getAttribute('id')
93
132
  expect(id).toHaveLength(36)
94
133
  })
95
134
 
96
- it('sets ref correctly', () => {
97
- const ref = React.createRef<HTMLDivElement>()
98
- const { container } = render(<PktProgressbar ref={ref} id="progress-ref" valueCurrent={50} title="Ref bar" />)
99
- const outerDiv = container.firstChild
100
- expect(ref.current).toBeInstanceOf(HTMLDivElement)
101
- expect(ref.current).not.toBeNull()
102
- expect(outerDiv).toBe(ref.current)
135
+ test('sets ref correctly', async () => {
136
+ const ref = createRef<HTMLElement>()
137
+ const { unmount } = render(<PktProgressbar ref={ref} id="progress-ref" valueCurrent={50} title="Ref bar" />)
138
+
139
+ await window.customElements.whenDefined('pkt-progressbar')
140
+
141
+ expect(ref.current).toBeInstanceOf(HTMLElement)
142
+ unmount()
143
+ expect(ref.current).toBeNull()
103
144
  })
104
145
  })
105
146
 
147
+ // ACCESSIBILITY TESTS
148
+
106
149
  describe('accessibility', () => {
107
- it('renders with no wcag errors with axe', async () => {
108
- const { container } = render(<PktProgressbar id="progress-axe" valueCurrent={50} title="Axe test" />)
150
+ test('renders with no wcag errors with axe', async () => {
151
+ const { container } = render(
152
+ <PktProgressbar id="progress-axe" valueCurrent={50} title="Axe test" role="progressbar" />,
153
+ )
154
+ await window.customElements.whenDefined('pkt-progressbar')
109
155
  const results = await axe(container)
110
156
  expect(results).toHaveNoViolations()
111
157
  })
@@ -1,123 +1,27 @@
1
- import React, { useState, useEffect, useRef, CSSProperties, forwardRef, HTMLAttributes, ForwardedRef } from 'react'
2
- import classNames from 'classnames'
3
- import { v4 as uuidv4 } from 'uuid'
1
+ import React, { forwardRef, ForwardedRef, ReactElement } from 'react'
2
+ import { createComponent } from '@lit/react'
3
+ import { PktProgressbar as PktElProgressbar } from '@oslokommune/punkt-elements'
4
+ import type { PktElType, PktElConstructor } from '@/interfaces/IPktElements'
5
+ import type { IPktProgressbar as IPktElProgressbar } from '@oslokommune/punkt-elements'
4
6
 
5
- interface IPktProgressbar extends HTMLAttributes<HTMLDivElement> {
6
- valueCurrent: number
7
- valueMax?: number
8
- valueMin?: number
9
- ariaValueText?: string
10
- skin?: 'dark-blue' | 'light-blue' | 'green' | 'red'
11
- title?: string
12
- titlePosition?: 'left' | 'center'
13
- statusType?: 'none' | 'percentage' | 'fraction'
14
- statusPlacement?: 'center' | 'left' | 'following'
15
- ariaLabel?: string
16
- ariaLabelledby?: string
17
- role?: 'progressbar' | 'meter'
18
- className?: string
19
- id?: string
20
- }
7
+ type ExtendedProgressbar = IPktElProgressbar & PktElType
21
8
 
22
- export const PktProgressbar = forwardRef(
23
- (
24
- {
25
- valueCurrent,
26
- valueMin = 0,
27
- valueMax = 100,
28
- skin = 'dark-blue',
29
- title,
30
- titlePosition = 'left',
31
- statusType = 'none',
32
- statusPlacement = 'following',
33
- id,
34
- ariaLabel,
35
- ariaLabelledby,
36
- role = 'progressbar',
37
- className,
38
- ariaValueText,
39
- ...props
40
- }: IPktProgressbar,
41
- ref: ForwardedRef<HTMLDivElement>,
42
- ) => {
43
- const labelRef = useRef(null)
44
- const [labelWidth, setLabelWidth] = useState(0)
45
- const [progressId, setProgressId] = useState(id)
46
-
47
- useEffect(() => {
48
- if (!id) {
49
- setProgressId(uuidv4())
50
- }
51
- }, [id])
52
-
53
- useEffect(() => {
54
- if (labelRef.current) {
55
- const label: HTMLSpanElement = labelRef.current
56
- setLabelWidth(label.getBoundingClientRect().width)
57
- }
58
- })
59
- const totalSteps = valueMax - valueMin
60
- const percentageOfSteps = (valueCurrent / totalSteps) * 100
61
- const currentPercentage = valueMax !== 100 || valueMin !== 0 ? Math.round(percentageOfSteps) : valueCurrent
62
- const formattedTitle = `${valueCurrent} av ${valueMax}`
63
- const hasStatus = statusType !== 'none'
64
-
65
- const barClasses = classNames('pkt-progressbar__bar', { [`pkt-progressbar__bar--${skin}`]: skin })
66
-
67
- const titleClasses = classNames('pkt-progressbar__title', {
68
- ['pkt-progressbar__title-center']: titlePosition === 'center',
69
- })
9
+ export interface IPktProgressbar extends ExtendedProgressbar {}
70
10
 
71
- const statusClasses = classNames('pkt-progressbar__status', {
72
- ['pkt-progressbar__status--center']: statusPlacement === 'center',
73
- })
74
-
75
- const placementClasses = classNames({
76
- ['pkt-progressbar__status-placement--following']: statusPlacement === 'following',
77
- ['pkt-progressbar__status-placement--center']: statusPlacement === 'center',
78
- ['pkt-progressbar__status-placement--left']: statusPlacement === 'left',
79
- })
11
+ const LitComponent = createComponent({
12
+ tagName: 'pkt-progressbar',
13
+ elementClass: PktElProgressbar as PktElConstructor<HTMLElement>,
14
+ react: React,
15
+ displayName: 'PktProgressbar',
16
+ events: {},
17
+ })
80
18
 
19
+ export const PktProgressbar = forwardRef(
20
+ ({ children, ...props }: IPktProgressbar, ref: ForwardedRef<HTMLElement>): ReactElement => {
81
21
  return (
82
- <div
83
- {...props}
84
- ref={ref}
85
- className={classNames('pkt-progressbar__container', className)}
86
- id={progressId}
87
- style={
88
- {
89
- '--pkt-progress-label-width': `${labelWidth}px`,
90
- '--pkt-progress-width': `${currentPercentage}%`,
91
- } as CSSProperties
92
- }
93
- >
94
- {title && (
95
- <p id={`${progressId}-title`} className={titleClasses}>
96
- {title}
97
- </p>
98
- )}
99
-
100
- <div
101
- role={role}
102
- className="pkt-progressbar__bar-wrapper"
103
- aria-valuemin={valueMin}
104
- aria-valuemax={valueMax}
105
- aria-valuenow={valueCurrent}
106
- aria-labelledby={ariaLabelledby || (title && `${progressId}-title`)}
107
- aria-label={ariaLabel}
108
- aria-valuetext={ariaValueText}
109
- >
110
- <div className={barClasses}></div>
111
- </div>
112
-
113
- {hasStatus && (
114
- <div className={statusClasses}>
115
- <span className={placementClasses} ref={labelRef}>
116
- {statusType === 'percentage' ? `${currentPercentage}%` : formattedTitle}
117
- </span>
118
- </div>
119
- )}
120
- </div>
22
+ <LitComponent ref={ref} {...props}>
23
+ <div className="pkt-contents">{children}</div>
24
+ </LitComponent>
121
25
  )
122
26
  },
123
27
  )