@oslokommune/punkt-react 12.43.2 → 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
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
import { PktSelect as PktElSelect } from '@oslokommune/punkt-elements'
|
|
14
|
-
import type { TSelectOption } from '@oslokommune/punkt-elements'
|
|
15
|
-
import { createComponent, EventName } from '@lit/react'
|
|
16
|
-
import { PktEventWithTarget } from '@/interfaces/IPktElements'
|
|
3
|
+
import { ForwardedRef, forwardRef, ReactNode, SelectHTMLAttributes } from 'react'
|
|
4
|
+
|
|
5
|
+
import { PktInputWrapper } from '../inputwrapper/InputWrapper'
|
|
6
|
+
|
|
7
|
+
export type TSelectOption = {
|
|
8
|
+
value: string
|
|
9
|
+
label: string
|
|
10
|
+
selected?: boolean
|
|
11
|
+
disabled?: boolean
|
|
12
|
+
}
|
|
17
13
|
|
|
18
14
|
export interface IPktSelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|
19
15
|
ariaDescribedby?: string
|
|
@@ -30,49 +26,72 @@ export interface IPktSelectProps extends SelectHTMLAttributes<HTMLSelectElement>
|
|
|
30
26
|
fullwidth?: boolean
|
|
31
27
|
label: string
|
|
32
28
|
name?: string
|
|
33
|
-
options?: TSelectOption[]
|
|
34
29
|
optionalTag?: boolean
|
|
35
30
|
optionalText?: string
|
|
36
31
|
requiredTag?: boolean
|
|
37
32
|
requiredText?: string
|
|
38
|
-
|
|
39
|
-
onChange?: ChangeEventHandler<HTMLSelectElement>
|
|
40
|
-
onInput?: ChangeEventHandler<HTMLSelectElement>
|
|
41
|
-
onBlur?: FocusEventHandler<HTMLSelectElement>
|
|
42
|
-
onFocus?: FocusEventHandler<HTMLSelectElement>
|
|
43
|
-
onValueChange?: (e: CustomEvent) => void
|
|
44
|
-
onToggleHelpText?: (e: CustomEvent) => void
|
|
33
|
+
options?: Array<{ value: string; label: string; disabled?: boolean }>
|
|
45
34
|
}
|
|
46
35
|
|
|
47
|
-
export const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
36
|
+
export const PktSelect = forwardRef(
|
|
37
|
+
(
|
|
38
|
+
{
|
|
39
|
+
ariaDescribedby,
|
|
40
|
+
ariaLabelledby,
|
|
41
|
+
children,
|
|
42
|
+
className,
|
|
43
|
+
disabled = false,
|
|
44
|
+
errorMessage,
|
|
45
|
+
hasError,
|
|
46
|
+
helptext,
|
|
47
|
+
helptextDropdown,
|
|
48
|
+
helptextDropdownButton,
|
|
49
|
+
id,
|
|
50
|
+
inline = false,
|
|
51
|
+
fullwidth = false,
|
|
52
|
+
label,
|
|
53
|
+
name,
|
|
54
|
+
optionalTag = false,
|
|
55
|
+
optionalText,
|
|
56
|
+
requiredTag = false,
|
|
57
|
+
requiredText,
|
|
58
|
+
...props
|
|
59
|
+
}: IPktSelectProps,
|
|
60
|
+
ref: ForwardedRef<HTMLSelectElement>,
|
|
61
|
+
) => {
|
|
62
|
+
const classNames = [className, 'pkt-select'].join(' ')
|
|
63
|
+
return (
|
|
64
|
+
<PktInputWrapper
|
|
65
|
+
className={classNames}
|
|
66
|
+
forId={`${id}-input`}
|
|
67
|
+
label={label}
|
|
68
|
+
helptext={helptext}
|
|
69
|
+
helptextDropdown={helptextDropdown}
|
|
70
|
+
helptextDropdownButton={helptextDropdownButton}
|
|
71
|
+
optionalTag={optionalTag}
|
|
72
|
+
optionalText={optionalText}
|
|
73
|
+
requiredTag={requiredTag}
|
|
74
|
+
requiredText={requiredText}
|
|
75
|
+
hasError={hasError}
|
|
76
|
+
errorMessage={errorMessage}
|
|
77
|
+
disabled={disabled}
|
|
78
|
+
inline={inline}
|
|
79
|
+
ariaDescribedby={ariaDescribedby}
|
|
80
|
+
>
|
|
81
|
+
<select
|
|
82
|
+
ref={ref}
|
|
83
|
+
className={`pkt-input ${fullwidth ? 'pkt-input--fullwidth' : ''}`}
|
|
84
|
+
aria-invalid={hasError}
|
|
85
|
+
aria-errormessage={`${id}-error`}
|
|
86
|
+
aria-labelledby={ariaLabelledby || undefined}
|
|
87
|
+
disabled={disabled}
|
|
88
|
+
id={`${id}-input`}
|
|
89
|
+
name={name || id}
|
|
90
|
+
{...props}
|
|
91
|
+
>
|
|
92
|
+
{children}
|
|
93
|
+
</select>
|
|
94
|
+
</PktInputWrapper>
|
|
95
|
+
)
|
|
59
96
|
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Note:
|
|
63
|
-
// helptext slot needs to be before children because of how React reactivity works.
|
|
64
|
-
// Please do not change this.
|
|
65
|
-
export const PktSelect: FC<IPktSelectProps> = forwardRef(({ children, helptext, ...props }: IPktSelectProps, ref) => {
|
|
66
|
-
return (
|
|
67
|
-
<LitComponent ref={ref} {...props}>
|
|
68
|
-
{helptext && (
|
|
69
|
-
<div slot="helptext" className="pkt-contents">
|
|
70
|
-
{helptext}
|
|
71
|
-
</div>
|
|
72
|
-
)}
|
|
73
|
-
{children}
|
|
74
|
-
</LitComponent>
|
|
75
|
-
)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
PktSelect.displayName = 'PktSelect'
|
|
97
|
+
)
|
|
@@ -11,7 +11,6 @@ expect.extend(toHaveNoViolations)
|
|
|
11
11
|
describe('PktTextarea', () => {
|
|
12
12
|
test('renders input field with correct props', async () => {
|
|
13
13
|
const { getByLabelText } = render(<PktTextarea label="Input Label" id="inputId" />)
|
|
14
|
-
await window.customElements.whenDefined('pkt-textarea')
|
|
15
14
|
|
|
16
15
|
const inputElement = getByLabelText('Input Label')
|
|
17
16
|
expect(inputElement).toBeInTheDocument()
|
|
@@ -21,11 +20,13 @@ describe('PktTextarea', () => {
|
|
|
21
20
|
|
|
22
21
|
test('renders error message when hasError prop is true', async () => {
|
|
23
22
|
const { getByText } = render(<PktTextarea label="Input Label" id="inputId" hasError errorMessage="Input error" />)
|
|
24
|
-
await window.customElements.whenDefined('pkt-textarea')
|
|
25
23
|
|
|
24
|
+
await window.customElements.whenDefined('pkt-alert')
|
|
26
25
|
const errorMessage = getByText('Input error')
|
|
27
26
|
expect(errorMessage).toBeInTheDocument()
|
|
28
|
-
|
|
27
|
+
const alertContainer = errorMessage.closest('pkt-alert')
|
|
28
|
+
|
|
29
|
+
expect(alertContainer).toHaveAttribute('id', 'inputId-input-error')
|
|
29
30
|
})
|
|
30
31
|
|
|
31
32
|
describe('PktTextarea', () => {
|
|
@@ -33,7 +34,6 @@ describe('PktTextarea', () => {
|
|
|
33
34
|
const { getByText, container } = render(
|
|
34
35
|
<PktTextarea label="Input Label" id="inputId" helptext="Help Text" helptextDropdown="Help Text" />,
|
|
35
36
|
)
|
|
36
|
-
await window.customElements.whenDefined('pkt-textarea')
|
|
37
37
|
|
|
38
38
|
const expandButton = getByText('Les mer').closest('button') as HTMLButtonElement
|
|
39
39
|
const helptextElement = container.querySelector('.pkt-inputwrapper__helptext-expandable-closed')
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import {
|
|
4
|
+
ChangeEvent,
|
|
5
|
+
ForwardedRef,
|
|
6
|
+
forwardRef,
|
|
7
7
|
ReactNode,
|
|
8
8
|
TextareaHTMLAttributes,
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
useEffect,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
11
12
|
} from 'react'
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
import type { PktEventWithTarget } from '@/interfaces/IPktElements'
|
|
13
|
+
|
|
14
|
+
import { PktInputWrapper } from '../inputwrapper/InputWrapper'
|
|
15
15
|
|
|
16
16
|
export interface IPktTextarea extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
17
|
+
id: string
|
|
17
18
|
ariaDescribedby?: string
|
|
18
19
|
ariaLabelledby?: string
|
|
19
20
|
counter?: boolean
|
|
20
21
|
counterMaxLength?: number
|
|
22
|
+
disabled?: boolean
|
|
21
23
|
errorMessage?: string | ReactNode | ReactNode[]
|
|
22
24
|
hasError?: boolean
|
|
23
25
|
helptext?: string | ReactNode | ReactNode[]
|
|
@@ -25,51 +27,128 @@ export interface IPktTextarea extends TextareaHTMLAttributes<HTMLTextAreaElement
|
|
|
25
27
|
helptextDropdownButton?: string
|
|
26
28
|
inline?: boolean
|
|
27
29
|
fullwidth?: boolean
|
|
28
|
-
label
|
|
30
|
+
label: string
|
|
31
|
+
name?: string
|
|
29
32
|
optionalTag?: boolean
|
|
30
33
|
optionalText?: string
|
|
31
34
|
requiredTag?: boolean
|
|
32
35
|
requiredText?: string
|
|
36
|
+
placeholder?: string
|
|
37
|
+
rows?: number
|
|
33
38
|
useWrapper?: boolean
|
|
34
39
|
value?: string
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
onValueChange?: (e: CustomEvent) => void
|
|
41
|
-
onToggleHelpText?: (e: CustomEvent) => void
|
|
42
|
-
dataTestid?: string
|
|
40
|
+
autocomplete?: string
|
|
41
|
+
minLength?: number
|
|
42
|
+
maxLength?: number
|
|
43
|
+
readOnly?: boolean
|
|
44
|
+
required?: boolean
|
|
43
45
|
skipForwardTestid?: boolean
|
|
46
|
+
onChange?: (event: ChangeEvent<HTMLTextAreaElement>) => void
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
export const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
export const PktTextarea = forwardRef(
|
|
50
|
+
(
|
|
51
|
+
{
|
|
52
|
+
id,
|
|
53
|
+
ariaDescribedby,
|
|
54
|
+
ariaLabelledby,
|
|
55
|
+
counter,
|
|
56
|
+
counterMaxLength,
|
|
57
|
+
className,
|
|
58
|
+
disabled,
|
|
59
|
+
errorMessage,
|
|
60
|
+
hasError,
|
|
61
|
+
helptext,
|
|
62
|
+
helptextDropdown,
|
|
63
|
+
helptextDropdownButton,
|
|
64
|
+
inline,
|
|
65
|
+
fullwidth = false,
|
|
66
|
+
label,
|
|
67
|
+
name,
|
|
68
|
+
optionalTag = false,
|
|
69
|
+
optionalText,
|
|
70
|
+
requiredTag = false,
|
|
71
|
+
requiredText,
|
|
72
|
+
placeholder,
|
|
73
|
+
rows,
|
|
74
|
+
useWrapper = true,
|
|
75
|
+
onChange,
|
|
76
|
+
value,
|
|
77
|
+
autoComplete = 'off',
|
|
78
|
+
minLength,
|
|
79
|
+
maxLength,
|
|
80
|
+
readOnly = false,
|
|
81
|
+
skipForwardTestid = false,
|
|
82
|
+
...props
|
|
83
|
+
}: IPktTextarea,
|
|
84
|
+
ref: ForwardedRef<HTMLTextAreaElement>,
|
|
85
|
+
) => {
|
|
86
|
+
const classNames = [className, 'pkt-textinput', 'pkt-textarea'].join(' ')
|
|
87
|
+
|
|
88
|
+
const inputId = `${id}-input` // id til input element og sendes inn til htmlFor/forId i InputWrapper
|
|
89
|
+
const labelId = `${inputId}-label` // id til label elementet og sendes inn til aria-labelledby i input (genereres i InputWrapper)
|
|
90
|
+
const labelledBy = ariaLabelledby || labelId
|
|
91
|
+
|
|
92
|
+
const internalRef = useRef<HTMLTextAreaElement>(null)
|
|
93
|
+
|
|
94
|
+
const [counterCurrent, setCounterCurrent] = useState(0)
|
|
95
|
+
|
|
96
|
+
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
|
97
|
+
counter && setCounterCurrent(e.currentTarget?.value?.length || 0)
|
|
98
|
+
if (onChange) {
|
|
99
|
+
return onChange(e)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
typeof ref === 'function' && ref(internalRef.current)
|
|
105
|
+
counter && setCounterCurrent(internalRef?.current?.value?.length || 0)
|
|
106
|
+
}, [ref])
|
|
60
107
|
|
|
61
|
-
export const PktTextarea = forwardRef<HTMLTextAreaElement | IPktTextarea, IPktTextarea>(
|
|
62
|
-
({ helptext, ...props }, ref) => {
|
|
63
108
|
return (
|
|
64
|
-
<
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
109
|
+
<PktInputWrapper
|
|
110
|
+
ariaDescribedby={ariaDescribedby}
|
|
111
|
+
className={classNames}
|
|
112
|
+
disabled={disabled}
|
|
113
|
+
errorMessage={errorMessage}
|
|
114
|
+
forId={inputId}
|
|
115
|
+
hasError={hasError}
|
|
116
|
+
helptext={helptext}
|
|
117
|
+
helptextDropdown={helptextDropdown}
|
|
118
|
+
helptextDropdownButton={helptextDropdownButton}
|
|
119
|
+
inline={inline}
|
|
120
|
+
label={label}
|
|
121
|
+
optionalTag={optionalTag}
|
|
122
|
+
optionalText={optionalText}
|
|
123
|
+
requiredTag={requiredTag}
|
|
124
|
+
requiredText={requiredText}
|
|
125
|
+
useWrapper={useWrapper}
|
|
126
|
+
counter={counter}
|
|
127
|
+
counterCurrent={counterCurrent}
|
|
128
|
+
counterMaxLength={counterMaxLength}
|
|
129
|
+
>
|
|
130
|
+
<textarea
|
|
131
|
+
ref={internalRef}
|
|
132
|
+
className={`pkt-input ${fullwidth ? 'pkt-input--fullwidth' : ''} ${
|
|
133
|
+
counterMaxLength && counterCurrent > counterMaxLength ? 'pkt-input--counter-error' : ''
|
|
134
|
+
}`}
|
|
135
|
+
name={`${name || id}-input`}
|
|
136
|
+
id={inputId}
|
|
137
|
+
placeholder={placeholder}
|
|
138
|
+
disabled={disabled}
|
|
139
|
+
rows={rows}
|
|
140
|
+
aria-labelledby={labelledBy}
|
|
141
|
+
aria-invalid={hasError}
|
|
142
|
+
aria-errormessage={`${id}-error`}
|
|
143
|
+
{...props}
|
|
144
|
+
onChange={handleChange}
|
|
145
|
+
value={value}
|
|
146
|
+
autoComplete={autoComplete}
|
|
147
|
+
minLength={minLength}
|
|
148
|
+
maxLength={maxLength}
|
|
149
|
+
data-skip-forward-testid={skipForwardTestid ? 'true' : undefined}
|
|
150
|
+
/>
|
|
151
|
+
</PktInputWrapper>
|
|
71
152
|
)
|
|
72
153
|
},
|
|
73
154
|
)
|
|
74
|
-
|
|
75
|
-
PktTextarea.displayName = 'PktTextarea'
|
|
@@ -2,38 +2,39 @@ import '@testing-library/jest-dom'
|
|
|
2
2
|
|
|
3
3
|
import { fireEvent, render } from '@testing-library/react'
|
|
4
4
|
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
5
|
-
import React from 'react'
|
|
6
5
|
|
|
7
6
|
import { PktTextinput } from './Textinput'
|
|
8
7
|
|
|
9
8
|
expect.extend(toHaveNoViolations)
|
|
10
9
|
|
|
10
|
+
const inputId = 'inputId'
|
|
11
|
+
const label = 'Input Label'
|
|
12
|
+
|
|
11
13
|
describe('PktTextinput', () => {
|
|
12
14
|
test('renders input field with correct props', async () => {
|
|
13
|
-
const { getByLabelText } = render(<PktTextinput label=
|
|
14
|
-
await window.customElements.whenDefined('pkt-textinput')
|
|
15
|
+
const { getByLabelText } = render(<PktTextinput label={label} id={inputId} />)
|
|
15
16
|
|
|
16
17
|
const inputElement = getByLabelText('Input Label')
|
|
17
18
|
expect(inputElement).toBeInTheDocument()
|
|
18
19
|
expect(inputElement.tagName).toBe('INPUT')
|
|
19
|
-
expect(inputElement.id).toBe('inputId
|
|
20
|
+
expect(inputElement.id).toBe('inputId')
|
|
20
21
|
})
|
|
21
22
|
|
|
22
23
|
test('renders error message when hasError prop is true', async () => {
|
|
23
|
-
const { getByText } = render(<PktTextinput label=
|
|
24
|
-
await window.customElements.whenDefined('pkt-textinput')
|
|
24
|
+
const { getByText } = render(<PktTextinput label={label} id={inputId} hasError errorMessage="Input error" />)
|
|
25
25
|
|
|
26
26
|
const errorMessage = getByText('Input error')
|
|
27
27
|
expect(errorMessage).toBeInTheDocument()
|
|
28
|
-
|
|
28
|
+
const alertContainer = errorMessage.closest('pkt-alert')
|
|
29
|
+
expect(alertContainer).not.toBeNull()
|
|
30
|
+
expect(alertContainer).toHaveAttribute('id', 'inputId-error')
|
|
29
31
|
})
|
|
30
32
|
|
|
31
33
|
describe('PktTextinput', () => {
|
|
32
34
|
test('toggles helptext class', async () => {
|
|
33
35
|
const { getByText, container } = render(
|
|
34
|
-
<PktTextinput label=
|
|
36
|
+
<PktTextinput label={label} id={inputId} helptext="Help Text" helptextDropdown="Help Text" />,
|
|
35
37
|
)
|
|
36
|
-
await window.customElements.whenDefined('pkt-textinput')
|
|
37
38
|
|
|
38
39
|
const expandButton = getByText('Les mer').closest('button') as HTMLButtonElement
|
|
39
40
|
const helptextElement = container.querySelector('.pkt-inputwrapper__helptext-expandable-closed')
|
|
@@ -48,8 +49,8 @@ describe('PktTextinput', () => {
|
|
|
48
49
|
|
|
49
50
|
describe('accessibility', () => {
|
|
50
51
|
it('renders with no wcag errors with axe', async () => {
|
|
51
|
-
const { container } = render(<PktTextinput label=
|
|
52
|
-
|
|
52
|
+
const { container } = render(<PktTextinput label={label} id={inputId} />)
|
|
53
|
+
|
|
53
54
|
const results = await axe(container)
|
|
54
55
|
expect(results).toHaveNoViolations()
|
|
55
56
|
})
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import React, {
|
|
4
|
-
ForwardRefExoticComponent,
|
|
5
|
-
ChangeEventHandler,
|
|
6
|
-
FocusEventHandler,
|
|
7
|
-
ReactNode,
|
|
8
|
-
InputHTMLAttributes,
|
|
9
|
-
forwardRef,
|
|
10
4
|
ForwardedRef,
|
|
5
|
+
forwardRef,
|
|
6
|
+
InputHTMLAttributes,
|
|
7
|
+
useRef,
|
|
8
|
+
useEffect,
|
|
9
|
+
useState,
|
|
10
|
+
ChangeEvent,
|
|
11
|
+
ReactNode,
|
|
11
12
|
} from 'react'
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
13
|
+
|
|
14
|
+
import { PktIcon } from '../icon/Icon'
|
|
15
|
+
import { PktInputWrapper } from '../inputwrapper/InputWrapper'
|
|
15
16
|
|
|
16
17
|
export interface IPktTextinput extends InputHTMLAttributes<HTMLInputElement> {
|
|
18
|
+
id: string
|
|
19
|
+
ariaDescribedby?: string
|
|
17
20
|
ariaLabelledby?: string
|
|
21
|
+
autocomplete?: string
|
|
18
22
|
counter?: boolean
|
|
19
23
|
counterMaxLength?: number
|
|
24
|
+
disabled?: boolean
|
|
20
25
|
errorMessage?: string | ReactNode | ReactNode[]
|
|
21
26
|
hasError?: boolean
|
|
22
27
|
helptext?: string | ReactNode | ReactNode[]
|
|
@@ -25,52 +30,171 @@ export interface IPktTextinput extends InputHTMLAttributes<HTMLInputElement> {
|
|
|
25
30
|
iconNameRight?: string
|
|
26
31
|
inline?: boolean
|
|
27
32
|
fullwidth?: boolean
|
|
28
|
-
label
|
|
33
|
+
label: string
|
|
34
|
+
name?: string
|
|
29
35
|
optionalTag?: boolean
|
|
30
36
|
optionalText?: string
|
|
31
37
|
requiredTag?: boolean
|
|
32
38
|
requiredText?: string
|
|
39
|
+
placeholder?: string
|
|
33
40
|
prefix?: string
|
|
34
41
|
suffix?: string
|
|
42
|
+
type?: string
|
|
35
43
|
useWrapper?: boolean
|
|
36
44
|
value?: string
|
|
37
45
|
omitSearchIcon?: boolean
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
min?: number | string
|
|
47
|
+
max?: number | string
|
|
48
|
+
minLength?: number
|
|
49
|
+
maxLength?: number
|
|
50
|
+
step?: string
|
|
51
|
+
size?: number
|
|
52
|
+
readonly?: boolean
|
|
53
|
+
required?: boolean
|
|
45
54
|
dataTestid?: string
|
|
55
|
+
onChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
|
46
56
|
skipForwardTestid?: boolean
|
|
47
57
|
}
|
|
48
58
|
|
|
49
|
-
export const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
export const PktTextinput = forwardRef(
|
|
60
|
+
(
|
|
61
|
+
{
|
|
62
|
+
id,
|
|
63
|
+
ariaDescribedby,
|
|
64
|
+
ariaLabelledby,
|
|
65
|
+
autocomplete = 'off',
|
|
66
|
+
counter,
|
|
67
|
+
counterMaxLength,
|
|
68
|
+
className,
|
|
69
|
+
disabled = false,
|
|
70
|
+
errorMessage,
|
|
71
|
+
hasError = false,
|
|
72
|
+
helptext,
|
|
73
|
+
helptextDropdown,
|
|
74
|
+
helptextDropdownButton,
|
|
75
|
+
iconNameRight,
|
|
76
|
+
inline = false,
|
|
77
|
+
fullwidth = false,
|
|
78
|
+
label,
|
|
79
|
+
name,
|
|
80
|
+
optionalTag = false,
|
|
81
|
+
optionalText,
|
|
82
|
+
requiredTag = false,
|
|
83
|
+
requiredText,
|
|
84
|
+
placeholder,
|
|
85
|
+
prefix,
|
|
86
|
+
suffix,
|
|
87
|
+
type = 'text',
|
|
88
|
+
useWrapper = true,
|
|
89
|
+
omitSearchIcon = false,
|
|
90
|
+
value,
|
|
91
|
+
minLength,
|
|
92
|
+
maxLength,
|
|
93
|
+
min,
|
|
94
|
+
max,
|
|
95
|
+
step,
|
|
96
|
+
size,
|
|
97
|
+
readonly,
|
|
98
|
+
required,
|
|
99
|
+
dataTestid,
|
|
100
|
+
onChange,
|
|
101
|
+
skipForwardTestid = false,
|
|
102
|
+
...props
|
|
103
|
+
}: IPktTextinput,
|
|
104
|
+
ref: ForwardedRef<HTMLInputElement>,
|
|
105
|
+
) => {
|
|
106
|
+
const classNames = [className, 'pkt-textinput'].join(' ')
|
|
63
107
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
108
|
+
const shouldShowSearchIcon = type === 'search' && !iconNameRight && !omitSearchIcon
|
|
109
|
+
const isoValue = type === 'date' && value ? value.slice(0, 10) : value
|
|
110
|
+
|
|
111
|
+
const [counterCurrent, setCounterCurrent] = useState(value?.length || 0)
|
|
112
|
+
|
|
113
|
+
const inputId = `${id}` // id til input element og sendes inn til htmlFor i InputWrapper
|
|
114
|
+
const labelId = `${inputId}-label` // id til label elementet og sendes inn til aria-labelledby i input (genereres i InputWrapper)
|
|
115
|
+
const labelledBy = ariaLabelledby || labelId
|
|
116
|
+
|
|
117
|
+
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
118
|
+
const newValue = event.currentTarget.value
|
|
119
|
+
if (counter) {
|
|
120
|
+
setCounterCurrent(newValue?.length || 0)
|
|
121
|
+
}
|
|
122
|
+
if (onChange) {
|
|
123
|
+
onChange(event)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
75
126
|
|
|
76
|
-
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (value !== undefined) {
|
|
129
|
+
setCounterCurrent(value?.length || 0)
|
|
130
|
+
}
|
|
131
|
+
}, [value])
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<PktInputWrapper
|
|
135
|
+
ariaDescribedby={ariaDescribedby}
|
|
136
|
+
className={classNames}
|
|
137
|
+
disabled={disabled}
|
|
138
|
+
errorMessage={errorMessage}
|
|
139
|
+
forId={inputId}
|
|
140
|
+
hasError={hasError}
|
|
141
|
+
helptext={helptext}
|
|
142
|
+
helptextDropdown={helptextDropdown}
|
|
143
|
+
helptextDropdownButton={helptextDropdownButton}
|
|
144
|
+
inline={inline}
|
|
145
|
+
label={label}
|
|
146
|
+
optionalTag={optionalTag}
|
|
147
|
+
optionalText={optionalText}
|
|
148
|
+
requiredTag={requiredTag}
|
|
149
|
+
requiredText={requiredText}
|
|
150
|
+
useWrapper={useWrapper}
|
|
151
|
+
counter={counter}
|
|
152
|
+
counterCurrent={counterCurrent}
|
|
153
|
+
counterMaxLength={counterMaxLength}
|
|
154
|
+
>
|
|
155
|
+
<div
|
|
156
|
+
className="pkt-input__container"
|
|
157
|
+
data-testid={dataTestid}
|
|
158
|
+
data-skip-forward-testid={skipForwardTestid ? 'true' : undefined}
|
|
159
|
+
>
|
|
160
|
+
{prefix && <div className="pkt-input-prefix">{prefix}</div>}
|
|
161
|
+
<input
|
|
162
|
+
{...props}
|
|
163
|
+
ref={ref}
|
|
164
|
+
className={`pkt-input ${fullwidth ? 'pkt-input--fullwidth' : ''} ${
|
|
165
|
+
counterMaxLength && counterCurrent > counterMaxLength ? 'pkt-input--counter-error' : ''
|
|
166
|
+
}`}
|
|
167
|
+
type={type}
|
|
168
|
+
name={`${name || id}`}
|
|
169
|
+
value={isoValue}
|
|
170
|
+
id={inputId}
|
|
171
|
+
placeholder={placeholder}
|
|
172
|
+
autoComplete={autocomplete}
|
|
173
|
+
disabled={disabled}
|
|
174
|
+
aria-invalid={hasError}
|
|
175
|
+
aria-errormessage={`${id}-error`}
|
|
176
|
+
aria-labelledby={labelledBy}
|
|
177
|
+
min={min}
|
|
178
|
+
max={max}
|
|
179
|
+
onChange={handleChange}
|
|
180
|
+
step={step}
|
|
181
|
+
minLength={minLength}
|
|
182
|
+
maxLength={maxLength}
|
|
183
|
+
size={size}
|
|
184
|
+
required={required}
|
|
185
|
+
/>
|
|
186
|
+
{suffix && (
|
|
187
|
+
<p className="pkt-input-suffix">
|
|
188
|
+
{suffix}
|
|
189
|
+
{iconNameRight && <PktIcon className="pkt-input-suffix-icon" name={iconNameRight} />}
|
|
190
|
+
{shouldShowSearchIcon && <PktIcon className="pkt-input-suffix-icon" name="magnifying-glass-big" />}
|
|
191
|
+
</p>
|
|
192
|
+
)}
|
|
193
|
+
|
|
194
|
+
{!suffix && iconNameRight && <PktIcon className="pkt-input-icon" name={iconNameRight} />}
|
|
195
|
+
{!suffix && shouldShowSearchIcon && <PktIcon className="pkt-input-icon" name="magnifying-glass-big" />}
|
|
196
|
+
</div>
|
|
197
|
+
</PktInputWrapper>
|
|
198
|
+
)
|
|
199
|
+
},
|
|
200
|
+
)
|