@oslokommune/punkt-react 17.0.10 → 17.1.1

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.
@@ -11,3 +11,14 @@ export declare const booleanishConverter: {
11
11
  fromAttribute(value: string | boolean | null): boolean;
12
12
  toAttribute(value: boolean): string | null;
13
13
  };
14
+ /**
15
+ * Som booleanishConverter, men skiller «ikke satt» fra `false`.
16
+ *
17
+ * Brukes for props der komponenten skal utlede en fornuftig standard når
18
+ * forbrukeren ikke har sagt noe — for eksempel `counter`, som slås på av seg
19
+ * selv når det finnes noe å telle, men skal kunne skrus av eksplisitt.
20
+ */
21
+ export declare const nullableBooleanishConverter: {
22
+ fromAttribute(value: string | boolean | null): boolean | null;
23
+ toAttribute(value: boolean | null): string | null;
24
+ };
@@ -1,5 +1,5 @@
1
1
  export type { Booleanish } from './booleanish';
2
- export { booleanishConverter } from './booleanish';
2
+ export { booleanishConverter, nullableBooleanishConverter } from './booleanish';
3
3
  export type { TAriaBoolean, TAriaBooleanMixed, TAriaCurrent, TAriaHasPopup, TAriaInvalid, TAriaLive, TAriaRelevant, TAriaSort, TAriaAutoComplete, TAriaOrientation, IAriaAttributes, TAlertRole, } from './aria';
4
4
  export type { THeadingLevel, THeadingSize, THeadingWeight } from './heading';
5
5
  export type { User, Representing, UserMenuItem, InternalMenuItemBase, InternalMenuLink, InternalMenuButton, TInternalMenuItem, TSlotMenuVariant, THeaderMenu, TLogOutButtonPlacement, THeaderPosition, THeaderScrollBehavior, } from './header';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Bygger `aria-describedby` for et skjemafelt.
3
+ *
4
+ * Beskrivelsen må ligge på selve kontrollen — `aria-describedby` på en `<label>`
5
+ * bidrar ikke til feltets tilgjengelige beskrivelse, og hjelpeteksten blir da
6
+ * aldri lest opp når fokus havner i feltet.
7
+ *
8
+ * Feilmeldingen er med vilje ikke med: den kobles gjennom `aria-errormessage`,
9
+ * som er den riktige kanalen, og ville ellers blitt lest opp to ganger.
10
+ */
11
+ export declare const describedByIds: (options: {
12
+ /** Id-en feltet bruker (samme som wrapperens forId). */
13
+ id: string;
14
+ /** Om det finnes en hjelpetekst å beskrive feltet med. */
15
+ hasHelptext?: boolean;
16
+ /** Om telleren er aktiv. Statusregionen leses bare opp når grensa er brutt. */
17
+ hasCounter?: boolean;
18
+ /** Forbrukerens egen `ariaDescribedby`, hvis satt. */
19
+ ariaDescribedby?: string | null;
20
+ }) => string | undefined;
21
+ /** Id-en til den skjulte statusregionen som melder at tellergrensa er brutt. */
22
+ export declare const counterStatusId: (id: string) => string;
23
+ /** Om tellergrensa er brutt. */
24
+ export declare const isCounterExceeded: (current: number | undefined, max: number | undefined) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-react",
3
- "version": "17.0.10",
3
+ "version": "17.1.1",
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",
@@ -45,7 +45,7 @@
45
45
  "@eslint/eslintrc": "^3.3.5",
46
46
  "@eslint/js": "^10.0.1",
47
47
  "@oslokommune/punkt-assets": "^17.0.3",
48
- "@oslokommune/punkt-css": "^17.0.10",
48
+ "@oslokommune/punkt-css": "^17.1.1",
49
49
  "@testing-library/jest-dom": "^6.9.1",
50
50
  "@testing-library/react": "^16.3.2",
