@oslokommune/punkt-react 16.26.5 → 17.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/dist/components/accordion/Accordion.d.ts +1 -0
  3. package/dist/components/alert/Alert.d.ts +9 -0
  4. package/dist/components/button/Button.d.ts +1 -1
  5. package/dist/components/combobox/types.d.ts +2 -0
  6. package/dist/components/datepicker/types.d.ts +3 -0
  7. package/dist/components/footer/Footer.accessibility.test.d.ts +0 -0
  8. package/dist/components/footer/Footer.d.ts +12 -34
  9. package/dist/components/footer/Footer.test.d.ts +1 -0
  10. package/dist/components/footer/FooterGlobal.d.ts +23 -0
  11. package/dist/components/footerSimple/FooterSimple.d.ts +19 -16
  12. package/dist/components/footerSimple/FooterSimple.test.d.ts +1 -0
  13. package/dist/components/header/Header.d.ts +15 -3
  14. package/dist/components/header/HeaderGlobal.accessibility.test.d.ts +0 -0
  15. package/dist/components/header/HeaderGlobal.d.ts +2 -0
  16. package/dist/components/header/HeaderGlobal.test.d.ts +0 -0
  17. package/dist/components/header/HeaderService.accessibility.test.d.ts +0 -0
  18. package/dist/components/header/types.d.ts +30 -2
  19. package/dist/components/header-menu/HeaderMenu.d.ts +2 -4
  20. package/dist/components/index.d.ts +4 -0
  21. package/dist/components/inputwrapper/InputWrapper.d.ts +1 -0
  22. package/dist/components/searchinput/SearchInput.d.ts +1 -0
  23. package/dist/components/select/Select.d.ts +4 -7
  24. package/dist/components/textarea/Textarea.d.ts +2 -0
  25. package/dist/components/textinput/Textinput.d.ts +1 -0
  26. package/dist/components/timepicker/types.d.ts +3 -0
  27. package/dist/components/timepicker/useTimepickerState.d.ts +2 -0
  28. package/dist/punkt-react.cjs +1 -1
  29. package/dist/punkt-react.js +3188 -2729
  30. package/dist/shared-types/footer.d.ts +95 -0
  31. package/dist/shared-types/{header-menu.d.ts → header-footer.d.ts} +51 -4
  32. package/dist/shared-types/header.d.ts +1 -1
  33. package/dist/shared-types/index.d.ts +6 -4
  34. package/dist/shared-types/searchinput.d.ts +2 -0
  35. package/dist/shared-types/select.d.ts +3 -2
  36. package/dist/shared-types/size.d.ts +4 -0
  37. package/dist/shared-types/timepicker.d.ts +0 -6
  38. package/dist/shared-utils/datepicker-utils.d.ts +5 -4
  39. package/dist/shared-utils/fileupload/focus-trap.d.ts +4 -10
  40. package/dist/shared-utils/focus-trap/index.d.ts +22 -0
  41. package/dist/shared-utils/{header-menu → header-footer}/example-data.d.ts +5 -4
  42. package/dist/shared-utils/header-footer/external-domain.d.ts +26 -0
  43. package/dist/shared-utils/{header-menu → header-footer}/fetch-header-footer-data.d.ts +6 -2
  44. package/dist/shared-utils/{header-menu → header-footer}/index.d.ts +2 -1
  45. package/dist/shared-utils/{header-menu → header-footer}/select-megamenu.d.ts +1 -1
  46. package/dist/shared-utils/scroll-lock/index.d.ts +24 -0
  47. package/package.json +4 -4
  48. package/src/components/accordion/Accordion.test.tsx +51 -0
  49. package/src/components/accordion/Accordion.tsx +3 -3
  50. package/src/components/accordion/AccordionItem.tsx +7 -14
  51. package/src/components/alert/Alert.test.tsx +45 -9
  52. package/src/components/alert/Alert.tsx +13 -3
  53. package/src/components/button/Button.tsx +2 -2
  54. package/src/components/combobox/Combobox.core.test.tsx +14 -0
  55. package/src/components/combobox/Combobox.tsx +1 -0
  56. package/src/components/combobox/ComboboxInput.tsx +2 -0
  57. package/src/components/combobox/types.ts +2 -0
  58. package/src/components/combobox/useComboboxState.ts +2 -0
  59. package/src/components/consent/Consent.test.tsx +8 -14
  60. package/src/components/consent/Consent.tsx +9 -19
  61. package/src/components/datepicker/Datepicker.core.test.tsx +11 -0
  62. package/src/components/datepicker/Datepicker.tsx +4 -1
  63. package/src/components/datepicker/DatepickerInputs.tsx +16 -1
  64. package/src/components/datepicker/types.ts +3 -0
  65. package/src/components/datepicker/useDatepickerState.ts +17 -9
  66. package/src/components/footer/Footer.accessibility.test.tsx +46 -0
  67. package/src/components/footer/Footer.test.tsx +192 -86
  68. package/src/components/footer/Footer.tsx +169 -159
  69. package/src/components/footer/FooterGlobal.tsx +181 -0
  70. package/src/components/footerSimple/FooterSimple.test.tsx +49 -46
  71. package/src/components/footerSimple/FooterSimple.tsx +117 -78
  72. package/src/components/header/Header.tsx +17 -4
  73. package/src/components/header/HeaderGlobal.accessibility.test.tsx +86 -0
  74. package/src/components/header/HeaderGlobal.test.tsx +254 -0
  75. package/src/components/header/HeaderGlobal.tsx +467 -0
  76. package/src/components/header/HeaderService.accessibility.test.tsx +70 -0
  77. package/src/components/header/HeaderService.test.tsx +29 -16
  78. package/src/components/header/HeaderService.tsx +24 -84
  79. package/src/components/header/types.ts +35 -0
  80. package/src/components/header-menu/HeaderMenu.test.tsx +16 -39
  81. package/src/components/header-menu/HeaderMenu.tsx +62 -70
  82. package/src/components/index.ts +4 -0
  83. package/src/components/inputwrapper/InputWrapper.tsx +6 -1
  84. package/src/components/modal/Modal.tsx +7 -11
  85. package/src/components/searchinput/SearchInput.test.tsx +10 -0
  86. package/src/components/searchinput/SearchInput.tsx +19 -7
  87. package/src/components/select/Select.test.tsx +18 -0
  88. package/src/components/select/Select.tsx +8 -4
  89. package/src/components/textarea/Textarea.test.tsx +10 -0
  90. package/src/components/textarea/Textarea.tsx +5 -1
  91. package/src/components/textinput/Textinput.test.tsx +28 -0
  92. package/src/components/textinput/Textinput.tsx +5 -2
  93. package/src/components/timepicker/Timepicker.test.tsx +10 -0
  94. package/src/components/timepicker/Timepicker.tsx +8 -7
  95. package/src/components/timepicker/types.ts +8 -5
  96. package/src/components/timepicker/useTimepickerState.ts +4 -0
  97. package/dist/shared-utils/modal-scroll-lock.d.ts +0 -10
  98. /package/dist/shared-utils/{header-menu → header-footer}/icon-map.d.ts +0 -0
