@oslokommune/punkt-react 13.6.15 → 13.7.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 +41 -0
- package/dist/index.d.ts +38 -74
- package/dist/punkt-react.es.js +5358 -35971
- package/dist/punkt-react.umd.js +418 -548
- package/package.json +11 -15
- package/src/components/accordion/Accordion.test.tsx +1 -1
- package/src/components/accordion/Accordion.tsx +1 -1
- package/src/components/accordion/AccordionItem.tsx +6 -5
- package/src/components/alert/Alert.tsx +2 -1
- package/src/components/breadcrumbs/Breadcrumbs.test.tsx +16 -4
- package/src/components/breadcrumbs/Breadcrumbs.tsx +3 -2
- package/src/components/button/Button.tsx +3 -2
- package/src/components/checkbox/Checkbox.tsx +4 -3
- package/src/components/datepicker/Datepicker.test.tsx +393 -0
- package/src/components/footer/Footer.tsx +6 -5
- package/src/components/footerSimple/FooterSimple.tsx +4 -3
- package/src/components/icon/Icon.test.tsx +6 -19
- package/src/components/index.ts +0 -2
- package/src/components/input/Input.tsx +4 -3
- package/src/components/radio/RadioButton.tsx +3 -2
- package/src/components/searchinput/SearchInput.tsx +3 -3
- package/src/components/stepper/Stepper.tsx +6 -6
- package/src/components/table/Table.tsx +2 -1
- package/src/components/table/TableBody.tsx +2 -1
- package/src/components/table/TableData.tsx +2 -1
- package/src/components/table/TableDataCell.tsx +2 -1
- package/src/components/table/TableHeader.tsx +2 -1
- package/src/components/table/TableHeaderCell.tsx +2 -1
- package/src/components/table/TableRow.tsx +2 -1
- package/src/components/tag/Tag.tsx +2 -1
- package/src/components/preview/Preview.tsx +0 -274
- package/src/components/preview/PreviewCode.tsx +0 -118
- package/src/components/preview/PreviewPropEditor.tsx +0 -266
- package/src/components/preview/PreviewSpecs.tsx +0 -125
- package/src/components/preview/PreviewWithJson.tsx +0 -519
- package/src/components/preview/preview-types.ts +0 -42
- package/src/components/preview/preview-utils.ts +0 -226
- package/src/components/preview/previewJson/accordion.json +0 -84
- package/src/components/preview/previewJson/alert.json +0 -27
- package/src/components/preview/previewJson/backlink.json +0 -14
- package/src/components/preview/previewJson/breadcrumbs.json +0 -17
- package/src/components/preview/previewJson/button.json +0 -28
- package/src/components/preview/previewJson/card.json +0 -41
- package/src/components/preview/previewJson/checkbox.json +0 -21
- package/src/components/preview/previewJson/combobox.json +0 -24
- package/src/components/preview/previewJson/consent.json +0 -14
- package/src/components/preview/previewJson/datepicker.json +0 -14
- package/src/components/preview/previewJson/footer-simple.json +0 -17
- package/src/components/preview/previewJson/footer.json +0 -29
- package/src/components/preview/previewJson/header.json +0 -34
- package/src/components/preview/previewJson/icon.json +0 -13
- package/src/components/preview/previewJson/input-wrapper.json +0 -39
- package/src/components/preview/previewJson/link.json +0 -28
- package/src/components/preview/previewJson/linkcard.json +0 -30
- package/src/components/preview/previewJson/loader.json +0 -28
- package/src/components/preview/previewJson/messagebox.json +0 -28
- package/src/components/preview/previewJson/modal.json +0 -65
- package/src/components/preview/previewJson/progressbar.json +0 -16
- package/src/components/preview/previewJson/radiobutton.json +0 -21
- package/src/components/preview/previewJson/searchinput.json +0 -20
- package/src/components/preview/previewJson/select.json +0 -73
- package/src/components/preview/previewJson/steps.json +0 -72
- package/src/components/preview/previewJson/table.json +0 -130
- package/src/components/preview/previewJson/tabs.json +0 -33
- package/src/components/preview/previewJson/tag.json +0 -26
- package/src/components/preview/previewJson/textarea.json +0 -18
- package/src/components/preview/previewJson/textinput.json +0 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslokommune/punkt-react",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.7.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",
|
|
@@ -33,12 +33,13 @@
|
|
|
33
33
|
"lint:fix": "eslint --fix 'src/**/*.{jsx,ts,tsx}'",
|
|
34
34
|
"format": "prettier --write src//**/*.{ts,tsx,css} --config ./.prettierrc",
|
|
35
35
|
"preview": "vite preview --outDir dist-app",
|
|
36
|
-
"test": "
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
40
|
"@lit-labs/ssr-dom-shim": "^1.2.1",
|
|
40
41
|
"@lit/react": "^1.0.7",
|
|
41
|
-
"@oslokommune/punkt-elements": "^13.
|
|
42
|
+
"@oslokommune/punkt-elements": "^13.7.0",
|
|
42
43
|
"prettier": "^3.3.3",
|
|
43
44
|
"react-element-to-jsx-string": "^15.0.0",
|
|
44
45
|
"react-hook-form": "^7.53.0",
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
"@testing-library/jest-dom": "^6.5.0",
|
|
52
53
|
"@testing-library/react": "^16.0.1",
|
|
53
54
|
"@testing-library/user-event": "^14.5.2",
|
|
54
|
-
"@types/jest": "^29.5.
|
|
55
|
+
"@types/jest": "^29.5.14",
|
|
55
56
|
"@types/jest-axe": "^3.5.9",
|
|
56
57
|
"@types/node": "^20.12.10",
|
|
57
58
|
"@types/react": "^18.3.5",
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
60
61
|
"@vitejs/plugin-react": "^4.3.1",
|
|
61
62
|
"classnames": "^2.5.1",
|
|
62
|
-
"eslint": "^9.
|
|
63
|
+
"eslint": "^9.37.0",
|
|
63
64
|
"eslint-config-prettier": "^9.1.0",
|
|
64
65
|
"eslint-plugin-prettier": "^5.2.1",
|
|
65
66
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
@@ -67,11 +68,11 @@
|
|
|
67
68
|
"jest-axe": "^9.0.0",
|
|
68
69
|
"jest-environment-jsdom": "^29.7.0",
|
|
69
70
|
"react-hooks": "^1.0.1",
|
|
70
|
-
"react-scripts": "^5.0.1",
|
|
71
71
|
"sass": "^1.78.0",
|
|
72
|
-
"typescript": "^5.
|
|
73
|
-
"vite": "^
|
|
74
|
-
"vite-plugin-dts": "^4.
|
|
72
|
+
"typescript": "^5.9.3",
|
|
73
|
+
"vite": "^6.3.6",
|
|
74
|
+
"vite-plugin-dts": "^4.5.4",
|
|
75
|
+
"vitest": "^3.2.4"
|
|
75
76
|
},
|
|
76
77
|
"peerDependencies": {
|
|
77
78
|
"@oslokommune/punkt-assets": "*",
|
|
@@ -97,11 +98,6 @@
|
|
|
97
98
|
"ux",
|
|
98
99
|
"ui"
|
|
99
100
|
],
|
|
100
|
-
"jest": {
|
|
101
|
-
"transformIgnorePatterns": [
|
|
102
|
-
"<rootDir>/node_modules/(?!@lit/react)"
|
|
103
|
-
]
|
|
104
|
-
},
|
|
105
101
|
"repository": {
|
|
106
102
|
"type": "git",
|
|
107
103
|
"url": "git+https://github.com/oslokommune/punkt.git"
|
|
@@ -110,5 +106,5 @@
|
|
|
110
106
|
"url": "https://github.com/oslokommune/punkt/issues"
|
|
111
107
|
},
|
|
112
108
|
"license": "MIT",
|
|
113
|
-
"gitHead": "
|
|
109
|
+
"gitHead": "cfe27c04046718ff79f60e3babba60062296f05b"
|
|
114
110
|
}
|
|
@@ -3,7 +3,7 @@ import { axe, toHaveNoViolations } from 'jest-axe'
|
|
|
3
3
|
import { PktAccordion } from './Accordion'
|
|
4
4
|
import { PktAccordionItem } from './AccordionItem'
|
|
5
5
|
import { render, screen, fireEvent } from '@testing-library/react'
|
|
6
|
-
import
|
|
6
|
+
import { createRef } from 'react'
|
|
7
7
|
|
|
8
8
|
expect.extend(toHaveNoViolations)
|
|
9
9
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { ReactNode, useState, useEffect, forwardRef } from 'react'
|
|
4
|
+
import type { MouseEvent, SyntheticEvent } from 'react'
|
|
4
5
|
import { PktIcon } from '../icon/Icon'
|
|
5
6
|
import { useAccordionContext } from './Accordion'
|
|
6
7
|
|
|
@@ -16,8 +17,8 @@ export interface IPktAccordionItem {
|
|
|
16
17
|
children?: ReactNode
|
|
17
18
|
name?: string
|
|
18
19
|
className?: string
|
|
19
|
-
onClick?: (e:
|
|
20
|
-
onToggle?: (e:
|
|
20
|
+
onClick?: (e: MouseEvent<HTMLDetailsElement>) => void
|
|
21
|
+
onToggle?: (e: SyntheticEvent<HTMLDetailsElement>) => void
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export const PktAccordionItem = forwardRef<HTMLDetailsElement, IPktAccordionItem>(
|
|
@@ -52,7 +53,7 @@ export const PktAccordionItem = forwardRef<HTMLDetailsElement, IPktAccordionItem
|
|
|
52
53
|
}
|
|
53
54
|
}, [defaultOpen, controlledIsOpen])
|
|
54
55
|
|
|
55
|
-
const handleToggle = (e:
|
|
56
|
+
const handleToggle = (e: SyntheticEvent<HTMLDetailsElement>) => {
|
|
56
57
|
const detailsElement = e.currentTarget
|
|
57
58
|
const newOpenState = detailsElement.open
|
|
58
59
|
|
|
@@ -63,7 +64,7 @@ export const PktAccordionItem = forwardRef<HTMLDetailsElement, IPktAccordionItem
|
|
|
63
64
|
onToggle?.(e)
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
const handleClick = (e:
|
|
67
|
+
const handleClick = (e: MouseEvent<HTMLDetailsElement>) => {
|
|
67
68
|
// La native toggle skje først, så trigge onClick
|
|
68
69
|
setTimeout(() => {
|
|
69
70
|
onClick?.(e)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { FC, ForwardedRef, forwardRef, ReactElement } from 'react'
|
|
4
|
+
import * as React from 'react'
|
|
4
5
|
import { createComponent, EventName } from '@lit/react'
|
|
5
6
|
import { PktAlert as PktElAlert } from '@oslokommune/punkt-elements'
|
|
6
7
|
import type { PktElType, PktElConstructor } from '@/interfaces/IPktElements'
|
|
@@ -6,6 +6,18 @@ import { PktBreadcrumbs } from './Breadcrumbs'
|
|
|
6
6
|
|
|
7
7
|
expect.extend(toHaveNoViolations)
|
|
8
8
|
|
|
9
|
+
// Create a Router with future flags to suppress warnings
|
|
10
|
+
const TestRouter = ({ children }: { children: React.ReactNode }) => (
|
|
11
|
+
<Router
|
|
12
|
+
future={{
|
|
13
|
+
v7_startTransition: true,
|
|
14
|
+
v7_relativeSplatPath: true,
|
|
15
|
+
}}
|
|
16
|
+
>
|
|
17
|
+
{children}
|
|
18
|
+
</Router>
|
|
19
|
+
)
|
|
20
|
+
|
|
9
21
|
describe('PktBreadcrumbs Component', () => {
|
|
10
22
|
const breadcrumbs = [
|
|
11
23
|
{ text: 'Home', href: '/' },
|
|
@@ -15,9 +27,9 @@ describe('PktBreadcrumbs Component', () => {
|
|
|
15
27
|
|
|
16
28
|
it('renders breadcrumbs correctly with react-router navigation', async () => {
|
|
17
29
|
const { getAllByText } = render(
|
|
18
|
-
<
|
|
30
|
+
<TestRouter>
|
|
19
31
|
<PktBreadcrumbs breadcrumbs={breadcrumbs} navigationType="router" />
|
|
20
|
-
</
|
|
32
|
+
</TestRouter>,
|
|
21
33
|
)
|
|
22
34
|
await window.customElements.whenDefined('pkt-icon')
|
|
23
35
|
|
|
@@ -37,9 +49,9 @@ describe('PktBreadcrumbs Component', () => {
|
|
|
37
49
|
|
|
38
50
|
it('renders back link correctly on mobile with router navigation', async () => {
|
|
39
51
|
const { getAllByText } = render(
|
|
40
|
-
<
|
|
52
|
+
<TestRouter>
|
|
41
53
|
<PktBreadcrumbs breadcrumbs={breadcrumbs} navigationType="router" />
|
|
42
|
-
</
|
|
54
|
+
</TestRouter>,
|
|
43
55
|
)
|
|
44
56
|
await window.customElements.whenDefined('pkt-icon')
|
|
45
57
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { ForwardedRef, forwardRef } from 'react'
|
|
4
|
+
import type { AnchorHTMLAttributes } from 'react'
|
|
4
5
|
import { Link } from 'react-router-dom'
|
|
5
6
|
|
|
6
7
|
import PktIcon from '../icon/Icon'
|
|
@@ -10,7 +11,7 @@ export interface IBreadcrumbs {
|
|
|
10
11
|
href: string
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
export interface IPktBreadcrumbs extends
|
|
14
|
+
export interface IPktBreadcrumbs extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
14
15
|
breadcrumbs: IBreadcrumbs[]
|
|
15
16
|
navigationType?: 'router' | 'anchor'
|
|
16
17
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
import
|
|
2
|
+
import { ForwardedRef, forwardRef } from 'react'
|
|
3
|
+
import type { ButtonHTMLAttributes } from 'react'
|
|
3
4
|
|
|
4
5
|
import { PktIcon } from '../icon/Icon'
|
|
5
6
|
declare global {
|
|
@@ -10,7 +11,7 @@ declare global {
|
|
|
10
11
|
|
|
11
12
|
window.pktAnimationPath = window.pktAnimationPath || 'https://punkt-cdn.oslo.kommune.no/latest/animations/'
|
|
12
13
|
|
|
13
|
-
export interface IPktButton extends
|
|
14
|
+
export interface IPktButton extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
14
15
|
iconName?: string
|
|
15
16
|
secondIconName?: string
|
|
16
17
|
mode?: 'light' | 'dark'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ChangeEventHandler, ForwardedRef, forwardRef, ReactNode, ReactElement } from 'react'
|
|
2
|
+
import type { InputHTMLAttributes } from 'react'
|
|
2
3
|
|
|
3
|
-
export interface IPktCheckbox extends
|
|
4
|
+
export interface IPktCheckbox extends InputHTMLAttributes<HTMLInputElement> {
|
|
4
5
|
id: string
|
|
5
6
|
hasTile?: boolean
|
|
6
7
|
disabled?: boolean
|
|
@@ -44,7 +45,7 @@ export const PktCheckbox = forwardRef(
|
|
|
44
45
|
...props
|
|
45
46
|
}: IPktCheckbox,
|
|
46
47
|
ref: ForwardedRef<HTMLInputElement>,
|
|
47
|
-
):
|
|
48
|
+
): ReactElement => {
|
|
48
49
|
const classes = [className, 'pkt-input-check'].filter(Boolean).join(' ')
|
|
49
50
|
|
|
50
51
|
const labelClasses = [
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { fireEvent, render, waitFor } from '@testing-library/react'
|
|
3
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
4
|
+
import { PktDatepicker, IPktDatepicker } from './Datepicker'
|
|
5
|
+
|
|
6
|
+
const datePickerId = 'datepickerId'
|
|
7
|
+
const label = 'Date Picker Label'
|
|
8
|
+
|
|
9
|
+
const waitForDatepicker = async () => {
|
|
10
|
+
await window.customElements.whenDefined('pkt-datepicker')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const waitForCustomElementUpdate = async () => {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
requestAnimationFrame(resolve)
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const createDatepickerTest = (props: Partial<IPktDatepicker> = {}) => {
|
|
20
|
+
const defaultProps: IPktDatepicker = {
|
|
21
|
+
label,
|
|
22
|
+
id: datePickerId,
|
|
23
|
+
...props,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return render(<PktDatepicker {...defaultProps} />)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('PktDatepicker - React Form Element', () => {
|
|
30
|
+
beforeEach(async () => {
|
|
31
|
+
await waitForDatepicker()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
expect.extend(toHaveNoViolations)
|
|
35
|
+
|
|
36
|
+
describe('Single date selection', () => {
|
|
37
|
+
test('sets and confirms a single date value', async () => {
|
|
38
|
+
const handleChange = jest.fn()
|
|
39
|
+
const handleValueChange = jest.fn()
|
|
40
|
+
const { container } = createDatepickerTest({
|
|
41
|
+
onChange: handleChange,
|
|
42
|
+
onValueChange: handleValueChange,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
await waitFor(() => {
|
|
46
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
47
|
+
expect(input).toBeInTheDocument()
|
|
48
|
+
|
|
49
|
+
fireEvent.change(input, { target: { value: '2024-06-15' } })
|
|
50
|
+
fireEvent.blur(input)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
await waitFor(() => {
|
|
54
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
55
|
+
expect(input.value).toBe('2024-06-15')
|
|
56
|
+
|
|
57
|
+
expect(handleChange).toHaveBeenCalledWith(
|
|
58
|
+
expect.objectContaining({
|
|
59
|
+
target: expect.objectContaining({
|
|
60
|
+
value: '2024-06-15',
|
|
61
|
+
}),
|
|
62
|
+
}),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
expect(handleValueChange).toHaveBeenCalledWith(
|
|
66
|
+
expect.objectContaining({
|
|
67
|
+
detail: '2024-06-15',
|
|
68
|
+
}),
|
|
69
|
+
)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('sets date via calendar interaction', async () => {
|
|
74
|
+
const handleValueChange = jest.fn()
|
|
75
|
+
const { container } = createDatepickerTest({
|
|
76
|
+
onValueChange: handleValueChange,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
await waitFor(() => {
|
|
80
|
+
const calendarButton = container.querySelector('button[type="button"]')
|
|
81
|
+
expect(calendarButton).toBeInTheDocument()
|
|
82
|
+
fireEvent.click(calendarButton!)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
await waitFor(() => {
|
|
86
|
+
const calendar = container.querySelector('pkt-calendar')
|
|
87
|
+
expect(calendar).toBeInTheDocument()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
await waitFor(() => {
|
|
91
|
+
const dateButtons = container.querySelectorAll('pkt-calendar button[data-date]')
|
|
92
|
+
const firstDateButton = Array.from(dateButtons).find(
|
|
93
|
+
(btn) => btn.textContent && btn.textContent.trim() && !btn.hasAttribute('disabled'),
|
|
94
|
+
) as HTMLButtonElement
|
|
95
|
+
|
|
96
|
+
if (firstDateButton) {
|
|
97
|
+
fireEvent.click(firstDateButton)
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
await waitForCustomElementUpdate()
|
|
102
|
+
|
|
103
|
+
await waitFor(() => {
|
|
104
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
105
|
+
expect(input.value).not.toBe('')
|
|
106
|
+
expect(handleValueChange).toHaveBeenCalled()
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('sets date via keyboard input', async () => {
|
|
111
|
+
const handleValueChange = jest.fn()
|
|
112
|
+
const { container } = createDatepickerTest({
|
|
113
|
+
onValueChange: handleValueChange,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
118
|
+
expect(input).toBeInTheDocument()
|
|
119
|
+
|
|
120
|
+
fireEvent.change(input, { target: { value: '2025-10-15' } })
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
await waitForCustomElementUpdate()
|
|
124
|
+
|
|
125
|
+
await waitFor(() => {
|
|
126
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
127
|
+
expect(input.value).toBe('2025-10-15')
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('has no accessibility violations with single date set', async () => {
|
|
132
|
+
const { container } = createDatepickerTest({
|
|
133
|
+
value: '2024-06-15',
|
|
134
|
+
label: 'Select Date',
|
|
135
|
+
helptext: 'Choose a date from the calendar',
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
await waitForCustomElementUpdate()
|
|
139
|
+
|
|
140
|
+
await waitFor(() => {
|
|
141
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
142
|
+
expect(input.value).toBe('2024-06-15')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
const results = await axe(container)
|
|
146
|
+
expect(results).toHaveNoViolations()
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
describe('Multiple date selection', () => {
|
|
151
|
+
test('sets and confirms multiple date values', async () => {
|
|
152
|
+
const handleValueChange = jest.fn()
|
|
153
|
+
const { container } = createDatepickerTest({
|
|
154
|
+
multiple: true,
|
|
155
|
+
value: ['2024-06-15', '2024-06-20'],
|
|
156
|
+
onValueChange: handleValueChange,
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
await waitFor(() => {
|
|
160
|
+
const datepicker = container.querySelector('pkt-datepicker')
|
|
161
|
+
expect(datepicker).toHaveAttribute('multiple')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
await waitForCustomElementUpdate()
|
|
165
|
+
|
|
166
|
+
await waitFor(() => {
|
|
167
|
+
const tags = container.querySelectorAll('pkt-tag')
|
|
168
|
+
expect(tags.length).toBe(2)
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test('dispatches onChange and onValueChange for multiple dates', async () => {
|
|
173
|
+
const handleChange = jest.fn()
|
|
174
|
+
const handleValueChange = jest.fn()
|
|
175
|
+
const dates = ['2024-06-15', '2024-06-20']
|
|
176
|
+
|
|
177
|
+
const { container } = createDatepickerTest({
|
|
178
|
+
multiple: true,
|
|
179
|
+
value: dates,
|
|
180
|
+
onChange: handleChange,
|
|
181
|
+
onValueChange: handleValueChange,
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
await waitForCustomElementUpdate()
|
|
185
|
+
|
|
186
|
+
await waitFor(() => {
|
|
187
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
188
|
+
fireEvent.change(input, { target: { value: '2024-06-25' } })
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
await waitForCustomElementUpdate()
|
|
192
|
+
|
|
193
|
+
await waitFor(() => {
|
|
194
|
+
expect(handleChange).toHaveBeenCalledWith(
|
|
195
|
+
expect.objectContaining({
|
|
196
|
+
target: expect.objectContaining({
|
|
197
|
+
tagName: 'PKT-DATEPICKER',
|
|
198
|
+
value: '2024-06-15,2024-06-20',
|
|
199
|
+
}),
|
|
200
|
+
}),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
expect(handleValueChange).toHaveBeenCalledWith(
|
|
204
|
+
expect.objectContaining({
|
|
205
|
+
detail: ['2024-06-15', '2024-06-20'],
|
|
206
|
+
}),
|
|
207
|
+
)
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('has no accessibility violations with multiple dates set', async () => {
|
|
212
|
+
const { container } = createDatepickerTest({
|
|
213
|
+
multiple: true,
|
|
214
|
+
value: ['2024-06-15', '2024-06-20', '2024-06-25'],
|
|
215
|
+
label: 'Select Multiple Dates',
|
|
216
|
+
helptext: 'Choose multiple dates from the calendar',
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
await waitForCustomElementUpdate()
|
|
220
|
+
|
|
221
|
+
await waitFor(() => {
|
|
222
|
+
const tags = container.querySelectorAll('pkt-tag')
|
|
223
|
+
expect(tags.length).toBe(3)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
const results = await axe(container)
|
|
227
|
+
expect(results).toHaveNoViolations()
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
describe('Range date selection', () => {
|
|
232
|
+
test('sets and confirms range date values', async () => {
|
|
233
|
+
const handleValueChange = jest.fn()
|
|
234
|
+
const { container } = createDatepickerTest({
|
|
235
|
+
range: true,
|
|
236
|
+
value: ['2024-06-15', '2024-06-25'],
|
|
237
|
+
onValueChange: handleValueChange,
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
await waitFor(() => {
|
|
241
|
+
const datepicker = container.querySelector('pkt-datepicker')
|
|
242
|
+
expect(datepicker).toHaveAttribute('range')
|
|
243
|
+
const rangeInputs = container.querySelectorAll('input[type="date"]')
|
|
244
|
+
expect(rangeInputs.length).toBe(2)
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('has no accessibility violations with range dates set', async () => {
|
|
249
|
+
const { container } = createDatepickerTest({
|
|
250
|
+
range: true,
|
|
251
|
+
value: ['2024-06-15', '2024-06-25'],
|
|
252
|
+
label: 'Select Date Range',
|
|
253
|
+
helptext: 'Choose start and end dates for your range',
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
await waitForCustomElementUpdate()
|
|
257
|
+
|
|
258
|
+
await waitFor(() => {
|
|
259
|
+
const datepicker = container.querySelector('pkt-datepicker')
|
|
260
|
+
expect(datepicker).toHaveAttribute('range')
|
|
261
|
+
expect(datepicker).toHaveAttribute('value', '2024-06-15,2024-06-25')
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
const results = await axe(container)
|
|
265
|
+
expect(results).toHaveNoViolations()
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
describe('Form integration', () => {
|
|
270
|
+
test('works as a form element with name attribute', async () => {
|
|
271
|
+
const { container } = createDatepickerTest({
|
|
272
|
+
name: 'dateField',
|
|
273
|
+
value: '2024-06-15',
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
await waitFor(() => {
|
|
277
|
+
const datepicker = container.querySelector('pkt-datepicker')
|
|
278
|
+
expect(datepicker).toHaveAttribute('name', 'dateField')
|
|
279
|
+
expect(datepicker).toHaveAttribute('value', '2024-06-15')
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
test('handles required validation', async () => {
|
|
284
|
+
const { container } = createDatepickerTest({
|
|
285
|
+
required: true,
|
|
286
|
+
name: 'requiredDate',
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
await waitFor(() => {
|
|
290
|
+
const datepicker = container.querySelector('pkt-datepicker')
|
|
291
|
+
expect(datepicker).toHaveAttribute('required')
|
|
292
|
+
expect(datepicker).toHaveAttribute('name', 'requiredDate')
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
test('handles disabled state', async () => {
|
|
297
|
+
const { container } = createDatepickerTest({
|
|
298
|
+
disabled: true,
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
await waitFor(() => {
|
|
302
|
+
const datepicker = container.querySelector('pkt-datepicker')
|
|
303
|
+
expect(datepicker).toHaveAttribute('disabled')
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
test('sets min and max date boundaries', async () => {
|
|
308
|
+
const { container } = createDatepickerTest({
|
|
309
|
+
min: '2024-01-01',
|
|
310
|
+
max: '2024-12-31',
|
|
311
|
+
value: '2024-06-15',
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
await waitFor(() => {
|
|
315
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
316
|
+
expect(input).toBeInTheDocument()
|
|
317
|
+
expect(input).toHaveAttribute('min', '2024-01-01')
|
|
318
|
+
expect(input).toHaveAttribute('max', '2024-12-31')
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
describe('Event handling', () => {
|
|
324
|
+
test('handles input value changes for form behavior', async () => {
|
|
325
|
+
const { container } = createDatepickerTest({})
|
|
326
|
+
|
|
327
|
+
await waitFor(() => {
|
|
328
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
329
|
+
|
|
330
|
+
fireEvent.change(input, { target: { value: '2024-07-20' } })
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
await waitForCustomElementUpdate()
|
|
334
|
+
|
|
335
|
+
await waitFor(() => {
|
|
336
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
337
|
+
expect(input.value).toBe('2024-07-20')
|
|
338
|
+
|
|
339
|
+
const datepicker = container.querySelector('pkt-datepicker')
|
|
340
|
+
expect(datepicker).toBeInTheDocument()
|
|
341
|
+
})
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
test('triggers focus and blur events', async () => {
|
|
345
|
+
const handleFocus = jest.fn()
|
|
346
|
+
const handleBlur = jest.fn()
|
|
347
|
+
const { container } = createDatepickerTest({
|
|
348
|
+
onFocus: handleFocus,
|
|
349
|
+
onBlur: handleBlur,
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
await waitFor(() => {
|
|
353
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
354
|
+
|
|
355
|
+
fireEvent.focus(input)
|
|
356
|
+
fireEvent.blur(input)
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
await waitFor(() => {
|
|
360
|
+
expect(handleFocus).toHaveBeenCalled()
|
|
361
|
+
expect(handleBlur).toHaveBeenCalled()
|
|
362
|
+
})
|
|
363
|
+
})
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
describe('Accessibility and form behavior', () => {
|
|
367
|
+
test('has proper label association for screen readers', async () => {
|
|
368
|
+
const { container } = createDatepickerTest({
|
|
369
|
+
label: 'Select your birthday',
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
await waitFor(() => {
|
|
373
|
+
const label = container.querySelector('label')
|
|
374
|
+
const input = container.querySelector('input[type="date"]')
|
|
375
|
+
|
|
376
|
+
expect(label).toHaveTextContent('Select your birthday')
|
|
377
|
+
expect(label).toHaveAttribute('for', expect.stringContaining('input'))
|
|
378
|
+
expect(input).toHaveAttribute('id', expect.stringContaining('input'))
|
|
379
|
+
})
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
test('supports help text for user guidance', async () => {
|
|
383
|
+
const { container } = createDatepickerTest({
|
|
384
|
+
helptext: 'Please select a date in the format DD.MM.YYYY',
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
await waitFor(() => {
|
|
388
|
+
const helpText = container.querySelector('[id*="helptext"]')
|
|
389
|
+
expect(helpText).toBeInTheDocument()
|
|
390
|
+
})
|
|
391
|
+
})
|
|
392
|
+
})
|
|
393
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { FC, Fragment } from 'react'
|
|
4
|
+
import type { HTMLAttributes } from 'react'
|
|
4
5
|
|
|
5
6
|
import { PktIcon } from '../icon/Icon'
|
|
6
7
|
import { PktConsent } from '../consent/Consent'
|
|
@@ -25,7 +26,7 @@ interface SocialLink {
|
|
|
25
26
|
openInNewTab?: boolean
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
export interface IPktFooter extends
|
|
29
|
+
export interface IPktFooter extends HTMLAttributes<HTMLDivElement> {
|
|
29
30
|
columnOne: Column
|
|
30
31
|
columnTwo: Column
|
|
31
32
|
socialLinks?: SocialLink[]
|
|
@@ -42,7 +43,7 @@ export interface IPktFooter extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
42
43
|
onToggleConsent?: (e: CustomEvent) => void
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
export const PktFooter:
|
|
46
|
+
export const PktFooter: FC<IPktFooter> = ({
|
|
46
47
|
columnOne,
|
|
47
48
|
columnTwo,
|
|
48
49
|
socialLinks,
|
|
@@ -169,7 +170,7 @@ export const PktFooter: React.FC<IPktFooter> = ({
|
|
|
169
170
|
{socialLinks
|
|
170
171
|
.filter((link) => link.iconName)
|
|
171
172
|
.map((link, index) => (
|
|
172
|
-
<
|
|
173
|
+
<Fragment key={`sociallinks-${index}`}>
|
|
173
174
|
<a
|
|
174
175
|
href={link.href}
|
|
175
176
|
aria-label={`til ${link.iconName}`}
|
|
@@ -179,7 +180,7 @@ export const PktFooter: React.FC<IPktFooter> = ({
|
|
|
179
180
|
>
|
|
180
181
|
<PktIcon className="pkt-footer__social-icon" name={link.iconName} />
|
|
181
182
|
</a>
|
|
182
|
-
</
|
|
183
|
+
</Fragment>
|
|
183
184
|
))}
|
|
184
185
|
</div>
|
|
185
186
|
</div>
|