51
51
  "@testing-library/user-event": "^14.6.1",
@@ -101,5 +101,5 @@
101
101
  "url": "https://github.com/oslokommune/punkt/issues"
102
102
  },
103
103
  "license": "MIT",
104
- "gitHead": "d7aa97ec38415ce31bd73314e4669ca2ba04a8cf"
104
+ "gitHead": "0e600db899cfda4ac87979c94724908424ba56c8"
105
105
  }
@@ -28,7 +28,7 @@ export const PktCombobox = forwardRef<HTMLDivElement, IPktCombobox>((props, ref)
28
28
  <div className={outerClasses} ref={state.wrapperRef} id={state.id || undefined}>
29
29
  <PktInputWrapper
30
30
  forId={wrapperForId}
31
- hasFieldset={!hasTextInput}
31
+ hasFieldset={props.hasFieldset ?? !hasTextInput}
32
32
  label={state.label || ''}
33
33
  helptext={state.helptext}
34
34
  helptextDropdown={state.helptextDropdown}
@@ -42,6 +42,7 @@ export const PktCombobox = forwardRef<HTMLDivElement, IPktCombobox>((props, ref)
42
42
  requiredText={state.requiredText}
43
43
  tagText={state.tagText}
44
44
  useWrapper={state.useWrapper}
45
+ inline={state.inline}
45
46
  size={props.inputSize ?? 'medium'}
46
47
  counter={state.hasCounter}
47
48
  counterCurrent={state.values.length}
@@ -50,6 +50,9 @@ export interface IPktCombobox {
50
50
  optionalText?: string
51
51
  tagText?: string
52
52
  useWrapper?: boolean
53
+ inline?: boolean
54
+ counter?: boolean
55
+ hasFieldset?: boolean
53
56
  inputSize?: 'small' | 'medium' | 'xsmall'
54
57
 
55
58
  // Events
@@ -110,6 +113,7 @@ export interface IComboboxState {
110
113
  requiredText?: string
111
114
  tagText?: string
112
115
  useWrapper: boolean
116
+ inline: boolean
113
117
  inputSize: 'small' | 'medium' | 'xsmall'
114
118
  name?: string
115
119
  className?: string
@@ -72,6 +72,8 @@ export function useComboboxState(props: IPktCombobox, ref: React.ForwardedRef<HT
72
72
  requiredText,
73
73
  tagText,
74
74
  useWrapper = true,
75
+ inline = false,
76
+ counter,
75
77
  inputSize = 'medium',
76
78
  name,
77
79
  className,
@@ -184,7 +186,8 @@ export function useComboboxState(props: IPktCombobox, ref: React.ForwardedRef<HT
184
186
 
185
187
  // Derived state
186
188
  const maxIsReached = isMaxSelectionReached(values.length, maxlength ?? null)
187
- const hasCounter = multiple && maxlength != null
189
+ // Telleren vises av seg selv når det er noe å telle, men kan slås av eksplisitt
190
+ const hasCounter = counter ?? (multiple && maxlength != null)
188
191
 
189
192
  // Filter options by search (typeahead uses fulltext filtering, includeSearch uses same)
190
193
  const filteredOptions = useMemo(() => {
@@ -1105,6 +1108,7 @@ export function useComboboxState(props: IPktCombobox, ref: React.ForwardedRef<HT
1105
1108
  requiredText,
1106
1109
  tagText,
1107
1110
  useWrapper,
1111
+ inline,
1108
1112
  inputSize,
1109
1113
  name,
1110
1114
  className,
@@ -12,7 +12,12 @@ export type { IPktDatepicker } from './types'
12
12
  export const PktDatepicker = forwardRef<HTMLDivElement, IPktDatepicker>((props, ref) => {
13
13
  const state = useDatepickerState(props, ref)
14
14
 
15
- const outerClasses = ['pkt-datepicker', `pkt-datepicker--${state.inputSize}`, state.className]
15
+ const outerClasses = [
16
+ 'pkt-datepicker',
17
+ `pkt-datepicker--${state.inputSize}`,
18
+ state.fullwidth ? 'pkt-datepicker--fullwidth' : '',
19
+ state.className,
20
+ ]
16
21
  .filter(Boolean)
17
22
  .join(' ')
18
23
 
@@ -20,6 +25,7 @@ export const PktDatepicker = forwardRef<HTMLDivElement, IPktDatepicker>((props,
20
25
  <div className={outerClasses} id={state.id || undefined}>
21
26
  <PktInputWrapper
22
27
  ref={state.wrapperRef}
28
+ ariaDescribedby={props.ariaDescribedby}
23
29
  forId={state.inputId}
24
30
  label={state.label}
25
31
  hasFieldset={state.hasFieldset}
@@ -5,6 +5,7 @@ import type { TPktInputSize } from 'shared-types/size'
5
5
  export interface IPktDatepicker extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'> {
6
6
  id: string
7
7
  label: string
8
+ ariaDescribedby?: string
8
9
 
9
10
  // Datepicker-specific
10
11
  value?: string | string[]
@@ -27,6 +28,7 @@ export interface IPktDatepicker extends Omit<InputHTMLAttributes<HTMLInputElemen
27
28
  inputSize?: TPktInputSize
28
29
  hasFieldset?: boolean
29
30
  inline?: boolean
31
+ counter?: boolean
30
32
  helptext?: string | ReactNode
31
33
  helptextDropdown?: string | ReactNode
32
34
  helptextDropdownButton?: string
@@ -38,6 +38,7 @@ export function useDatepickerState(props: IPktDatepicker, ref: React.ForwardedRe
38
38
  fullwidth = false,
39
39
  inputSize = 'medium' as TPktInputSize,
40
40
  hasFieldset = false,
41
+ counter,
41
42
  inline = false,
42
43
  helptext,
43
44
  helptextDropdown,
@@ -152,7 +153,8 @@ export function useDatepickerState(props: IPktDatepicker, ref: React.ForwardedRe
152
153
  }, [currentmonth, maxStr])
153
154
 
154
155
  const isInputDisabled = disabled || (multiple && maxlength != null && values.length >= maxlength)
155
- const hasCounter = multiple && maxlength != null
156
+ // Telleren vises av seg selv når det er noe å telle, men kan slås av eksplisitt
157
+ const hasCounter = counter ?? (multiple && maxlength != null)
156
158
  const inputId = `${id}-input`
157
159
  const formValue = values.join(',')
158
160
 
@@ -421,27 +421,29 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
421
421
 
422
422
  if (label) {
423
423
  return (
424
- <PktInputWrapper
425
- forId={id}
426
- label={label}
427
- helptext={helptext}
428
- helptextDropdown={helptextDropdown}
429
- helptextDropdownButton={helptextDropdownButton}
430
- disabled={disabled}
431
- hasError={hasError}
432
- optionalTag={optionalTag}
433
- optionalText={optionalText}
434
- requiredTag={requiredTag}
435
- requiredText={requiredText}
436
- tagText={tagText}
437
- className={classNames('pkt-fileupload-wrapper', { 'pkt-fileupload-wrapper--full-width': fullwidth })}
438
- >
439
- {fileUploadContent}
440
- </PktInputWrapper>
424
+ <div className="pkt-fileupload-component">
425
+ <PktInputWrapper
426
+ forId={id}
427
+ label={label}
428
+ helptext={helptext}
429
+ helptextDropdown={helptextDropdown}
430
+ helptextDropdownButton={helptextDropdownButton}
431
+ disabled={disabled}
432
+ hasError={hasError}
433
+ optionalTag={optionalTag}
434
+ optionalText={optionalText}
435
+ requiredTag={requiredTag}
436
+ requiredText={requiredText}
437
+ tagText={tagText}
438
+ className={classNames('pkt-fileupload-wrapper', { 'pkt-fileupload-wrapper--full-width': fullwidth })}
439
+ >
440
+ {fileUploadContent}
441
+ </PktInputWrapper>
442
+ </div>
441
443
  )
442
444
  }
443
445
 
444
- return fileUploadContent
446
+ return <div className="pkt-fileupload-component">{fileUploadContent}</div>
445
447
  },
446
448
  )
447
449
 
@@ -63,6 +63,8 @@ export const PktHelptext = forwardRef<HTMLDivElement, IPktHelptextProps>(functio
63
63
  <button
64
64
  type="button"
65
65
  className="pkt-link pkt-link--icon-right pkt-btn pkt-btn--small pkt-btn--tertiary pkt-btn--icon-right"
66
+ aria-expanded={isOpen}
67
+ aria-controls={`${forId}-helptext-expandable`}
66
68
  onClick={toggle}
67
69
  >
68
70
  <PktIcon
@@ -77,6 +79,7 @@ export const PktHelptext = forwardRef<HTMLDivElement, IPktHelptextProps>(functio
77
79
  />
78
80
  </button>
79
81
  <div
82
+ id={`${forId}-helptext-expandable`}
80
83
  className={classNames(
81
84
  'pkt-inputwrapper__helptext',
82
85
  isOpen
@@ -3,6 +3,7 @@
3
3
  import { ForwardedRef, forwardRef, type ReactNode, RefAttributes } from 'react'
4
4
 
5
5
  import { useFocusModality } from '../../hooks/useFocusModality'
6
+ import { counterStatusId, isCounterExceeded } from 'shared-utils/forms/described-by'
6
7
  import { PktAlert } from '../alert/Alert'
7
8
  import { PktHelptext } from '../helptext/Helptext'
8
9
 
@@ -59,7 +60,7 @@ export const PktInputWrapper = forwardRef(
59
60
  children,
60
61
  className = '',
61
62
  hasFieldset = false,
62
- role = 'group',
63
+ role,
63
64
  counterPosition = 'bottom',
64
65
  size = 'medium',
65
66
  }: IPktInputWrapper,
@@ -85,12 +86,18 @@ export const PktInputWrapper = forwardRef(
85
86
 
86
87
  const tagClasses = ['pkt-tag', 'pkt-tag--small', 'pkt-tag--thin-text'].join(' ')
87
88
 
89
+ const counterExceeded = isCounterExceeded(counterCurrent, counterMaxLength)
88
90
  const Counter = () =>
89
91
  showCounter ? (
90
- <div className="pkt-input__counter" aria-live="polite" aria-atomic="true">
91
- {counterCurrent}
92
- {counterMaxLength ? `/${counterMaxLength}` : ''}
93
- </div>
92
+ <>
93
+ <div className="pkt-input__counter" aria-hidden="true">
94
+ {counterCurrent}
95
+ {counterMaxLength ? `/${counterMaxLength}` : ''}
96
+ </div>
97
+ <div className="pkt-sr-only" id={counterStatusId(forId)} aria-live="polite">
98
+ {counterExceeded ? `Du har brukt ${counterCurrent} av ${counterMaxLength} tegn` : ''}
99
+ </div>
100
+ </>
94
101
  ) : null
95
102
 
96
103
  const renderTags = () => {
@@ -111,7 +118,7 @@ export const PktInputWrapper = forwardRef(
111
118
 
112
119
  if (!useWrapper) {
113
120
  return (
114
- <label htmlFor={forId} className="pkt-sr-only" aria-describedby={describedBy} id={`${forId}-label`}>
121
+ <label htmlFor={forId} className="pkt-sr-only" id={`${forId}-label`}>
115
122
  {label}
116
123
  </label>
117
124
  )
@@ -126,7 +133,7 @@ export const PktInputWrapper = forwardRef(
126
133
  }
127
134
 
128
135
  return (
129
- <label className="pkt-inputwrapper__label" htmlFor={forId} aria-describedby={describedBy} id={`${forId}-label`}>
136
+ <label className="pkt-inputwrapper__label" htmlFor={forId} id={`${forId}-label`}>
130
137
  {labelContent}
131
138
  </label>
132
139
  )
@@ -166,7 +173,12 @@ export const PktInputWrapper = forwardRef(
166
173
  )
167
174
 
168
175
  return (
169
- <div className={wrapperClasses} role={role} {...focusModality}>
176
+ <div
177
+ className={wrapperClasses}
178
+ role={role}
179
+ aria-labelledby={role ? `${forId}-label` : undefined}
180
+ {...focusModality}
181
+ >
170
182
  {hasFieldset ? (
171
183
  <fieldset className="pkt-inputwrapper__fieldset" aria-describedby={describedBy}>
172
184
  {content}
@@ -3,6 +3,7 @@
3
3
  import { ForwardedRef, forwardRef, ReactNode, SelectHTMLAttributes } from 'react'
4
4
 
5
5
  import { PktInputWrapper } from '../inputwrapper/InputWrapper'
6
+ import { describedByIds } from 'shared-utils/forms/described-by'
6
7
 
7
8
  export type { IPktSelectOption, TSelectOption } from 'shared-types/select'
8
9
 
@@ -26,6 +27,8 @@ export interface IPktSelectProps extends SelectHTMLAttributes<HTMLSelectElement>
26
27
  requiredTag?: boolean
27
28
  requiredText?: string
28
29
  tagText?: string | null
30
+ useWrapper?: boolean
31
+ hasFieldset?: boolean
29
32
  inputSize?: 'small' | 'medium' | 'xsmall'
30
33
  /** @deprecated Var aldri implementert — bruk children med `<option>`-elementer. */
31
34
  options?: never
@@ -54,47 +57,58 @@ export const PktSelect = forwardRef(
54
57
  requiredTag = false,
55
58
  requiredText,
56
59
  tagText,
60
+ useWrapper = true,
61
+ hasFieldset,
57
62
  inputSize = 'medium',
58
63
  ...props
59
64
  }: IPktSelectProps,
60
65
  ref: ForwardedRef<HTMLSelectElement>,
61
66
  ) => {
62
- const classNames = [className, 'pkt-select'].join(' ')
67
+ const outerClasses = [className, 'pkt-select'].filter(Boolean).join(' ')
63
68
 
64
69
  return (
65
- <PktInputWrapper
66
- className={classNames}
67
- forId={`${id}-input`}
68
- label={label}
69
- helptext={helptext}
70
- helptextDropdown={helptextDropdown}
71
- helptextDropdownButton={helptextDropdownButton}
72
- optionalTag={optionalTag}
73
- optionalText={optionalText}
74
- requiredTag={requiredTag}
75
- requiredText={requiredText}
76
- tagText={tagText}
77
- hasError={hasError}
78
- errorMessage={errorMessage}
79
- disabled={disabled}
80
- inline={inline}
81
- ariaDescribedby={ariaDescribedby}
82
- size={inputSize}
83
- >
84
- <select
85
- ref={ref}
86
- className={`pkt-input pkt-input--${inputSize} ${fullwidth ? 'pkt-input--fullwidth' : ''}`}
87
- aria-invalid={hasError}
88
- aria-errormessage={`${id}-error`}
89
- aria-labelledby={ariaLabelledby || undefined}
70
+ <div className={outerClasses}>
71
+ <PktInputWrapper
72
+ forId={`${id}-input`}
73
+ label={label}
74
+ helptext={helptext}
75
+ helptextDropdown={helptextDropdown}
76
+ helptextDropdownButton={helptextDropdownButton}
77
+ optionalTag={optionalTag}
78
+ optionalText={optionalText}
79
+ requiredTag={requiredTag}
80
+ requiredText={requiredText}
81
+ tagText={tagText}
82
+ hasError={hasError}
83
+ errorMessage={errorMessage}
90
84
  disabled={disabled}
91
- id={`${id}-input`}
92
- name={name || id}
93
- {...props}
85
+ inline={inline}
86
+ ariaDescribedby={ariaDescribedby}
87
+ useWrapper={useWrapper}
88
+ hasFieldset={hasFieldset ?? false}
89
+ size={inputSize}
94
90
  >
95
- {children}
96
- </select>
97
- </PktInputWrapper>
91
+ <select
92
+ ref={ref}
93
+ className={`pkt-input pkt-input--${inputSize} ${fullwidth ? 'pkt-input--fullwidth' : ''}`}
94
+ aria-invalid={hasError}
95
+ aria-errormessage={hasError ? `${id}-error` : undefined}
96
+ aria-describedby={describedByIds({
97
+ id: `${id}-input`,
98
+ hasHelptext: !!helptext,
99
+ hasCounter: false,
100
+ ariaDescribedby,
101
+ })}
102
+ aria-labelledby={ariaLabelledby || undefined}
103
+ disabled={disabled}
104
+ id={`${id}-input`}
105
+ name={name || id}
106
+ {...props}
107
+ >
108
+ {children}
109
+ </select>
110
+ </PktInputWrapper>
111
+ </div>
98
112
  )
99
113
  },
100
114
  )
@@ -13,12 +13,14 @@ import {
13
13
 
14
14
  import { PktInputWrapper } from '../inputwrapper/InputWrapper'
15
15
  import { TPktInputSize } from 'shared-types/size'
16
+ import { describedByIds } from 'shared-utils/forms/described-by'
16
17
 
17
18
  export interface IPktTextarea extends TextareaHTMLAttributes<HTMLTextAreaElement> {
18
19
  id: string
19
20
  ariaDescribedby?: string
20
21
  ariaLabelledby?: string
21
22
  counter?: boolean
23
+ hasFieldset?: boolean
22
24
  counterMaxLength?: number
23
25
  disabled?: boolean
24
26
  errorMessage?: string | ReactNode | ReactNode[]
@@ -56,6 +58,7 @@ export const PktTextarea = forwardRef(
56
58
  ariaDescribedby,
57
59
  ariaLabelledby,
58
60
  counter,
61
+ hasFieldset,
59
62
  counterMaxLength,
60
63
  className,
61
64
  disabled,
@@ -88,7 +91,8 @@ export const PktTextarea = forwardRef(
88
91
  }: IPktTextarea,
89
92
  ref: ForwardedRef<HTMLTextAreaElement>,
90
93
  ) => {
91
- const classNames = [className, 'pkt-textinput', 'pkt-textarea'].join(' ')
94
+ const showCounter = counter ?? (counterMaxLength ?? 0) > 0
95
+ const outerClasses = [className, 'pkt-textinput', 'pkt-textarea'].filter(Boolean).join(' ')
92
96
 
93
97
  const inputId = `${id}-input` // id til input element og sendes inn til htmlFor/forId i InputWrapper
94
98
  const labelId = `${inputId}-label` // id til label elementet og sendes inn til aria-labelledby i input (genereres i InputWrapper)
@@ -97,7 +101,7 @@ export const PktTextarea = forwardRef(
97
101
  const [counterCurrent, setCounterCurrent] = useState(0)
98
102
 
99
103
  const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
100
- if (counter) {
104
+ if (showCounter) {
101
105
  setCounterCurrent(e.currentTarget?.value?.length || 0)
102
106
  }
103
107
  if (onChange) {
@@ -119,52 +123,60 @@ export const PktTextarea = forwardRef(
119
123
  }, [value])
120
124
 
121
125
  return (
122
- <PktInputWrapper
123
- ariaDescribedby={ariaDescribedby}
124
- className={classNames}
125
- disabled={disabled}
126
- errorMessage={errorMessage}
127
- forId={inputId}
128
- hasError={hasError}
129
- helptext={helptext}
130
- helptextDropdown={helptextDropdown}
131
- helptextDropdownButton={helptextDropdownButton}
132
- inline={inline}
133
- label={label}
134
- optionalTag={optionalTag}
135
- optionalText={optionalText}
136
- requiredTag={requiredTag}
137
- requiredText={requiredText}
138
- tagText={tagText}
139
- useWrapper={useWrapper}
140
- counter={counter}
141
- counterCurrent={counterCurrent}
142
- counterMaxLength={counterMaxLength}
143
- size={inputSize}
144
- >
145
- <textarea
146
- ref={initRef}
147
- className={`pkt-input pkt-input--${inputSize} ${fullwidth ? 'pkt-input--fullwidth' : ''} ${
148
- counterMaxLength && counterCurrent > counterMaxLength ? 'pkt-input--counter-error' : ''
149
- }`}
150
- name={`${name || id}`}
151
- id={inputId}
152
- placeholder={placeholder}
126
+ <div className={outerClasses}>
127
+ <PktInputWrapper
128
+ ariaDescribedby={ariaDescribedby}
153
129
  disabled={disabled}
154
- rows={rows}
155
- aria-labelledby={labelledBy}
156
- aria-invalid={hasError}
157
- aria-errormessage={`${id}-error`}
158
- {...props}
159
- onChange={handleChange}
160
- value={value}
161
- autoComplete={autoComplete}
162
- minLength={minLength}
163
- maxLength={maxLength}
164
- readOnly={readOnly}
165
- data-skip-forward-testid={skipForwardTestid ? 'true' : undefined}
166
- />
167
- </PktInputWrapper>
130
+ errorMessage={errorMessage}
131
+ forId={inputId}
132
+ hasError={hasError}
133
+ helptext={helptext}
134
+ helptextDropdown={helptextDropdown}
135
+ helptextDropdownButton={helptextDropdownButton}
136
+ inline={inline}
137
+ label={label}
138
+ optionalTag={optionalTag}
139
+ optionalText={optionalText}
140
+ requiredTag={requiredTag}
141
+ requiredText={requiredText}
142
+ tagText={tagText}
143
+ useWrapper={useWrapper}
144
+ counter={showCounter}
145
+ counterCurrent={counterCurrent}
146
+ counterMaxLength={counterMaxLength}
147
+ hasFieldset={hasFieldset ?? false}
148
+ size={inputSize}
149
+ >
150
+ <textarea
151
+ ref={initRef}
152
+ className={`pkt-input pkt-input--${inputSize} ${fullwidth ? 'pkt-input--fullwidth' : ''} ${
153
+ counterMaxLength && counterCurrent > counterMaxLength ? 'pkt-input--counter-error' : ''
154
+ }`}
155
+ name={`${name || id}`}
156
+ id={inputId}
157
+ placeholder={placeholder}
158
+ disabled={disabled}
159
+ rows={rows}
160
+ aria-labelledby={labelledBy}
161
+ aria-invalid={hasError}
162
+ aria-errormessage={hasError ? `${id}-error` : undefined}
163
+ aria-describedby={describedByIds({
164
+ id: inputId,
165
+ hasHelptext: !!helptext,
166
+ hasCounter: showCounter,
167
+ ariaDescribedby,
168
+ })}
169
+ {...props}
170
+ onChange={handleChange}
171
+ value={value}
172
+ autoComplete={autoComplete}
173
+ minLength={minLength}
174
+ maxLength={maxLength}
175
+ readOnly={readOnly}
176
+ data-skip-forward-testid={skipForwardTestid ? 'true' : undefined}
177
+ />
178
+ </PktInputWrapper>
179
+ </div>
168
180
  )
169
181
  },
170
182
  )