@@ -31,6 +31,7 @@ export interface IPktInputWrapper extends RefAttributes<HTMLElement> {
31
31
  hasFieldset?: boolean
32
32
  role?: string
33
33
  counterPosition?: 'top' | 'bottom'
34
+ size?: 'small' | 'medium' | 'xsmall'
34
35
  }
35
36
 
36
37
  export const PktInputWrapper = forwardRef(
@@ -60,6 +61,7 @@ export const PktInputWrapper = forwardRef(
60
61
  hasFieldset = false,
61
62
  role = 'group',
62
63
  counterPosition = 'bottom',
64
+ size = 'medium',
63
65
  }: IPktInputWrapper,
64
66
  ref: ForwardedRef<HTMLDivElement>,
65
67
  ) => {
@@ -76,6 +78,7 @@ export const PktInputWrapper = forwardRef(
76
78
  hasError ? 'pkt-inputwrapper--error' : '',
77
79
  disabled ? 'pkt-inputwrapper--disabled' : '',
78
80
  inline ? 'pkt-inputwrapper--inline' : '',
81
+ size ? `pkt-inputwrapper--${size}` : '',
79
82
  ]
80
83
  .filter(Boolean)
81
84
  .join(' ')
@@ -143,6 +146,8 @@ export const PktInputWrapper = forwardRef(
143
146
  )
144
147
  }
145
148
 
