@oslokommune/punkt-react 12.43.1 → 13.0.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/CHANGELOG.md +28 -0
- package/dist/index.d.ts +57 -61
- package/dist/punkt-react.es.js +12898 -12547
- package/dist/punkt-react.umd.js +374 -372
- package/package.json +5 -5
- package/src/components/checkbox/Checkbox.test.tsx +1 -10
- package/src/components/checkbox/Checkbox.tsx +47 -36
- package/src/components/inputwrapper/InputWrapper.tsx +177 -27
- package/src/components/radio/RadioButton.test.tsx +4 -8
- package/src/components/radio/RadioButton.tsx +47 -36
- package/src/components/select/Select.test.tsx +27 -35
- package/src/components/select/Select.tsx +72 -53
- package/src/components/textarea/Textarea.test.tsx +4 -4
- package/src/components/textarea/Textarea.tsx +122 -43
- package/src/components/textinput/Textinput.test.tsx +12 -11
- package/src/components/textinput/Textinput.tsx +167 -43
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslokommune/punkt-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.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.7",
|
|
41
|
-
"@oslokommune/punkt-elements": "^
|
|
41
|
+
"@oslokommune/punkt-elements": "^13.0.0",
|
|
42
42
|
"angular-html-parser": "^6.0.2",
|
|
43
43
|
"html-format": "^1.1.7",
|
|
44
44
|
"prettier": "^3.3.3",
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@babel/plugin-proposal-private-property-in-object": "^7.18.6",
|
|
51
|
-
"@oslokommune/punkt-assets": "^
|
|
52
|
-
"@oslokommune/punkt-css": "^
|
|
51
|
+
"@oslokommune/punkt-assets": "^13.0.0",
|
|
52
|
+
"@oslokommune/punkt-css": "^13.0.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",
|
|
@@ -112,5 +112,5 @@
|
|
|
112
112
|
"url": "https://github.com/oslokommune/punkt/issues"
|
|
113
113
|
},
|
|
114
114
|
"license": "MIT",
|
|
115
|
-
"gitHead": "
|
|
115
|
+
"gitHead": "8012adb5b795a7ab26d844a3311435fef05121c4"
|
|
116
116
|
}
|
|
@@ -12,8 +12,6 @@ describe('PktCheckbox', () => {
|
|
|
12
12
|
it('renders a basic checkbox with label', async () => {
|
|
13
13
|
const { getByLabelText } = render(<PktCheckbox id="myCheckbox" label="My Checkbox" />)
|
|
14
14
|
|
|
15
|
-
await window.customElements.whenDefined('pkt-checkbox')
|
|
16
|
-
|
|
17
15
|
// Check if the label is present in the document
|
|
18
16
|
const checkboxLabel = getByLabelText('My Checkbox')
|
|
19
17
|
expect(checkboxLabel).toBeInTheDocument()
|
|
@@ -22,8 +20,6 @@ describe('PktCheckbox', () => {
|
|
|
22
20
|
it('renders an error state checkbox', async () => {
|
|
23
21
|
render(<PktCheckbox id="myCheckbox" label="My Checkbox" hasError />)
|
|
24
22
|
|
|
25
|
-
await window.customElements.whenDefined('pkt-checkbox')
|
|
26
|
-
|
|
27
23
|
const checkbox = screen.getByRole('checkbox', { name: 'My Checkbox' })
|
|
28
24
|
|
|
29
25
|
// Verify that the error class is applied to the checkbox
|
|
@@ -33,8 +29,6 @@ describe('PktCheckbox', () => {
|
|
|
33
29
|
it('renders as Switch', async () => {
|
|
34
30
|
render(<PktCheckbox id="myCheckbox" label="My Checkbox" isSwitch />)
|
|
35
31
|
|
|
36
|
-
await window.customElements.whenDefined('pkt-checkbox')
|
|
37
|
-
|
|
38
32
|
const checkbox = screen.getByRole('switch', { name: 'My Checkbox' })
|
|
39
33
|
|
|
40
34
|
// Verify that the error class is applied to the checkbox
|
|
@@ -44,7 +38,6 @@ describe('PktCheckbox', () => {
|
|
|
44
38
|
test('renders a default checked checkbox', async () => {
|
|
45
39
|
const { getByRole } = render(<PktCheckbox id="myCheckbox" label="My Checkbox" defaultChecked={true} />)
|
|
46
40
|
|
|
47
|
-
await window.customElements.whenDefined('pkt-checkbox')
|
|
48
41
|
// Find the checkbox element by its "checkbox" role
|
|
49
42
|
const checkbox = getByRole('checkbox') as HTMLInputElement
|
|
50
43
|
// Verify that the checkbox is initially checked
|
|
@@ -55,8 +48,6 @@ describe('PktCheckbox', () => {
|
|
|
55
48
|
const onClickMock = jest.fn()
|
|
56
49
|
const { getByLabelText } = render(<PktCheckbox id="myCheckbox" label="My Checkbox" onClick={onClickMock} />)
|
|
57
50
|
|
|
58
|
-
await window.customElements.whenDefined('pkt-checkbox')
|
|
59
|
-
|
|
60
51
|
// Get the checkbox label element
|
|
61
52
|
const checkboxLabel = getByLabelText('My Checkbox')
|
|
62
53
|
|
|
@@ -70,7 +61,7 @@ describe('PktCheckbox', () => {
|
|
|
70
61
|
describe('accessibility', () => {
|
|
71
62
|
it('renders with no wcag errors with axe', async () => {
|
|
72
63
|
const { container } = render(<PktCheckbox id="accessibilityTest" label="My checkkbox"></PktCheckbox>)
|
|
73
|
-
|
|
64
|
+
|
|
74
65
|
const results = await axe(container)
|
|
75
66
|
|
|
76
67
|
expect(results).toHaveNoViolations()
|
|
@@ -1,49 +1,60 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { ForwardedRef, forwardRef } from 'react'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
ChangeEventHandler,
|
|
5
|
-
FocusEventHandler,
|
|
6
|
-
ForwardedRef,
|
|
7
|
-
ForwardRefExoticComponent,
|
|
8
|
-
InputHTMLAttributes,
|
|
9
|
-
ReactNode,
|
|
10
|
-
} from 'react'
|
|
11
|
-
import { createComponent, EventName } from '@lit/react'
|
|
12
|
-
import { PktCheckbox as PktElCheckbox } from '@oslokommune/punkt-elements'
|
|
13
|
-
import { PktEventWithTarget } from '@/interfaces/IPktElements'
|
|
14
|
-
|
|
15
|
-
export interface IPktCheckbox extends InputHTMLAttributes<HTMLInputElement> {
|
|
3
|
+
export interface IPktCheckbox extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
16
4
|
id: string
|
|
17
|
-
name?: string
|
|
18
|
-
label: string
|
|
19
5
|
hasTile?: boolean
|
|
6
|
+
disabled?: boolean
|
|
7
|
+
label?: string
|
|
8
|
+
checkHelptext?: string
|
|
20
9
|
hasError?: boolean
|
|
21
10
|
defaultChecked?: boolean
|
|
22
|
-
disabled?: boolean
|
|
23
11
|
value?: string
|
|
24
|
-
checkHelptext?: string | ReactNode | ReactNode[]
|
|
25
12
|
isSwitch?: boolean
|
|
26
13
|
hideLabel?: boolean
|
|
27
14
|
labelPosition?: 'right' | 'left'
|
|
28
15
|
checked?: boolean
|
|
29
|
-
ref?: ForwardedRef<HTMLInputElement>
|
|
30
|
-
onChange?: ChangeEventHandler<HTMLInputElement>
|
|
31
|
-
onInput?: ChangeEventHandler<HTMLInputElement>
|
|
32
|
-
onBlur?: FocusEventHandler<HTMLInputElement>
|
|
33
|
-
onFocus?: FocusEventHandler<HTMLInputElement>
|
|
34
|
-
onValueChange?: (e: CustomEvent) => void
|
|
35
16
|
}
|
|
36
17
|
|
|
37
|
-
export const PktCheckbox =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
18
|
+
export const PktCheckbox = forwardRef(
|
|
19
|
+
(
|
|
20
|
+
{
|
|
21
|
+
id,
|
|
22
|
+
hasTile = false,
|
|
23
|
+
disabled = false,
|
|
24
|
+
label,
|
|
25
|
+
checkHelptext,
|
|
26
|
+
hasError = false,
|
|
27
|
+
className,
|
|
28
|
+
isSwitch = false,
|
|
29
|
+
hideLabel = false,
|
|
30
|
+
labelPosition,
|
|
31
|
+
checked,
|
|
32
|
+
...props
|
|
33
|
+
}: IPktCheckbox,
|
|
34
|
+
ref: ForwardedRef<HTMLInputElement>,
|
|
35
|
+
): React.ReactElement => {
|
|
36
|
+
const classes = [className, 'pkt-input-check'].filter(Boolean).join(' ')
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className={classes}>
|
|
40
|
+
<div className={`pkt-input-check__input ${hasTile ? 'pkt-input-check__input--tile' : ''}`}>
|
|
41
|
+
<input
|
|
42
|
+
role={isSwitch ? 'switch' : 'checkbox'}
|
|
43
|
+
ref={ref}
|
|
44
|
+
className={`pkt-input-check__input-checkbox ${hasError ? 'pkt-input-check__input-checkbox--error' : ''}`}
|
|
45
|
+
type="checkbox"
|
|
46
|
+
id={id}
|
|
47
|
+
disabled={disabled}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
<label className="pkt-input-check__input-label" htmlFor={id}>
|
|
51
|
+
{label}
|
|
52
|
+
{checkHelptext && <div className="pkt-input-check__input-helptext">{checkHelptext}</div>}
|
|
53
|
+
</label>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
48
57
|
},
|
|
49
|
-
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
PktCheckbox.displayName = 'PktCheckbox'
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import type { PktElType } from '@/interfaces/IPktElements'
|
|
3
|
+
import { ForwardedRef, forwardRef, ReactNode, RefAttributes, useState, MouseEvent } from 'react'
|
|
4
|
+
import { PktAlert } from '../alert/Alert'
|
|
5
|
+
import { PktButton } from '../button/Button'
|
|
7
6
|
|
|
8
|
-
export interface IPktInputWrapper extends
|
|
7
|
+
export interface IPktInputWrapper extends RefAttributes<HTMLElement> {
|
|
9
8
|
forId: string
|
|
10
9
|
label: string
|
|
11
|
-
helptext?: string | ReactNode
|
|
12
|
-
helptextDropdown?: string | ReactNode
|
|
10
|
+
helptext?: string | ReactNode
|
|
11
|
+
helptextDropdown?: string | ReactNode
|
|
13
12
|
helptextDropdownButton?: string
|
|
14
13
|
counter?: boolean
|
|
15
14
|
counterCurrent?: number
|
|
@@ -19,35 +18,186 @@ export interface IPktInputWrapper extends Omit<PktElType, 'ref'> {
|
|
|
19
18
|
requiredTag?: boolean
|
|
20
19
|
requiredText?: string
|
|
21
20
|
hasError?: boolean
|
|
22
|
-
errorMessage?: string | ReactNode
|
|
21
|
+
errorMessage?: string | ReactNode
|
|
23
22
|
disabled?: boolean
|
|
24
23
|
inline?: boolean
|
|
25
24
|
ariaDescribedby?: string
|
|
26
25
|
useWrapper?: boolean
|
|
26
|
+
children?: ReactNode
|
|
27
|
+
className?: string
|
|
27
28
|
hasFieldset?: boolean
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
role?: string
|
|
30
|
+
counterPosition?: 'top' | 'bottom'
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
export const PktInputWrapper = forwardRef(
|
|
34
|
+
(
|
|
35
|
+
{
|
|
36
|
+
forId,
|
|
37
|
+
label,
|
|
38
|
+
helptext,
|
|
39
|
+
helptextDropdown,
|
|
40
|
+
helptextDropdownButton,
|
|
41
|
+
counter,
|
|
42
|
+
counterCurrent = 0,
|
|
43
|
+
counterMaxLength,
|
|
44
|
+
optionalTag = false,
|
|
45
|
+
optionalText = 'Valgfritt',
|
|
46
|
+
requiredTag = false,
|
|
47
|
+
requiredText = 'Må fylles ut',
|
|
48
|
+
hasError = false,
|
|
49
|
+
errorMessage,
|
|
50
|
+
disabled = false,
|
|
51
|
+
inline = false,
|
|
52
|
+
ariaDescribedby,
|
|
53
|
+
useWrapper = true,
|
|
54
|
+
children,
|
|
55
|
+
className = '',
|
|
56
|
+
hasFieldset = false,
|
|
57
|
+
role = 'group',
|
|
58
|
+
counterPosition = 'bottom',
|
|
59
|
+
}: IPktInputWrapper,
|
|
60
|
+
ref: ForwardedRef<HTMLDivElement>,
|
|
61
|
+
) => {
|
|
62
|
+
const [isHelpTextOpen, setIsHelpTextOpen] = useState(false)
|
|
41
63
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
const tagText = optionalTag ? optionalText : requiredTag ? requiredText : null
|
|
65
|
+
|
|
66
|
+
const describedBy = ariaDescribedby || (helptext ? `${forId}-helptext` : undefined)
|
|
67
|
+
const showCounter = !!counter
|
|
68
|
+
const counterTop = showCounter && counterPosition === 'top'
|
|
69
|
+
const counterBottom = showCounter && counterPosition === 'bottom'
|
|
70
|
+
|
|
71
|
+
const toggleHelpText = () => setIsHelpTextOpen((prev) => !prev)
|
|
72
|
+
|
|
73
|
+
const wrapperClasses = [
|
|
74
|
+
'pkt-inputwrapper',
|
|
75
|
+
className,
|
|
76
|
+
hasError ? 'pkt-inputwrapper--error' : '',
|
|
77
|
+
disabled ? 'pkt-inputwrapper--disabled' : '',
|
|
78
|
+
inline ? 'pkt-inputwrapper--inline' : '',
|
|
79
|
+
]
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.join(' ')
|
|
82
|
+
|
|
83
|
+
const tagClasses = [
|
|
84
|
+
'pkt-tag',
|
|
85
|
+
'pkt-tag--small',
|
|
86
|
+
'pkt-tag--thin-text',
|
|
87
|
+
optionalTag && 'pkt-tag--blue-light',
|
|
88
|
+
!optionalTag && requiredTag && 'pkt-tag--beige',
|
|
89
|
+
]
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join(' ')
|
|
92
|
+
|
|
93
|
+
const Counter = () =>
|
|
94
|
+
showCounter ? (
|
|
95
|
+
<div className="pkt-input__counter" aria-live="polite" aria-atomic="true">
|
|
96
|
+
{counterCurrent}
|
|
97
|
+
{counterMaxLength ? `/${counterMaxLength}` : ''}
|
|
48
98
|
</div>
|
|
49
|
-
|
|
50
|
-
|
|
99
|
+
) : null
|
|
100
|
+
|
|
101
|
+
const renderLabel = () => {
|
|
102
|
+
const labelContent = (
|
|
103
|
+
<>
|
|
104
|
+
{label} {tagText && <span className={tagClasses}>{tagText}</span>}
|
|
105
|
+
</>
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if (!useWrapper) {
|
|
109
|
+
return (
|
|
110
|
+
<label htmlFor={forId} className="pkt-sr-only" aria-describedby={describedBy} id={`${forId}-label`}>
|
|
111
|
+
{label}
|
|
112
|
+
</label>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (hasFieldset) {
|
|
117
|
+
return (
|
|
118
|
+
<legend className="pkt-inputwrapper__legend" id={`${forId}-label`}>
|
|
119
|
+
{labelContent}
|
|
120
|
+
</legend>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<label className="pkt-inputwrapper__label" htmlFor={forId} aria-describedby={describedBy} id={`${forId}-label`}>
|
|
126
|
+
{labelContent}
|
|
127
|
+
</label>
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const renderHelptext = () => {
|
|
132
|
+
if (!helptext && !helptextDropdown) return null
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<>
|
|
136
|
+
{helptext && (
|
|
137
|
+
<div className="pkt-inputwrapper__helptext" id={`${forId}-helptext`}>
|
|
138
|
+
{helptext}
|
|
139
|
+
</div>
|
|
140
|
+
)}
|
|
141
|
+
{helptextDropdown && (
|
|
142
|
+
<div className="pkt-inputwrapper__helptext-expandable">
|
|
143
|
+
<PktButton
|
|
144
|
+
skin="tertiary"
|
|
145
|
+
size="small"
|
|
146
|
+
variant="icon-right"
|
|
147
|
+
iconName={isHelpTextOpen ? 'chevron-thin-up' : 'chevron-thin-down'}
|
|
148
|
+
className="pkt-link pkt-link--icon-right"
|
|
149
|
+
onClick={toggleHelpText}
|
|
150
|
+
>
|
|
151
|
+
<span
|
|
152
|
+
dangerouslySetInnerHTML={{
|
|
153
|
+
__html: helptextDropdownButton ?? 'Les mer <span class="pkt-sr-only">om inputfeltet</span>',
|
|
154
|
+
}}
|
|
155
|
+
/>
|
|
156
|
+
</PktButton>
|
|
157
|
+
<div
|
|
158
|
+
className={`pkt-inputwrapper__helptext ${
|
|
159
|
+
isHelpTextOpen
|
|
160
|
+
? 'pkt-inputwrapper__helptext-expandable-open'
|
|
161
|
+
: 'pkt-inputwrapper__helptext-expandable-closed'
|
|
162
|
+
}`}
|
|
163
|
+
>
|
|
164
|
+
{helptextDropdown}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
)}
|
|
168
|
+
</>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const content = (
|
|
173
|
+
<>
|
|
174
|
+
{renderLabel()}
|
|
175
|
+
{renderHelptext()}
|
|
176
|
+
{counterTop && <Counter />}
|
|
177
|
+
{children}
|
|
178
|
+
{counterBottom && <Counter />}
|
|
179
|
+
{hasError && errorMessage && (
|
|
180
|
+
<div className="pkt-inputwrapper__alert-wrapper">
|
|
181
|
+
<PktAlert skin="error" aria-live="assertive" role="alert" id={`${forId}-error`} compact>
|
|
182
|
+
{errorMessage}
|
|
183
|
+
</PktAlert>
|
|
184
|
+
</div>
|
|
185
|
+
)}
|
|
186
|
+
</>
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<div className={wrapperClasses} ref={ref} role={role}>
|
|
191
|
+
{hasFieldset ? (
|
|
192
|
+
<fieldset className="pkt-inputwrapper__fieldset" aria-describedby={describedBy}>
|
|
193
|
+
{content}
|
|
194
|
+
</fieldset>
|
|
195
|
+
) : (
|
|
196
|
+
<div className="pkt-inputwrapper__fieldset">{content}</div>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
51
199
|
)
|
|
52
200
|
},
|
|
53
201
|
)
|
|
202
|
+
|
|
203
|
+
PktInputWrapper.displayName = 'PktInputWrapper'
|
|
@@ -12,9 +12,8 @@ describe('PktRadiobutton', () => {
|
|
|
12
12
|
// Test case for rendering a basic radiogroup
|
|
13
13
|
it('renders without errors', async () => {
|
|
14
14
|
const { container } = render(<PktRadioButton id="radio1" name="radioGroup" label="Option 1" value="option1" />)
|
|
15
|
-
await window.customElements.whenDefined('pkt-radiobutton')
|
|
16
15
|
|
|
17
|
-
const inputElement = container.querySelector('
|
|
16
|
+
const inputElement = container.querySelector('input[type="radio"]')
|
|
18
17
|
expect(inputElement).toBeInTheDocument()
|
|
19
18
|
expect(inputElement).toHaveAttribute('id', 'radio1')
|
|
20
19
|
expect(inputElement).toHaveAttribute('name', 'radioGroup')
|
|
@@ -37,11 +36,8 @@ describe('PktRadiobutton', () => {
|
|
|
37
36
|
</PktInputWrapper>,
|
|
38
37
|
)
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const option1 = container.querySelector('pkt-radiobutton[id="radio1"]')
|
|
44
|
-
const option2 = container.querySelector('pkt-radiobutton[id="radio2"]')
|
|
39
|
+
const option1 = container.querySelector('input[type="radio"][id="radio1"]')
|
|
40
|
+
const option2 = container.querySelector('input[type="radio"][id="radio2"]')
|
|
45
41
|
|
|
46
42
|
expect(option1).toBeInTheDocument()
|
|
47
43
|
expect(option2).toBeInTheDocument()
|
|
@@ -62,7 +58,7 @@ describe('PktRadiobutton', () => {
|
|
|
62
58
|
const { container } = render(
|
|
63
59
|
<PktRadioButton name="accessibilitytest" id="accessibilityTest" label="My checkkbox" />,
|
|
64
60
|
)
|
|
65
|
-
|
|
61
|
+
|
|
66
62
|
const results = await axe(container)
|
|
67
63
|
|
|
68
64
|
expect(results).toHaveNoViolations()
|
|
@@ -1,46 +1,57 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { ForwardedRef, forwardRef } from 'react'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
ChangeEventHandler,
|
|
5
|
-
FocusEventHandler,
|
|
6
|
-
ForwardedRef,
|
|
7
|
-
ForwardRefExoticComponent,
|
|
8
|
-
InputHTMLAttributes,
|
|
9
|
-
ReactNode,
|
|
10
|
-
} from 'react'
|
|
11
|
-
import { createComponent, EventName } from '@lit/react'
|
|
12
|
-
import { PktRadioButton as PktElRadioButton } from '@oslokommune/punkt-elements'
|
|
13
|
-
import { PktEventWithTarget } from '@/interfaces/IPktElements'
|
|
14
|
-
|
|
15
|
-
export interface IPktRadioButton extends InputHTMLAttributes<HTMLInputElement> {
|
|
3
|
+
export interface IPktRadioButton extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
16
4
|
id: string
|
|
17
5
|
name: string
|
|
18
6
|
label: string
|
|
19
7
|
hasTile?: boolean
|
|
20
|
-
hasError?: boolean
|
|
21
|
-
defaultChecked?: boolean
|
|
22
8
|
disabled?: boolean
|
|
9
|
+
checkHelptext?: string | React.ReactNode | React.ReactNode[]
|
|
10
|
+
hasError?: boolean
|
|
23
11
|
value?: string
|
|
24
|
-
checkHelptext?: string | ReactNode | ReactNode[]
|
|
25
|
-
|
|
26
|
-
ref?: ForwardedRef<HTMLInputElement>
|
|
27
|
-
onChange?: ChangeEventHandler<HTMLInputElement>
|
|
28
|
-
onInput?: ChangeEventHandler<HTMLInputElement>
|
|
29
|
-
onBlur?: FocusEventHandler<HTMLInputElement>
|
|
30
|
-
onFocus?: FocusEventHandler<HTMLInputElement>
|
|
31
|
-
onValueChange?: (e: CustomEvent) => void
|
|
32
12
|
}
|
|
33
13
|
|
|
34
|
-
export const PktRadioButton =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
14
|
+
export const PktRadioButton = forwardRef(
|
|
15
|
+
(
|
|
16
|
+
{
|
|
17
|
+
id,
|
|
18
|
+
name,
|
|
19
|
+
label,
|
|
20
|
+
className,
|
|
21
|
+
hasTile = false,
|
|
22
|
+
disabled = false,
|
|
23
|
+
checkHelptext,
|
|
24
|
+
hasError = false,
|
|
25
|
+
...props
|
|
26
|
+
}: IPktRadioButton,
|
|
27
|
+
ref: ForwardedRef<HTMLInputElement>,
|
|
28
|
+
): React.ReactElement => {
|
|
29
|
+
const classes = [className, 'pkt-input-check'].filter(Boolean).join(' ')
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className={classes}>
|
|
33
|
+
<div
|
|
34
|
+
className={`pkt-input-check__input ${hasTile ? 'pkt-input-check__input--tile' : ''} ${
|
|
35
|
+
disabled && hasTile ? 'pkt-input-check__input--tile-disabled' : ''
|
|
36
|
+
}`}
|
|
37
|
+
>
|
|
38
|
+
<input
|
|
39
|
+
ref={ref}
|
|
40
|
+
id={id}
|
|
41
|
+
type="radio"
|
|
42
|
+
name={name}
|
|
43
|
+
disabled={disabled}
|
|
44
|
+
className={`pkt-input-check__input-checkbox ${hasError ? 'pkt-input-check__input-checkbox--error' : ''}`}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
<label className="pkt-input-check__input-label" htmlFor={id}>
|
|
48
|
+
{label}
|
|
49
|
+
{checkHelptext && <div className="pkt-input-check__input-helptext">{checkHelptext}</div>}
|
|
50
|
+
</label>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
45
54
|
},
|
|
46
|
-
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
PktRadioButton.displayName = 'PktRadioButton'
|
|
@@ -11,55 +11,49 @@ expect.extend(toHaveNoViolations)
|
|
|
11
11
|
describe('PktSelect', () => {
|
|
12
12
|
test('renders select field with correct props', async () => {
|
|
13
13
|
const { container } = render(
|
|
14
|
-
<PktSelect
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
ariaLabelledby="inputId"
|
|
18
|
-
options={[{ value: 'verdi', label: 'Verdi' }]}
|
|
19
|
-
/>,
|
|
14
|
+
<PktSelect label="Input Label" id="inputIdeas" ariaLabelledby="inputId">
|
|
15
|
+
<option>Oslosommer</option>
|
|
16
|
+
</PktSelect>,
|
|
20
17
|
)
|
|
21
|
-
await window.customElements.whenDefined('pkt-select')
|
|
22
18
|
|
|
23
|
-
const select = container.querySelector('
|
|
19
|
+
const select = container.querySelector('#inputIdeas-input') as HTMLSelectElement | null
|
|
24
20
|
expect(select).toBeInTheDocument()
|
|
25
|
-
expect(select).toHaveAttribute('id', '
|
|
26
|
-
expect(select?.tagName).toBe('
|
|
27
|
-
expect(select?.id).toBe('
|
|
21
|
+
expect(select).toHaveAttribute('id', 'inputIdeas-input')
|
|
22
|
+
expect(select?.tagName).toBe('SELECT')
|
|
23
|
+
expect(select?.id).toBe('inputIdeas-input')
|
|
28
24
|
|
|
29
|
-
const inputWrapper = container.querySelector('pkt-
|
|
25
|
+
const inputWrapper = container.querySelector('.pkt-inputwrapper') as HTMLDivElement | null
|
|
30
26
|
expect(inputWrapper).toBeInTheDocument()
|
|
31
|
-
expect(inputWrapper).
|
|
27
|
+
expect(inputWrapper).toHaveTextContent('Input Label')
|
|
28
|
+
|
|
29
|
+
const label = container.querySelector('label') as HTMLLabelElement | null
|
|
30
|
+
expect(label).toBeInTheDocument()
|
|
31
|
+
expect(label).toHaveAttribute('for', 'inputIdeas-input')
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
test('renders error message when hasError prop is true', async () => {
|
|
35
|
-
const { getByText } = render(
|
|
36
|
-
<PktSelect
|
|
37
|
-
label="Input Label"
|
|
38
|
-
id="inputId"
|
|
39
|
-
hasError
|
|
40
|
-
errorMessage="Input error"
|
|
41
|
-
options={[{ value: 'verdi', label: 'Verdi' }]}
|
|
42
|
-
/>,
|
|
35
|
+
const { getByText, getByRole } = render(
|
|
36
|
+
<PktSelect label="Input Label" id="inputId" hasError errorMessage="Input error" />,
|
|
43
37
|
)
|
|
44
|
-
await window.customElements.whenDefined('pkt-select')
|
|
45
38
|
|
|
39
|
+
await window.customElements.whenDefined('pkt-alert')
|
|
46
40
|
const errorMessage = getByText('Input error')
|
|
47
41
|
expect(errorMessage).toBeInTheDocument()
|
|
48
|
-
|
|
42
|
+
|
|
43
|
+
const alertContainer = errorMessage.closest('pkt-alert')
|
|
44
|
+
expect(alertContainer).toHaveAttribute('id', 'inputId-input-error')
|
|
45
|
+
expect(alertContainer).toHaveTextContent('Input error')
|
|
46
|
+
|
|
47
|
+
const alert = getByRole('alert')
|
|
48
|
+
expect(alert).toBeInTheDocument()
|
|
49
|
+
expect(alert).toHaveAttribute('role', 'alert')
|
|
49
50
|
})
|
|
50
51
|
|
|
51
52
|
describe('PktSelect', () => {
|
|
52
53
|
test('toggles helptext class', async () => {
|
|
53
54
|
const { getByText, container } = render(
|
|
54
|
-
<PktSelect
|
|
55
|
-
label="Input Label"
|
|
56
|
-
id="inputId"
|
|
57
|
-
helptext="Help Text"
|
|
58
|
-
helptextDropdown="Help Text"
|
|
59
|
-
options={[{ value: 'verdi', label: 'Verdi' }]}
|
|
60
|
-
/>,
|
|
55
|
+
<PktSelect label="Input Label" id="inputId" helptext="Help Text" helptextDropdown="Help Text" />,
|
|
61
56
|
)
|
|
62
|
-
await window.customElements.whenDefined('pkt-select')
|
|
63
57
|
|
|
64
58
|
const expandButton = getByText('Les mer').closest('button') as HTMLButtonElement
|
|
65
59
|
const helptextElement = container.querySelector('.pkt-inputwrapper__helptext-expandable-closed')
|
|
@@ -74,10 +68,8 @@ describe('PktSelect', () => {
|
|
|
74
68
|
|
|
75
69
|
describe('accessibility', () => {
|
|
76
70
|
it('renders with no wcag errors with axe', async () => {
|
|
77
|
-
const { container } = render(
|
|
78
|
-
|
|
79
|
-
)
|
|
80
|
-
await window.customElements.whenDefined('pkt-select')
|
|
71
|
+
const { container } = render(<PktSelect label="Input Label" id="inputId" />)
|
|
72
|
+
|
|
81
73
|
const results = await axe(container)
|
|
82
74
|
expect(results).toHaveNoViolations()
|
|
83
75
|
})
|