@oslokommune/punkt-react 12.14.2 → 12.15.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 +39 -0
- package/dist/index.d.ts +10 -4
- package/dist/punkt-react.es.js +30175 -9589
- package/dist/punkt-react.umd.js +563 -286
- package/package.json +4 -4
- package/src/components/preview/Preview.tsx +3 -1
- package/src/components/preview/PreviewPropEditor.tsx +6 -6
- package/src/components/preview/PreviewSpecs.tsx +2 -2
- package/src/components/textinput/Textinput.test.tsx +13 -8
- package/src/components/textinput/Textinput.tsx +23 -112
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslokommune/punkt-react",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.15.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.15.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,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@oslokommune/punkt-assets": "^12.14.0",
|
|
51
|
-
"@oslokommune/punkt-css": "^12.
|
|
51
|
+
"@oslokommune/punkt-css": "^12.15.0",
|
|
52
52
|
"@testing-library/jest-dom": "^6.5.0",
|
|
53
53
|
"@testing-library/react": "^16.0.1",
|
|
54
54
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -111,5 +111,5 @@
|
|
|
111
111
|
"url": "https://github.com/oslokommune/punkt/issues"
|
|
112
112
|
},
|
|
113
113
|
"license": "MIT",
|
|
114
|
-
"gitHead": "
|
|
114
|
+
"gitHead": "31e12e08dcb69c897c9cfb553e3364efe5e340a1"
|
|
115
115
|
}
|
|
@@ -42,7 +42,7 @@ export const PktPreview: React.FC<PreviewProps> = ({ specs, children, fullWidth
|
|
|
42
42
|
return acc
|
|
43
43
|
}, {})
|
|
44
44
|
: {}
|
|
45
|
-
|
|
45
|
+
const [iteratedKey, setIteratedKey] = useState(0)
|
|
46
46
|
const [props, setProps] = useState(initialProps)
|
|
47
47
|
const [mode, setMode] = useState<'light' | 'dark'>('light')
|
|
48
48
|
const [htmlContent, setHtmlContent] = useState('')
|
|
@@ -153,6 +153,7 @@ export const PktPreview: React.FC<PreviewProps> = ({ specs, children, fullWidth
|
|
|
153
153
|
)
|
|
154
154
|
|
|
155
155
|
const handleChange = (key: string, value: any, displayAsFalse: boolean = false) => {
|
|
156
|
+
setIteratedKey(iteratedKey + 1)
|
|
156
157
|
if (!displayAsFalse && (!value || value == 'false')) {
|
|
157
158
|
const { [key]: _, ...rest } = props
|
|
158
159
|
setProps(rest)
|
|
@@ -183,6 +184,7 @@ export const PktPreview: React.FC<PreviewProps> = ({ specs, children, fullWidth
|
|
|
183
184
|
<div
|
|
184
185
|
className={`pkt-preview__component ${fullWidth && 'pkt-preview__component--fullwidth'}`}
|
|
185
186
|
ref={previewComponent}
|
|
187
|
+
key={iteratedKey}
|
|
186
188
|
>
|
|
187
189
|
<div>{component}</div>
|
|
188
190
|
</div>
|
|
@@ -41,7 +41,7 @@ export const PktPreviewPropEditor: FC<PropEditorProps> = ({ propKey, spec, props
|
|
|
41
41
|
id={propKey}
|
|
42
42
|
label={propKey}
|
|
43
43
|
type="checkbox"
|
|
44
|
-
checked={props[propKey]}
|
|
44
|
+
checked={props[propKey] || false}
|
|
45
45
|
onChange={(e) => handleChange(propKey, e.target.checked, spec.displayAsFalse)}
|
|
46
46
|
labelPosition="right"
|
|
47
47
|
isSwitch
|
|
@@ -56,7 +56,7 @@ export const PktPreviewPropEditor: FC<PropEditorProps> = ({ propKey, spec, props
|
|
|
56
56
|
label={spec.name || propKey}
|
|
57
57
|
helptext={spec.description || null}
|
|
58
58
|
id={propKey}
|
|
59
|
-
value={props[propKey]}
|
|
59
|
+
value={props[propKey] || ''}
|
|
60
60
|
onChange={(e) => handleChange(propKey, e.target.value)}
|
|
61
61
|
requiredTag={spec.required}
|
|
62
62
|
>
|
|
@@ -76,8 +76,8 @@ export const PktPreviewPropEditor: FC<PropEditorProps> = ({ propKey, spec, props
|
|
|
76
76
|
id={propKey}
|
|
77
77
|
label={spec.name || propKey}
|
|
78
78
|
helptext={spec.description || null}
|
|
79
|
-
value={props[propKey]}
|
|
80
|
-
multiple={spec.variant === 'multiple'}
|
|
79
|
+
value={props[propKey] || ''}
|
|
80
|
+
multiple={!!(spec.variant === 'multiple')}
|
|
81
81
|
maxlength={999}
|
|
82
82
|
onChange={(e) => handleChange(propKey, (e.target as HTMLInputElement).value)}
|
|
83
83
|
requiredTag={spec.required}
|
|
@@ -113,7 +113,7 @@ export const PktPreviewPropEditor: FC<PropEditorProps> = ({ propKey, spec, props
|
|
|
113
113
|
label={spec.name || propKey}
|
|
114
114
|
helptext={spec.description || null}
|
|
115
115
|
type="number"
|
|
116
|
-
value={props[propKey]}
|
|
116
|
+
value={props[propKey] || ''}
|
|
117
117
|
onChange={(e) => handleChange(propKey, parseFloat(e.currentTarget.value))}
|
|
118
118
|
requiredTag={spec.required}
|
|
119
119
|
/>
|
|
@@ -147,7 +147,7 @@ export const PktPreviewPropEditor: FC<PropEditorProps> = ({ propKey, spec, props
|
|
|
147
147
|
label={spec.name || propKey}
|
|
148
148
|
helptext={spec.description || null}
|
|
149
149
|
type="text"
|
|
150
|
-
value={props[propKey]}
|
|
150
|
+
value={props[propKey] || ''}
|
|
151
151
|
onChange={(e) =>
|
|
152
152
|
handleChange(propKey, spec.type === 'number' ? parseFloat(e.currentTarget.value) : e.currentTarget.value)
|
|
153
153
|
}
|
|
@@ -49,8 +49,8 @@ export const PktPreviewSpecs: React.FC<{ specs: ISpecObject }> = ({ specs }) =>
|
|
|
49
49
|
<PktTable compact>
|
|
50
50
|
<PktTableHeader>
|
|
51
51
|
<PktTableRow>
|
|
52
|
-
<PktTableHeaderCell>Navn</PktTableHeaderCell>
|
|
53
52
|
<PktTableHeaderCell>Prop</PktTableHeaderCell>
|
|
53
|
+
<PktTableHeaderCell>Navn</PktTableHeaderCell>
|
|
54
54
|
<PktTableHeaderCell>Beskrivelse</PktTableHeaderCell>
|
|
55
55
|
<PktTableHeaderCell>Type</PktTableHeaderCell>
|
|
56
56
|
<PktTableHeaderCell>Standardverdi</PktTableHeaderCell>
|
|
@@ -67,7 +67,7 @@ export const PktPreviewSpecs: React.FC<{ specs: ISpecObject }> = ({ specs }) =>
|
|
|
67
67
|
<>
|
|
68
68
|
<PktTableDataCell dataLabel="Beskrivelse"></PktTableDataCell>
|
|
69
69
|
<PktTableDataCell dataLabel="Type">
|
|
70
|
-
<pre>{value.join('
|
|
70
|
+
<pre>{value.join('\n')}</pre>
|
|
71
71
|
</PktTableDataCell>
|
|
72
72
|
</>
|
|
73
73
|
) : (
|
|
@@ -9,35 +9,39 @@ import { PktTextinput } from './Textinput'
|
|
|
9
9
|
expect.extend(toHaveNoViolations)
|
|
10
10
|
|
|
11
11
|
describe('PktTextinput', () => {
|
|
12
|
-
test('renders input field with correct props', () => {
|
|
12
|
+
test('renders input field with correct props', async () => {
|
|
13
13
|
const { getByLabelText } = render(<PktTextinput label="Input Label" id="inputId" />)
|
|
14
|
+
await window.customElements.whenDefined('pkt-textinput')
|
|
14
15
|
|
|
15
16
|
const inputElement = getByLabelText('Input Label')
|
|
16
17
|
expect(inputElement).toBeInTheDocument()
|
|
17
18
|
expect(inputElement.tagName).toBe('INPUT')
|
|
18
|
-
expect(inputElement.id).toBe('inputId')
|
|
19
|
+
expect(inputElement.id).toBe('inputId-input')
|
|
19
20
|
})
|
|
20
21
|
|
|
21
|
-
test('renders error message when hasError prop is true', () => {
|
|
22
|
+
test('renders error message when hasError prop is true', async () => {
|
|
22
23
|
const { getByText } = render(<PktTextinput label="Input Label" id="inputId" hasError errorMessage="Input error" />)
|
|
24
|
+
await window.customElements.whenDefined('pkt-textinput')
|
|
25
|
+
|
|
23
26
|
const errorMessage = getByText('Input error')
|
|
24
27
|
expect(errorMessage).toBeInTheDocument()
|
|
25
|
-
expect(errorMessage.closest('pkt-alert')).toHaveAttribute('id', 'inputId-error')
|
|
28
|
+
expect(errorMessage.closest('.pkt-alert')).toHaveAttribute('id', 'inputId-input-error')
|
|
26
29
|
})
|
|
27
30
|
|
|
28
31
|
describe('PktTextinput', () => {
|
|
29
|
-
test('toggles helptext class', () => {
|
|
32
|
+
test('toggles helptext class', async () => {
|
|
30
33
|
const { getByText, container } = render(
|
|
31
34
|
<PktTextinput label="Input Label" id="inputId" helptext="Help Text" helptextDropdown="Help Text" />,
|
|
32
35
|
)
|
|
36
|
+
await window.customElements.whenDefined('pkt-textinput')
|
|
33
37
|
|
|
34
|
-
const expandButton = getByText('Les mer')
|
|
38
|
+
const expandButton = getByText('Les mer').closest('button') as HTMLButtonElement
|
|
35
39
|
const helptextElement = container.querySelector('.pkt-inputwrapper__helptext-expandable-closed')
|
|
36
40
|
|
|
37
|
-
fireEvent.click(expandButton)
|
|
41
|
+
await fireEvent.click(expandButton)
|
|
38
42
|
expect(helptextElement).toHaveClass('pkt-inputwrapper__helptext-expandable-open')
|
|
39
43
|
|
|
40
|
-
fireEvent.click(expandButton)
|
|
44
|
+
await fireEvent.click(expandButton)
|
|
41
45
|
expect(helptextElement).toHaveClass('pkt-inputwrapper__helptext-expandable-closed')
|
|
42
46
|
})
|
|
43
47
|
})
|
|
@@ -45,6 +49,7 @@ describe('PktTextinput', () => {
|
|
|
45
49
|
describe('accessibility', () => {
|
|
46
50
|
it('renders with no wcag errors with axe', async () => {
|
|
47
51
|
const { container } = render(<PktTextinput label="Input Label" id="inputId" />)
|
|
52
|
+
await window.customElements.whenDefined('pkt-textinput')
|
|
48
53
|
const results = await axe(container)
|
|
49
54
|
expect(results).toHaveNoViolations()
|
|
50
55
|
})
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { ForwardRefExoticComponent, LegacyRef, ChangeEventHandler, FocusEventHandler, ReactNode } from 'react'
|
|
2
|
+
import { createComponent, EventName } from '@lit/react'
|
|
3
|
+
import { PktTextinput as PktEl } from '@oslokommune/punkt-elements'
|
|
4
|
+
import type { PktElType, PktEventWithTarget } from '@/interfaces/IPktElements'
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
import { PktInputWrapper } from '../inputwrapper/InputWrapper'
|
|
5
|
-
|
|
6
|
-
export interface IPktTextinput extends InputHTMLAttributes<HTMLInputElement> {
|
|
6
|
+
export interface IPktTextinput extends Omit<PktElType, 'ref'> {
|
|
7
7
|
id: string
|
|
8
|
-
ariaDescribedby?: string
|
|
9
8
|
ariaLabelledby?: string
|
|
10
9
|
autocomplete?: string
|
|
11
10
|
counter?: boolean
|
|
@@ -32,112 +31,24 @@ export interface IPktTextinput extends InputHTMLAttributes<HTMLInputElement> {
|
|
|
32
31
|
useWrapper?: boolean
|
|
33
32
|
value?: string
|
|
34
33
|
omitSearchIcon?: boolean
|
|
34
|
+
ref?: LegacyRef<PktEl>
|
|
35
|
+
onChange?: ChangeEventHandler<HTMLInputElement>
|
|
36
|
+
onInput?: ChangeEventHandler<HTMLInputElement>
|
|
37
|
+
onBlur?: FocusEventHandler<HTMLInputElement>
|
|
38
|
+
onFocus?: FocusEventHandler<HTMLInputElement>
|
|
39
|
+
onValueChange?: (e: CustomEvent) => void
|
|
40
|
+
onToggleHelpText?: (e: CustomEvent) => void
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
export const PktTextinput =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
disabled = false,
|
|
48
|
-
errorMessage,
|
|
49
|
-
hasError = false,
|
|
50
|
-
helptext,
|
|
51
|
-
helptextDropdown,
|
|
52
|
-
helptextDropdownButton,
|
|
53
|
-
iconNameRight,
|
|
54
|
-
inline = false,
|
|
55
|
-
fullwidth = false,
|
|
56
|
-
label,
|
|
57
|
-
name,
|
|
58
|
-
optionalTag = false,
|
|
59
|
-
optionalText,
|
|
60
|
-
requiredTag = false,
|
|
61
|
-
requiredText,
|
|
62
|
-
placeholder,
|
|
63
|
-
prefix,
|
|
64
|
-
suffix,
|
|
65
|
-
type = 'text',
|
|
66
|
-
useWrapper = true,
|
|
67
|
-
omitSearchIcon = false,
|
|
68
|
-
value,
|
|
69
|
-
onChange,
|
|
70
|
-
...props
|
|
71
|
-
}: IPktTextinput,
|
|
72
|
-
ref: ForwardedRef<HTMLInputElement>,
|
|
73
|
-
) => {
|
|
74
|
-
const classNames = [className, 'pkt-textinput'].join(' ')
|
|
75
|
-
const labelledBy = ariaLabelledby || `${id}-label`
|
|
76
|
-
const shouldShowSearchIcon = type === 'search' && !iconNameRight && !omitSearchIcon
|
|
77
|
-
|
|
78
|
-
const internalRef = useRef<HTMLTextAreaElement>(null)
|
|
79
|
-
const [counterCurrent, setCounterCurrent] = useState(0)
|
|
80
|
-
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
81
|
-
counter && setCounterCurrent(e.currentTarget?.value?.length || 0)
|
|
82
|
-
if (onChange) {
|
|
83
|
-
return onChange(e)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<PktInputWrapper
|
|
89
|
-
ariaDescribedby={ariaDescribedby}
|
|
90
|
-
className={classNames}
|
|
91
|
-
disabled={disabled}
|
|
92
|
-
errorMessage={errorMessage}
|
|
93
|
-
forId={id}
|
|
94
|
-
hasError={hasError}
|
|
95
|
-
helptext={helptext}
|
|
96
|
-
helptextDropdown={helptextDropdown}
|
|
97
|
-
helptextDropdownButton={helptextDropdownButton}
|
|
98
|
-
inline={inline}
|
|
99
|
-
label={label}
|
|
100
|
-
optionalTag={optionalTag}
|
|
101
|
-
optionalText={optionalText}
|
|
102
|
-
requiredTag={requiredTag}
|
|
103
|
-
requiredText={requiredText}
|
|
104
|
-
useWrapper={useWrapper}
|
|
105
|
-
counter={counter}
|
|
106
|
-
counterCurrent={counterCurrent}
|
|
107
|
-
counterMaxLength={counterMaxLength}
|
|
108
|
-
>
|
|
109
|
-
<div className="pkt-input__container">
|
|
110
|
-
{prefix && <div className="pkt-input-prefix">{prefix}</div>}
|
|
111
|
-
<input
|
|
112
|
-
ref={ref}
|
|
113
|
-
className={`pkt-input ${fullwidth ? 'pkt-input--fullwidth' : ''} ${
|
|
114
|
-
counterMaxLength && counterCurrent > counterMaxLength ? 'pkt-input--counter-error' : ''
|
|
115
|
-
}`}
|
|
116
|
-
type={type}
|
|
117
|
-
name={name || id}
|
|
118
|
-
id={id}
|
|
119
|
-
placeholder={placeholder}
|
|
120
|
-
autoComplete={autocomplete}
|
|
121
|
-
value={value}
|
|
122
|
-
disabled={disabled}
|
|
123
|
-
aria-invalid={hasError}
|
|
124
|
-
aria-errormessage={`${id}-error`}
|
|
125
|
-
aria-labelledby={labelledBy}
|
|
126
|
-
{...props}
|
|
127
|
-
onChange={handleChange}
|
|
128
|
-
/>
|
|
129
|
-
{suffix && (
|
|
130
|
-
<p className="pkt-input-suffix">
|
|
131
|
-
{suffix}
|
|
132
|
-
{iconNameRight && <PktIcon className="pkt-input-suffix-icon" name={iconNameRight} />}
|
|
133
|
-
{shouldShowSearchIcon && <PktIcon className="pkt-input-suffix-icon" name="magnifying-glass-big" />}
|
|
134
|
-
</p>
|
|
135
|
-
)}
|
|
136
|
-
|
|
137
|
-
{!suffix && iconNameRight && <PktIcon className="pkt-input-icon" name={iconNameRight} />}
|
|
138
|
-
{!suffix && shouldShowSearchIcon && <PktIcon className="pkt-input-icon" name="magnifying-glass-big" />}
|
|
139
|
-
</div>
|
|
140
|
-
</PktInputWrapper>
|
|
141
|
-
)
|
|
43
|
+
export const PktTextinput: ForwardRefExoticComponent<IPktTextinput> = createComponent({
|
|
44
|
+
tagName: 'pkt-textinput',
|
|
45
|
+
elementClass: PktEl,
|
|
46
|
+
react: React,
|
|
47
|
+
displayName: 'PktTextinput',
|
|
48
|
+
events: {
|
|
49
|
+
onChange: 'change' as EventName<PktEventWithTarget>,
|
|
50
|
+
onBlur: 'blur' as EventName<FocusEvent>,
|
|
51
|
+
onFocus: 'focus' as EventName<FocusEvent>,
|
|
52
|
+
onToggleHelpText: 'toggleHelpText' as EventName<CustomEvent>,
|
|
142
53
|
},
|
|
143
|
-
)
|
|
54
|
+
})
|