149
+ const errorAlertSize = size === 'medium' ? 'small' : 'xsmall'
150
+
146
151
  const content = (
147
152
  <>
148
153
  {renderLabel()}
@@ -152,7 +157,7 @@ export const PktInputWrapper = forwardRef(
152
157
  {counterBottom && <Counter />}
153
158
  {hasError && errorMessage && (
154
159
  <div className="pkt-inputwrapper__alert-wrapper">
155
- <PktAlert skin="error" aria-live="assertive" role="alert" id={`${forId}-error`} compact>
160
+ <PktAlert skin="error" size={errorAlertSize} aria-live="assertive" role="alert" id={`${forId}-error`}>
156
161
  {errorMessage}
157
162
  </PktAlert>
158
163
  </div>
@@ -13,11 +13,7 @@ import {
13
13
  useRef,
14
14
  } from 'react'
15
15
 
16
- import {
17
- acquireModalScrollLock,
18
- createModalScrollLockToken,
19
- releaseModalScrollLock,
20
- } from 'shared-utils/modal-scroll-lock'
16
+ import { createScrollLockToken, setScrollLock } from 'shared-utils/scroll-lock'
21
17
 
22
18
  import { PktButton } from '../button/Button'
23
19
 
@@ -59,7 +55,7 @@ export const PktModal = forwardRef(
59
55
  ref: ForwardedRef<HTMLDialogElement>,
60
56
  ) => {
61
57
  const internalRef = useRef<HTMLDialogElement | null>(null)
62
- const scrollLockToken = useRef(createModalScrollLockToken())
58
+ const scrollLockToken = useRef(createScrollLockToken())
63
59
 
64
60
  // Merge consumer ref + internal ref so we always have a working DOM handle
65
61
  // for our own logic, regardless of whether the consumer passes a callback ref
@@ -76,7 +72,7 @@ export const PktModal = forwardRef(
76
72
  const close = useCallback(() => {
77
73
  if (!internalRef.current?.open) return
78
74
  internalRef.current.close()
79
- releaseModalScrollLock(scrollLockToken.current)
75
+ setScrollLock(scrollLockToken.current, false)
80
76
  onClose?.()
81
77
  }, [onClose])
82
78
 
@@ -86,12 +82,12 @@ export const PktModal = forwardRef(
86
82
  if (!internalRef.current?.open) {
87
83
  internalRef.current?.showModal()
88
84
  }
89
- acquireModalScrollLock(scrollLockToken.current)
85
+ setScrollLock(scrollLockToken.current, true)
90
86
  } else {
91
87
  if (internalRef.current?.open) {
92
88
  internalRef.current.close()
93
89
  }
94
- releaseModalScrollLock(scrollLockToken.current)
90
+ setScrollLock(scrollLockToken.current, false)
95
91
  }
96
92
  }, [open])
97
93
 
@@ -99,7 +95,7 @@ export const PktModal = forwardRef(
99
95
  useEffect(() => {
100
96
  const token = scrollLockToken.current
101
97
  return () => {
102
- releaseModalScrollLock(token)
98
+ setScrollLock(token, false)
103
99
  }
104
100
  }, [])
105
101
 
@@ -113,7 +109,7 @@ export const PktModal = forwardRef(
113
109
  )
114
110
 
115
111
  const handleNativeClose = useCallback(() => {
116
- releaseModalScrollLock(scrollLockToken.current)
112
+ setScrollLock(scrollLockToken.current, false)
117
113
  onClose?.()
118
114
  }, [onClose])
119
115
 
@@ -91,6 +91,16 @@ describe('PktSearchInput', () => {
91
91
  })
92
92
  })
93
93
 
94
+ it('applies inputSize classes on wrapper, input and search button', () => {
95
+ const { container } = render(
96
+ <PktSearchInput id="test-search-input" appearance="local-with-button" inputSize="small" />,
97
+ )
98
+
99
+ expect(container.querySelector('.pkt-searchinput--small')).toBeTruthy()
100
+ expect(container.querySelector('.pkt-input--small')).toBeTruthy()
101
+ expect(container.querySelector('.pkt-searchinput__button.pkt-btn--small')).toBeTruthy()
102
+ })
103
+
94
104
  describe('accessibility', () => {
95
105
  it('renders with no wcag errors with axe', async () => {
96
106
  const suggestions = [
@@ -19,6 +19,7 @@ export type PktSearchInputSuggestion = Omit<IPktSearchInputSuggestion, 'title' |
19
19
 
20
20
  interface ISearch extends HTMLProps<HTMLInputElement> {
21
21
  appearance?: 'local' | 'local-with-button' | 'global'
22
+ inputSize?: 'small' | 'medium' | 'xsmall'
22
23
  disabled?: boolean
23
24
  fullwidth?: boolean
24
25
  id: string
@@ -47,6 +48,7 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
47
48
  (
48
49
  {
49
50
  appearance = 'local',
51
+ inputSize = 'medium',
50
52
  disabled = false,
51
53
  fullwidth = false,
52
54
  id,
@@ -76,9 +78,14 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
76
78
  value = e.target.value
77
79
  }
78
80
 
79
- const wrapperClass = `pkt-searchinput pkt-searchinput--${appearance} ${
80
- fullwidth ? 'pkt-searchinput--fullwidth' : ''
81
- }`
81
+ const wrapperClass = [
82
+ 'pkt-searchinput',
83
+ `pkt-searchinput--${appearance}`,
84
+ `pkt-searchinput--${inputSize}`,
85
+ fullwidth && 'pkt-searchinput--fullwidth',
86
+ ]
87
+ .filter(Boolean)
88
+ .join(' ')
82
89
 
83
90
  const focusModality = useFocusModality()
84
91
 
@@ -107,6 +114,9 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
107
114
  const suggestionClassName = (suggestion: PktSearchInputSuggestion) =>
108
115
  ['pkt-searchinput__suggestion', suggestion.onClick ? 'pkt-link-button' : ''].filter(Boolean).join(' ')
109
116
 
117
+ const fieldClass =
118
+ appearance === 'local' ? `pkt-input__container pkt-input__container--${inputSize}` : 'pkt-searchinput__field'
119
+
110
120
  return WrapperElement(
111
121
  <>
112
122
  {label && (
@@ -114,9 +124,9 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
114
124
  {label}
115
125
  </label>
116
126
  )}
117
- <div className={appearance === 'local' ? 'pkt-input__container' : 'pkt-searchinput__field'}>
127
+ <div className={fieldClass}>
118
128
  <input
119
- className={`pkt-input ${fullwidth ? 'pkt-input--fullwidth' : ''}`}
129
+ className={`pkt-input pkt-input--${inputSize} ${fullwidth ? 'pkt-input--fullwidth' : ''}`}
120
130
  type="search"
121
131
  name={name || id}
122
132
  id={id}
@@ -124,9 +134,9 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
124
134
  defaultValue={value}
125
135
  disabled={disabled}
126
136
  autoComplete="off"
127
- aria-autocomplete="list"
137
+ aria-autocomplete={suggestions ? 'list' : undefined}
128
138
  ref={ref}
129
- aria-controls={`${id}-suggestions`}
139
+ aria-controls={suggestions ? `${id}-suggestions` : undefined}
130
140
  onChange={onChange ? onChange : handleNoOnChange}
131
141
  onKeyUp={
132
142
  onSearch &&
@@ -139,10 +149,12 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
139
149
  }
140
150
  {...props}
141
151
  />
152
+
142
153
  <PktButton
143
154
  className={`pkt-searchinput__button ${appearance === 'local' ? 'pkt-input-icon' : ''}`}
144
155
  variant="icon-only"
145
156
  iconName="magnifying-glass-big"
157
+ size={inputSize}
146
158
  skin={appearance === 'local' ? 'tertiary' : 'primary'}
147
159
  color={appearance === 'global' ? 'yellow' : undefined}
148
160
  type="submit"
@@ -8,6 +8,24 @@ import { PktSelect } from './Select'
8
8
  expect.extend(toHaveNoViolations)
9
9
 
10
10
  describe('PktSelect', () => {
11
+ test('applies input wrapper size classes for small and xsmall', () => {
12
+ const { container: smallContainer } = render(
13
+ <PktSelect label="Label" id="select-small" inputSize="small">
14
+ <option value="a">A</option>
15
+ </PktSelect>,
16
+ )
17
+ expect(smallContainer.querySelector('.pkt-inputwrapper')).toHaveClass('pkt-inputwrapper--small')
18
+ expect(smallContainer.querySelector('.pkt-inputwrapper')).not.toHaveClass('pkt-inputwrapper--xsmall')
19
+
20
+ const { container: xsmallContainer } = render(
21
+ <PktSelect label="Label" id="select-xsmall" inputSize="xsmall">
22
+ <option value="a">A</option>
23
+ </PktSelect>,
24
+ )
25
+ expect(xsmallContainer.querySelector('.pkt-inputwrapper')).not.toHaveClass('pkt-inputwrapper--small')
26
+ expect(xsmallContainer.querySelector('.pkt-inputwrapper')).toHaveClass('pkt-inputwrapper--xsmall')
27
+ })
28
+
11
29
  test('renders select field with correct props', async () => {
12
30
  const { container } = render(
13
31
  <PktSelect label="Input Label" id="inputIdeas" ariaLabelledby="inputId">
@@ -1,11 +1,10 @@
1
1
  'use client'
2
2
 
3
3
  import { ForwardedRef, forwardRef, ReactNode, SelectHTMLAttributes } from 'react'
4
- import type { TSelectOption } from 'shared-types'
5
4
 
6
5
  import { PktInputWrapper } from '../inputwrapper/InputWrapper'
7
6
 
8
- export type { TSelectOption }
7
+ export type { IPktSelectOption, TSelectOption } from 'shared-types/select'
9
8
 
10
9
  export interface IPktSelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
11
10
  ariaDescribedby?: string
@@ -27,7 +26,9 @@ export interface IPktSelectProps extends SelectHTMLAttributes<HTMLSelectElement>
27
26
  requiredTag?: boolean
28
27
  requiredText?: string
29
28
  tagText?: string | null
30
- options?: Array<{ value: string; label: string; disabled?: boolean }>
29
+ inputSize?: 'small' | 'medium' | 'xsmall'
30
+ /** @deprecated Var aldri implementert — bruk children med `<option>`-elementer. */
31
+ options?: never
31
32
  }
32
33
 
33
34
  export const PktSelect = forwardRef(
@@ -53,11 +54,13 @@ export const PktSelect = forwardRef(
53
54
  requiredTag = false,
54
55
  requiredText,
55
56
  tagText,
57
+ inputSize = 'medium',
56
58
  ...props
57
59
  }: IPktSelectProps,
58
60
  ref: ForwardedRef<HTMLSelectElement>,
59
61
  ) => {
60
62
  const classNames = [className, 'pkt-select'].join(' ')
63
+
61
64
  return (
62
65
  <PktInputWrapper
63
66
  className={classNames}
@@ -76,10 +79,11 @@ export const PktSelect = forwardRef(
76
79
  disabled={disabled}
77
80
  inline={inline}
78
81
  ariaDescribedby={ariaDescribedby}
82
+ size={inputSize}
79
83
  >
80
84
  <select
81
85
  ref={ref}
82
- className={`pkt-input ${fullwidth ? 'pkt-input--fullwidth' : ''}`}
86
+ className={`pkt-input pkt-input--${inputSize} ${fullwidth ? 'pkt-input--fullwidth' : ''}`}
83
87
  aria-invalid={hasError}
84
88
  aria-errormessage={`${id}-error`}
85
89
  aria-labelledby={ariaLabelledby || undefined}
@@ -15,6 +15,16 @@ describe('PktTextarea', () => {
15
15
  expect(inputElement).toBeInTheDocument()
16
16
  expect(inputElement.tagName).toBe('TEXTAREA')
17
17
  expect(inputElement.id).toBe('inputId-input')
18
+ expect(inputElement).toHaveClass('pkt-input--medium')
19
+ })
20
+
21
+ test('applies inputSize to textarea and wrapper', async () => {
22
+ const { container, getByLabelText } = render(
23
+ <PktTextarea label="Small textarea" id="smallTextarea" inputSize="small" />,
24
+ )
25
+
26
+ expect(getByLabelText('Small textarea')).toHaveClass('pkt-input--small')
27
+ expect(container.querySelector('.pkt-inputwrapper--small')).toBeInTheDocument()
18
28
  })
19
29
 
20
30
  test('renders error message when hasError prop is true', async () => {
@@ -12,6 +12,7 @@ import {
12
12
  } from 'react'
13
13
 
14
14
  import { PktInputWrapper } from '../inputwrapper/InputWrapper'
15
+ import { TPktInputSize } from 'shared-types/size'
15
16
 
16
17
  export interface IPktTextarea extends TextareaHTMLAttributes<HTMLTextAreaElement> {
17
18
  id: string
@@ -44,6 +45,7 @@ export interface IPktTextarea extends TextareaHTMLAttributes<HTMLTextAreaElement
44
45
  readOnly?: boolean
45
46
  required?: boolean
46
47
  skipForwardTestid?: boolean
48
+ inputSize?: TPktInputSize
47
49
  onChange?: ChangeEventHandler<HTMLTextAreaElement>
48
50
  }
49
51
 
@@ -81,6 +83,7 @@ export const PktTextarea = forwardRef(
81
83
  maxLength,
82
84
  readOnly,
83
85
  skipForwardTestid = false,
86
+ inputSize = 'medium',
84
87
  ...props
85
88
  }: IPktTextarea,
86
89
  ref: ForwardedRef<HTMLTextAreaElement>,
@@ -137,10 +140,11 @@ export const PktTextarea = forwardRef(
137
140
  counter={counter}
138
141
  counterCurrent={counterCurrent}
139
142
  counterMaxLength={counterMaxLength}
143
+ size={inputSize}
140
144
  >
141
145
  <textarea
142
146
  ref={initRef}
143
- className={`pkt-input ${fullwidth ? 'pkt-input--fullwidth' : ''} ${
147
+ className={`pkt-input pkt-input--${inputSize} ${fullwidth ? 'pkt-input--fullwidth' : ''} ${
144
148
  counterMaxLength && counterCurrent > counterMaxLength ? 'pkt-input--counter-error' : ''
145
149
  }`}
146
150
  name={`${name || id}`}
@@ -27,7 +27,35 @@ describe('PktTextinput', () => {
27
27
  const alertContainer = screen.getByRole('alert')
28
28
  expect(alertContainer).toHaveAttribute('id', errorMessageId)
29
29
  expect(alertContainer).toHaveTextContent('Input error')
30
+ })
31
+
32
+ describe('inputSize', () => {
33
+ test('applies medium size classes by default', async () => {
34
+ const { container } = render(<PktTextinput label={label} id={inputId} />)
35
+
36
+ const inputElement = container.querySelector('input')
37
+ const containerElement = container.querySelector('.pkt-input__container')
38
+ expect(inputElement).toHaveClass('pkt-input--medium')
39
+ expect(containerElement).toHaveClass('pkt-input__container--medium')
40
+ })
30
41
 
42
+ test('applies small size classes when inputSize is small', async () => {
43
+ const { container } = render(<PktTextinput label={label} id={inputId} inputSize="small" />)
44
+
45
+ const inputElement = container.querySelector('input')
46
+ const containerElement = container.querySelector('.pkt-input__container')
47
+ expect(inputElement).toHaveClass('pkt-input--small')
48
+ expect(containerElement).toHaveClass('pkt-input__container--small')
49
+ })
50
+
51
+ test('applies xsmall size classes when inputSize is xsmall', async () => {
52
+ const { container } = render(<PktTextinput label={label} id={inputId} inputSize="xsmall" />)
53
+
54
+ const inputElement = container.querySelector('input')
55
+ const containerElement = container.querySelector('.pkt-input__container')
56
+ expect(inputElement).toHaveClass('pkt-input--xsmall')
57
+ expect(containerElement).toHaveClass('pkt-input__container--xsmall')
58
+ })
31
59
  })
32
60
 
33
61
  describe('PktTextinput', () => {
@@ -55,6 +55,7 @@ export interface IPktTextinput extends InputHTMLAttributes<HTMLInputElement> {
55
55
  dataTestid?: string
56
56
  onChange?: ChangeEventHandler<HTMLInputElement>
57
57
  skipForwardTestid?: boolean
58
+ inputSize?: 'small' | 'medium' | 'xsmall'
58
59
  }
59
60
 
60
61
  export const PktTextinput = forwardRef(
@@ -101,6 +102,7 @@ export const PktTextinput = forwardRef(
101
102
  dataTestid,
102
103
  onChange,
103
104
  skipForwardTestid = false,
105
+ inputSize = 'medium',
104
106
  ...props
105
107
  }: IPktTextinput,
106
108
  ref: ForwardedRef<HTMLInputElement>,
@@ -153,9 +155,10 @@ export const PktTextinput = forwardRef(
153
155
  counter={counter}
154
156
  counterCurrent={counterCurrent}
155
157
  counterMaxLength={counterMaxLength}
158
+ size={inputSize}
156
159
  >
157
160
  <div
158
- className="pkt-input__container"
161
+ className={`pkt-input__container pkt-input__container--${inputSize}`}
159
162
  data-testid={dataTestid}
160
163
  data-skip-forward-testid={skipForwardTestid ? 'true' : undefined}
161
164
  >
@@ -163,7 +166,7 @@ export const PktTextinput = forwardRef(
163
166
  <input
164
167
  {...props}
165
168
  ref={ref}
166
- className={`pkt-input ${fullwidth ? 'pkt-input--fullwidth' : ''} ${
169
+ className={`pkt-input pkt-input--${inputSize} ${fullwidth ? 'pkt-input--fullwidth' : ''} ${
167
170
  counterMaxLength && counterCurrent > counterMaxLength ? 'pkt-input--counter-error' : ''
168
171
  }`}
169
172
  type={type}
@@ -43,6 +43,16 @@ describe('PktTimepicker', () => {
43
43
  expect(container.querySelector('.pkt-timepicker--fullwidth')).toBeInTheDocument()
44
44
  })
45
45
 
46
+ test('applies inputSize classes on root, container, inputs and clock button', () => {
47
+ const { container } = render(
48
+ <PktTimepicker id={id} name="t" label={label} inputSize="small" />,
49
+ )
50
+ expect(container.querySelector('.pkt-timepicker--small')).toBeTruthy()
51
+ expect(container.querySelector('.pkt-input__container--small')).toBeTruthy()
52
+ expect(container.querySelectorAll('.pkt-input--small')).toHaveLength(2)
53
+ expect(container.querySelector('.pkt-timepicker__button.pkt-btn--small')).toBeTruthy()
54
+ })
55
+
46
56
  test('renders clock button when picker is visible', () => {
47
57
  render(<PktTimepicker id={id} name="t" label={label} />)
48
58
  expect(
@@ -10,7 +10,7 @@ import type { IPktTimepicker } from './types'
10
10
  export type { IPktTimepicker } from './types'
11
11
 
12
12
  export const PktTimepicker = forwardRef<HTMLDivElement, IPktTimepicker>((props, ref) => {
13
- const { onChange } = props
13
+ const { onChange, inputSize = 'medium' } = props
14
14
  const state = useTimepickerState(props, ref)
15
15
 
16
16
  const hoursLabel = state.strings.hours
@@ -80,7 +80,7 @@ export const PktTimepicker = forwardRef<HTMLDivElement, IPktTimepicker>((props,
80
80
 
81
81
  const renderContainer = () => (
82
82
  <div
83
- className="pkt-input__container"
83
+ className={`pkt-input__container pkt-input__container--${inputSize}`}
84
84
  onClick={(e) => {
85
85
  const target = e.target as HTMLElement
86
86
  if (target.closest('button, input')) return
@@ -90,7 +90,7 @@ export const PktTimepicker = forwardRef<HTMLDivElement, IPktTimepicker>((props,
90
90
  {state.stepArrows && (
91
91
  <button
92
92
  type="button"
93
- className="pkt-input-icon pkt-btn pkt-btn--icon-only pkt-btn--tertiary pkt-timepicker__button pkt-timepicker__button--prev"
93
+ className={`pkt-input-icon pkt-btn pkt-btn--icon-only pkt-btn--tertiary pkt-btn--${inputSize} pkt-timepicker__button pkt-timepicker__button--prev`}
94
94
  aria-label={state.strings.prevTime}
95
95
  disabled={state.disabled}
96
96
  onClick={() => state.stepTimeDelta(-1)}
@@ -105,7 +105,7 @@ export const PktTimepicker = forwardRef<HTMLDivElement, IPktTimepicker>((props,
105
105
  inputMode="numeric"
106
106
  maxLength={2}
107
107
  size={2}
108
- className="pkt-input pkt-timepicker__input"
108
+ className={`pkt-input pkt-input--${inputSize} pkt-timepicker__input`}
109
109
  id={state.hoursId}
110
110
  data-min="0"
111
111
  data-max="23"
@@ -137,7 +137,7 @@ export const PktTimepicker = forwardRef<HTMLDivElement, IPktTimepicker>((props,
137
137
  inputMode="numeric"
138
138
  maxLength={2}
139
139
  size={2}
140
- className="pkt-input pkt-timepicker__input"
140
+ className={`pkt-input pkt-input--${inputSize} pkt-timepicker__input`}
141
141
  id={state.minutesId}
142
142
  data-min="0"
143
143
  data-max="59"
@@ -169,7 +169,7 @@ export const PktTimepicker = forwardRef<HTMLDivElement, IPktTimepicker>((props,
169
169
  <button
170
170
  ref={state.buttonRef}
171
171
  type="button"
172
- className="pkt-input-icon pkt-btn pkt-btn--icon-only pkt-btn--tertiary pkt-timepicker__button"
172
+ className={`pkt-input-icon pkt-btn pkt-btn--icon-only pkt-btn--tertiary pkt-btn--${inputSize} pkt-timepicker__button`}
173
173
  aria-label={state.strings.openPicker}
174
174
  aria-haspopup="listbox"
175
175
  aria-expanded={state.isOpen}
@@ -184,7 +184,7 @@ export const PktTimepicker = forwardRef<HTMLDivElement, IPktTimepicker>((props,
184
184
  {state.stepArrows && (
185
185
  <button
186
186
  type="button"
187
- className="pkt-input-icon pkt-btn pkt-btn--icon-only pkt-btn--tertiary pkt-timepicker__button pkt-timepicker__button--next"
187
+ className={`pkt-input-icon pkt-btn pkt-btn--icon-only pkt-btn--tertiary pkt-btn--${inputSize} pkt-timepicker__button pkt-timepicker__button--next`}
188
188
  aria-label={state.strings.nextTime}
189
189
  disabled={state.disabled}
190
190
  onClick={() => state.stepTimeDelta(1)}
@@ -234,6 +234,7 @@ export const PktTimepicker = forwardRef<HTMLDivElement, IPktTimepicker>((props,
234
234
  helptextDropdown={state.helptextDropdown}
235
235
  helptextDropdownButton={state.helptextDropdownButton}
236
236
  tagText={state.tagText}
237
+ size={inputSize}
237
238
  >
238
239
  {!state.hidePicker && !state.stepArrows ? (
239
240
  <div className="pkt-timepicker__anchor">
@@ -1,4 +1,5 @@
1
1
  import type { ChangeEvent, FocusEvent, InputHTMLAttributes, ReactNode } from 'react'
2
+ import { TPktInputSize } from 'shared-types/size'
2
3
  import type { ITimepickerStrings } from 'shared-types/timepicker'
3
4
 
4
5
  /**
@@ -7,11 +8,10 @@ import type { ITimepickerStrings } from 'shared-types/timepicker'
7
8
  * The value is submitted via a hidden `input type="time"`; hour/minute fields are the editing UI. Use `value`
8
9
  * or `defaultValue` for controlled vs. uncontrolled mode.
9
10
  */
10
- export interface IPktTimepicker
11
- extends Omit<
12
- InputHTMLAttributes<HTMLInputElement>,
13
- 'value' | 'onChange' | 'onInput' | 'min' | 'max' | 'step' | 'onFocus' | 'onBlur'
14
- > {
11
+ export interface IPktTimepicker extends Omit<
12
+ InputHTMLAttributes<HTMLInputElement>,
13
+ 'value' | 'onChange' | 'onInput' | 'min' | 'max' | 'step' | 'onFocus' | 'onBlur'
14
+ > {
15
15
  /** Unique id; used for hour, minute, popup, and hidden input ids. */
16
16
  id: string
17
17
 
@@ -112,4 +112,7 @@ export interface IPktTimepicker
112
112
 
113
113
  /** Extra class names on the root (`pkt-timepicker`). */
114
114
  className?: string
115
+
116
+ /** Size of the Timepicker component. */
117
+ inputSize?: TPktInputSize
115
118
  }
@@ -14,6 +14,7 @@ import { stepTime } from 'shared-utils/timepicker/stepper'
14
14
  import { isValidTimeString, timeToMinutes } from 'shared-utils/timepicker/time-utils'
15
15
 
16
16
  import type { IPktTimepicker } from './types'
17
+ import { TPktInputSize } from 'shared-types/size'
17
18
 
18
19
  /**
19
20
  * ## Why this hook exists (React form model)
@@ -138,6 +139,7 @@ export function useTimepickerState(props: IPktTimepicker, ref: React.ForwardedRe
138
139
  ariaDescribedby,
139
140
  strings: stringsProp,
140
141
  className,
142
+ inputSize = 'medium' as TPktInputSize,
141
143
  onValueChange,
142
144
  onFocus: onFocusProp,
143
145
  onBlur: onBlurProp,
@@ -777,6 +779,7 @@ export function useTimepickerState(props: IPktTimepicker, ref: React.ForwardedRe
777
779
 
778
780
  const outerClasses = [
779
781
  'pkt-timepicker',
782
+ `pkt-timepicker--${inputSize}`,
780
783
  fullwidth ? 'pkt-timepicker--fullwidth' : '',
781
784
  stepArrows ? 'pkt-timepicker--stepper' : '',
782
785
  className ?? '',
@@ -788,6 +791,7 @@ export function useTimepickerState(props: IPktTimepicker, ref: React.ForwardedRe
788
791
  id,
789
792
  containerRef,
790
793
  outerClasses,
794
+ inputSize,
791
795
  strings,
792
796
  label,
793
797
  disabled,
@@ -1,10 +0,0 @@
1
- export declare const acquireModalScrollLock: (token: {
2
- locked: boolean;
3
- }) => void;
4
- export declare const releaseModalScrollLock: (token: {
5
- locked: boolean;
6
- }) => void;
7
- export type ModalScrollLockToken = {
8
- locked: boolean;
9
- };
10
- export declare const createModalScrollLockToken: () => ModalScrollLockToken;