@oslokommune/punkt-react 12.31.2 → 12.32.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 +19 -0
- package/dist/index.d.ts +17 -0
- package/dist/punkt-react.es.js +19224 -18140
- package/dist/punkt-react.umd.js +686 -472
- package/package.json +5 -4
- package/src/components/combobox/Combobox.test.tsx +219 -0
- package/src/components/combobox/Combobox.tsx +60 -0
- package/src/components/index.ts +1 -0
- package/src/components/interfaces.ts +1 -0
- package/src/components/preview/Preview.tsx +13 -0
- package/src/components/preview/PreviewSpecs.tsx +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslokommune/punkt-react",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.32.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.
|
|
41
|
+
"@oslokommune/punkt-elements": "^12.32.0",
|
|
42
42
|
"angular-html-parser": "^6.0.2",
|
|
43
43
|
"html-format": "^1.1.7",
|
|
44
44
|
"prettier": "^3.3.3",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@oslokommune/punkt-assets": "^12.30.1",
|
|
51
|
-
"@oslokommune/punkt-css": "^12.
|
|
51
|
+
"@oslokommune/punkt-css": "^12.32.0",
|
|
52
|
+
"@oslokommune/punkt-testing-utils": "^12.32.0",
|
|
52
53
|
"@testing-library/jest-dom": "^6.5.0",
|
|
53
54
|
"@testing-library/react": "^16.0.1",
|
|
54
55
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -111,5 +112,5 @@
|
|
|
111
112
|
"url": "https://github.com/oslokommune/punkt/issues"
|
|
112
113
|
},
|
|
113
114
|
"license": "MIT",
|
|
114
|
-
"gitHead": "
|
|
115
|
+
"gitHead": "8d17b2b21d83b498b237d837747e753c64570285"
|
|
115
116
|
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import * as ReactTestingLibrary from '@testing-library/react'
|
|
3
|
+
import { setupPktTestingLibrary } from '@oslokommune/punkt-testing-utils'
|
|
4
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
5
|
+
import { PktCombobox } from './Combobox'
|
|
6
|
+
|
|
7
|
+
const { getPktElementByLabelText, waitForPktElementsToBeDefined } = setupPktTestingLibrary(ReactTestingLibrary)
|
|
8
|
+
|
|
9
|
+
describe('PktCombobox', () => {
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
expect.extend(toHaveNoViolations)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('renders a basic combobox with label', async () => {
|
|
19
|
+
const { container } = ReactTestingLibrary.render(<PktCombobox id="myCombobox" label="My Combobox" allowUserInput />)
|
|
20
|
+
|
|
21
|
+
await waitForPktElementsToBeDefined()
|
|
22
|
+
|
|
23
|
+
// Check if the label is present in the document
|
|
24
|
+
const comboboxLabel = getPktElementByLabelText('My Combobox')
|
|
25
|
+
expect(comboboxLabel).toBeInTheDocument()
|
|
26
|
+
})
|
|
27
|
+
it('renders an error state combobox', async () => {
|
|
28
|
+
const { container } = ReactTestingLibrary.render(<PktCombobox id="myCombobox" label="My Combobox" hasError />)
|
|
29
|
+
|
|
30
|
+
await waitForPktElementsToBeDefined()
|
|
31
|
+
|
|
32
|
+
const comboboxError = getPktElementByLabelText('My Combobox').querySelector('.pkt-combobox__input') as HTMLElement
|
|
33
|
+
|
|
34
|
+
// Verify that the error class is applied to the combobox
|
|
35
|
+
expect(comboboxError).toHaveClass('pkt-combobox__input--error')
|
|
36
|
+
})
|
|
37
|
+
it('renders a disabled combobox', async () => {
|
|
38
|
+
const { container } = ReactTestingLibrary.render(<PktCombobox id="myCombobox" label="My Combobox" disabled />)
|
|
39
|
+
|
|
40
|
+
await waitForPktElementsToBeDefined()
|
|
41
|
+
|
|
42
|
+
const comboboxDisabled = getPktElementByLabelText('My Combobox') as HTMLElement
|
|
43
|
+
|
|
44
|
+
// Verify that the error class is applied to the combobox
|
|
45
|
+
expect(comboboxDisabled).toBeDisabled()
|
|
46
|
+
})
|
|
47
|
+
it('renders a combobox with default value', async () => {
|
|
48
|
+
const { container } = ReactTestingLibrary.render(
|
|
49
|
+
<PktCombobox id="myCombobox" label="My Combobox" defaultValue="Option 1" />,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
await waitForPktElementsToBeDefined()
|
|
53
|
+
|
|
54
|
+
const combobox: HTMLElement = getPktElementByLabelText('My Combobox')
|
|
55
|
+
|
|
56
|
+
// Verify that the default value is set
|
|
57
|
+
expect(combobox).toHaveAttribute('value', 'Option 1')
|
|
58
|
+
})
|
|
59
|
+
it('renders a combobox with options', async () => {
|
|
60
|
+
const { queryAllByRole } = ReactTestingLibrary.render(
|
|
61
|
+
<PktCombobox id="myCombobox" label="My Combobox">
|
|
62
|
+
<option value="Option 1">Option 1</option>
|
|
63
|
+
<option value="Option 2">Option 2</option>
|
|
64
|
+
<option value="Option 3">Option 3</option>
|
|
65
|
+
</PktCombobox>,
|
|
66
|
+
)
|
|
67
|
+
await waitForPktElementsToBeDefined()
|
|
68
|
+
const options = queryAllByRole('option')
|
|
69
|
+
// Verify that the options are rendered correctly
|
|
70
|
+
expect(options).toHaveLength(3)
|
|
71
|
+
expect(options[0].textContent).toContain('Option 1')
|
|
72
|
+
expect(options[1].textContent).toContain('Option 2')
|
|
73
|
+
expect(options[2].textContent).toContain('Option 3')
|
|
74
|
+
})
|
|
75
|
+
it('handles onChange callback', async () => {
|
|
76
|
+
const onChangeMock = jest.fn()
|
|
77
|
+
const { container } = ReactTestingLibrary.render(
|
|
78
|
+
<PktCombobox id="myCombobox" label="My Combobox" onChange={onChangeMock} allowUserInput />,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
await waitForPktElementsToBeDefined()
|
|
82
|
+
|
|
83
|
+
// Get the combobox element
|
|
84
|
+
const combobox = getPktElementByLabelText('My Combobox')
|
|
85
|
+
const comboboxInput = combobox.querySelector('input') as HTMLInputElement
|
|
86
|
+
|
|
87
|
+
ReactTestingLibrary.fireEvent.change(comboboxInput, { target: { value: 'New Value' } })
|
|
88
|
+
|
|
89
|
+
// Verify that the onChange callback is called
|
|
90
|
+
expect(onChangeMock).toHaveBeenCalledTimes(1)
|
|
91
|
+
})
|
|
92
|
+
it('handles onFocus callback', async () => {
|
|
93
|
+
const onFocusMock = jest.fn()
|
|
94
|
+
const { container } = ReactTestingLibrary.render(
|
|
95
|
+
<PktCombobox id="myCombobox" label="My Combobox" onFocus={onFocusMock} allowUserInput />,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
await waitForPktElementsToBeDefined()
|
|
99
|
+
|
|
100
|
+
// Get the combobox element
|
|
101
|
+
const combobox = getPktElementByLabelText('My Combobox')
|
|
102
|
+
const comboboxInput = combobox.querySelector('input') as HTMLInputElement
|
|
103
|
+
|
|
104
|
+
// Simulate a focus event on the combobox
|
|
105
|
+
ReactTestingLibrary.fireEvent.focus(comboboxInput)
|
|
106
|
+
|
|
107
|
+
// Verify that the onFocus callback is called
|
|
108
|
+
expect(onFocusMock).toHaveBeenCalledTimes(1)
|
|
109
|
+
})
|
|
110
|
+
it('handles onBlur callback', async () => {
|
|
111
|
+
const onBlurMock = jest.fn()
|
|
112
|
+
const { container } = ReactTestingLibrary.render(
|
|
113
|
+
<PktCombobox id="myCombobox" label="My Combobox" onBlur={onBlurMock} allowUserInput />,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
await waitForPktElementsToBeDefined()
|
|
117
|
+
|
|
118
|
+
// Get the combobox element
|
|
119
|
+
const combobox = getPktElementByLabelText('My Combobox')
|
|
120
|
+
const comboboxInput = combobox.querySelector('input') as HTMLInputElement
|
|
121
|
+
|
|
122
|
+
// Simulate a blur event on the combobox
|
|
123
|
+
ReactTestingLibrary.fireEvent.blur(comboboxInput)
|
|
124
|
+
|
|
125
|
+
// Verify that the onBlur callback is called
|
|
126
|
+
expect(onBlurMock).toHaveBeenCalled()
|
|
127
|
+
})
|
|
128
|
+
it('should not have any accessibility violations', async () => {
|
|
129
|
+
const { container } = ReactTestingLibrary.render(<PktCombobox id="myCombobox" label="My Combobox" />)
|
|
130
|
+
|
|
131
|
+
await waitForPktElementsToBeDefined()
|
|
132
|
+
|
|
133
|
+
const results = await axe(container)
|
|
134
|
+
|
|
135
|
+
expect(results).toHaveNoViolations()
|
|
136
|
+
})
|
|
137
|
+
it('should not have any accessibility violations with options', async () => {
|
|
138
|
+
const { container } = ReactTestingLibrary.render(
|
|
139
|
+
<PktCombobox id="myCombobox" label="My Combobox">
|
|
140
|
+
<option value="Option 1">Option 1</option>
|
|
141
|
+
<option value="Option 2">Option 2</option>
|
|
142
|
+
<option value="Option 3">Option 3</option>
|
|
143
|
+
</PktCombobox>,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
await waitForPktElementsToBeDefined()
|
|
147
|
+
|
|
148
|
+
const results = await axe(container)
|
|
149
|
+
|
|
150
|
+
expect(results).toHaveNoViolations()
|
|
151
|
+
})
|
|
152
|
+
it('should not have any accessibility violations with options and default value', async () => {
|
|
153
|
+
const { container } = ReactTestingLibrary.render(
|
|
154
|
+
<PktCombobox id="myCombobox" label="My Combobox" defaultValue="Option 1">
|
|
155
|
+
<option value="Option 1">Option 1</option>
|
|
156
|
+
<option value="Option 2">Option 2</option>
|
|
157
|
+
<option value="Option 3">Option 3</option>
|
|
158
|
+
</PktCombobox>,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
await waitForPktElementsToBeDefined()
|
|
162
|
+
|
|
163
|
+
const results = await axe(container)
|
|
164
|
+
|
|
165
|
+
expect(results).toHaveNoViolations()
|
|
166
|
+
})
|
|
167
|
+
it('should not have any accessibility violations with options and default value and error state', async () => {
|
|
168
|
+
const { container } = ReactTestingLibrary.render(
|
|
169
|
+
<PktCombobox id="myCombobox" label="My Combobox" defaultValue="Option 1" hasError>
|
|
170
|
+
<option value="Option 1">Option 1</option>
|
|
171
|
+
<option value="Option 2">Option 2</option>
|
|
172
|
+
<option value="Option 3">Option 3</option>
|
|
173
|
+
</PktCombobox>,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
await waitForPktElementsToBeDefined()
|
|
177
|
+
|
|
178
|
+
const results = await axe(container)
|
|
179
|
+
|
|
180
|
+
expect(results).toHaveNoViolations()
|
|
181
|
+
})
|
|
182
|
+
it('should not have any accessibility violations with options and default value and error state and disabled', async () => {
|
|
183
|
+
const { container } = ReactTestingLibrary.render(
|
|
184
|
+
<PktCombobox id="myCombobox" label="My Combobox" defaultValue="Option 1" hasError disabled>
|
|
185
|
+
<option value="Option 1">Option 1</option>
|
|
186
|
+
<option value="Option 2">Option 2</option>
|
|
187
|
+
<option value="Option 3">Option 3</option>
|
|
188
|
+
</PktCombobox>,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
await waitForPktElementsToBeDefined()
|
|
192
|
+
|
|
193
|
+
const results = await axe(container)
|
|
194
|
+
|
|
195
|
+
expect(results).toHaveNoViolations()
|
|
196
|
+
})
|
|
197
|
+
it('should not have any accessibility violations with options and default value and error state and disabled and placeholder', async () => {
|
|
198
|
+
const { container } = ReactTestingLibrary.render(
|
|
199
|
+
<PktCombobox
|
|
200
|
+
id="myCombobox"
|
|
201
|
+
label="My Combobox"
|
|
202
|
+
defaultValue="Option 1"
|
|
203
|
+
hasError
|
|
204
|
+
disabled
|
|
205
|
+
placeholder="Select an option"
|
|
206
|
+
>
|
|
207
|
+
<option value="Option 1">Option 1</option>
|
|
208
|
+
<option value="Option 2">Option 2</option>
|
|
209
|
+
<option value="Option 3">Option 3</option>
|
|
210
|
+
</PktCombobox>,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
await waitForPktElementsToBeDefined()
|
|
214
|
+
|
|
215
|
+
const results = await axe(container)
|
|
216
|
+
|
|
217
|
+
expect(results).toHaveNoViolations()
|
|
218
|
+
})
|
|
219
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
ForwardRefExoticComponent,
|
|
3
|
+
ReactNode,
|
|
4
|
+
SelectHTMLAttributes,
|
|
5
|
+
ChangeEventHandler,
|
|
6
|
+
FocusEventHandler,
|
|
7
|
+
LegacyRef,
|
|
8
|
+
FC,
|
|
9
|
+
forwardRef,
|
|
10
|
+
} from 'react'
|
|
11
|
+
import { PktCombobox as PktElCombobox } from '@oslokommune/punkt-elements'
|
|
12
|
+
import type { IPktCombobox as IPktElCombobox, IPktComboboxOption } from '@oslokommune/punkt-elements'
|
|
13
|
+
import { createComponent, EventName } from '@lit/react'
|
|
14
|
+
import { PktEventWithTarget } from '@/interfaces/IPktElements'
|
|
15
|
+
|
|
16
|
+
type ExtendedCombobox = Omit<IPktElCombobox, 'helptext'> & SelectHTMLAttributes<HTMLSelectElement>
|
|
17
|
+
|
|
18
|
+
export interface IPktCombobox extends ExtendedCombobox {
|
|
19
|
+
helptext?: string | ReactNode | ReactNode[]
|
|
20
|
+
ref?: LegacyRef<HTMLSelectElement>
|
|
21
|
+
onChange?: ChangeEventHandler<HTMLSelectElement>
|
|
22
|
+
onInput?: ChangeEventHandler<HTMLSelectElement>
|
|
23
|
+
onBlur?: FocusEventHandler<HTMLSelectElement>
|
|
24
|
+
onFocus?: FocusEventHandler<HTMLSelectElement>
|
|
25
|
+
onValueChange?: (e: CustomEvent) => void
|
|
26
|
+
onToggleHelpText?: (e: CustomEvent) => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const LitComponent = createComponent({
|
|
30
|
+
tagName: 'pkt-combobox',
|
|
31
|
+
elementClass: PktElCombobox,
|
|
32
|
+
react: React,
|
|
33
|
+
displayName: 'PktCombobox',
|
|
34
|
+
events: {
|
|
35
|
+
onChange: 'change' as EventName<PktEventWithTarget>,
|
|
36
|
+
onInput: 'input' as EventName<PktEventWithTarget>,
|
|
37
|
+
onBlur: 'blur' as EventName<FocusEvent>,
|
|
38
|
+
onFocus: 'focus' as EventName<FocusEvent>,
|
|
39
|
+
onValueChange: 'valueChange' as EventName<CustomEvent>,
|
|
40
|
+
onToggleHelpText: 'toggleHelpText' as EventName<CustomEvent>,
|
|
41
|
+
},
|
|
42
|
+
}) as ForwardRefExoticComponent<IPktCombobox>
|
|
43
|
+
|
|
44
|
+
// Note:
|
|
45
|
+
// helptext slot needs to be before children because of how React reactivity works.
|
|
46
|
+
// Please do not change this.
|
|
47
|
+
export const PktCombobox: FC<IPktCombobox> = forwardRef(({ children, helptext, ...props }: IPktCombobox, ref) => {
|
|
48
|
+
return (
|
|
49
|
+
<LitComponent {...props} ref={ref}>
|
|
50
|
+
{helptext && (
|
|
51
|
+
<div slot="helptext" className="pkt-contents">
|
|
52
|
+
{helptext}
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
{children}
|
|
56
|
+
</LitComponent>
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
PktCombobox.displayName = 'PktCombobox'
|
package/src/components/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { PktBreadcrumbs } from './breadcrumbs/Breadcrumbs'
|
|
|
6
6
|
export { PktButton } from './button/Button'
|
|
7
7
|
export { PktCard } from './card/Card'
|
|
8
8
|
export { PktCheckbox } from './checkbox/Checkbox'
|
|
9
|
+
export { PktCombobox } from './combobox/Combobox'
|
|
9
10
|
export { PktDatepicker } from './datepicker/Datepicker'
|
|
10
11
|
export { PktFooter } from './footer/Footer'
|
|
11
12
|
export { PktFooterSimple } from './footerSimple/FooterSimple'
|
|
@@ -5,6 +5,7 @@ export type { IPktBackLink } from './backlink/BackLink'
|
|
|
5
5
|
export type { IPktBreadcrumbs } from './breadcrumbs/Breadcrumbs'
|
|
6
6
|
export type { IPktButton } from './button/Button'
|
|
7
7
|
export type { IPktCheckbox } from './checkbox/Checkbox'
|
|
8
|
+
export type { IPktCombobox } from './combobox/Combobox'
|
|
8
9
|
export type { IPktDatepicker } from './datepicker/Datepicker'
|
|
9
10
|
export type { IPktFooter } from './footer/Footer'
|
|
10
11
|
export type { IPktFooterSimple } from './footerSimple/FooterSimple'
|
|
@@ -26,6 +26,19 @@ export const PktPreview: React.FC<PreviewProps> = ({ specs, children, fullWidth
|
|
|
26
26
|
specs.props && typeof specs.props === 'object' && !Array.isArray(specs.props)
|
|
27
27
|
? Object.entries(specs.props).reduce<Record<string, any>>((acc, [key, value]) => {
|
|
28
28
|
if (
|
|
29
|
+
typeof value === 'object' &&
|
|
30
|
+
!Array.isArray(value) &&
|
|
31
|
+
value.previewDefault !== undefined &&
|
|
32
|
+
value.previewDefault !== null
|
|
33
|
+
) {
|
|
34
|
+
if (value.previewDefault === 'false') {
|
|
35
|
+
value.previewDefault = false
|
|
36
|
+
}
|
|
37
|
+
if (value.previewDefault === 'true') {
|
|
38
|
+
value.previewDefault = true
|
|
39
|
+
}
|
|
40
|
+
acc[key] = value.previewDefault
|
|
41
|
+
} else if (
|
|
29
42
|
typeof value === 'object' &&
|
|
30
43
|
!Array.isArray(value) &&
|
|
31
44
|
value.default !== undefined &&
|