@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-react",
3
- "version": "12.31.2",
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.31.2",
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.31.0",
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": "bb030d6ba98fa9e45a95cf3092d95b46a750a730"
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'
@@ -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 &&
@@ -25,6 +25,7 @@ type PropObject = {
25
25
  converter?: string
26
26
  reflect?: boolean
27
27
  default?: any
28
+ previewDefault?: any
28
29
  items?: PropObject
29
30
  }
30
31