@startupjs-ui/multi-select 0.2.0-alpha.1 → 0.3.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 CHANGED
@@ -3,6 +3,28 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [0.3.0](https://github.com/startupjs/startupjs-ui/compare/v0.2.3...v0.3.0) (2026-05-27)
7
+
8
+
9
+ ### Features
10
+
11
+ * [BREAKING] [0.3] improve accessibility props for E2E tests. Support testID everywhere ([#31](https://github.com/startupjs/startupjs-ui/issues/31)) ([882588c](https://github.com/startupjs/startupjs-ui/commit/882588ca37d5e1fd14b5717b5697cf9ed47042e4))
12
+
13
+
14
+
15
+
16
+
17
+ # [0.2.0](https://github.com/startupjs/startupjs-ui/compare/v0.1.23...v0.2.0) (2026-05-04)
18
+
19
+
20
+ ### Features
21
+
22
+ * [v0.2] add accessibility features to most components, which also dramatically improves E2E testability ([#22](https://github.com/startupjs/startupjs-ui/issues/22)) ([b563e2f](https://github.com/startupjs/startupjs-ui/commit/b563e2fa498a8a83bd5abc7836d120f6d2381e3f))
23
+
24
+
25
+
26
+
27
+
6
28
  # [0.2.0-alpha.1](https://github.com/startupjs/startupjs-ui/compare/v0.2.0-alpha.0...v0.2.0-alpha.1) (2026-04-10)
7
29
 
8
30
  **Note:** Version bump only for package @startupjs-ui/multi-select
package/index.d.ts CHANGED
@@ -26,6 +26,22 @@ export interface MultiSelectProps {
26
26
  disabled?: boolean;
27
27
  /** Render non-editable value @default false */
28
28
  readonly?: boolean;
29
+ /** Web id for label association */
30
+ id?: string;
31
+ /** Accessible name for the combobox trigger */
32
+ 'aria-label'?: string;
33
+ /** Element id that labels the combobox trigger */
34
+ 'aria-labelledby'?: string;
35
+ /** Element id that describes the combobox trigger */
36
+ 'aria-describedby'?: string;
37
+ /** Whether the combobox trigger value is invalid */
38
+ 'aria-invalid'?: boolean;
39
+ /** Element id for the related error message */
40
+ 'aria-errormessage'?: string;
41
+ /** Whether a value is required */
42
+ 'aria-required'?: boolean;
43
+ /** Test identifier for the combobox trigger */
44
+ testID?: string;
29
45
  /** Maximum number of visible tags (extra tags are collapsed) */
30
46
  tagLimit?: number;
31
47
  /** Behavior when tags are limited (legacy prop) @default 'hidden' */
package/index.tsx CHANGED
@@ -46,6 +46,22 @@ export interface MultiSelectProps {
46
46
  disabled?: boolean
47
47
  /** Render non-editable value @default false */
48
48
  readonly?: boolean
49
+ /** Web id for label association */
50
+ id?: string
51
+ /** Accessible name for the combobox trigger */
52
+ 'aria-label'?: string
53
+ /** Element id that labels the combobox trigger */
54
+ 'aria-labelledby'?: string
55
+ /** Element id that describes the combobox trigger */
56
+ 'aria-describedby'?: string
57
+ /** Whether the combobox trigger value is invalid */
58
+ 'aria-invalid'?: boolean
59
+ /** Element id for the related error message */
60
+ 'aria-errormessage'?: string
61
+ /** Whether a value is required */
62
+ 'aria-required'?: boolean
63
+ /** Test identifier for the combobox trigger */
64
+ testID?: string
49
65
  /** Maximum number of visible tags (extra tags are collapsed) */
50
66
  tagLimit?: number
51
67
  /** Behavior when tags are limited (legacy prop) @default 'hidden' */
@@ -84,6 +100,14 @@ function MultiSelect ({
84
100
  placeholder = 'Select',
85
101
  disabled = false,
86
102
  readonly = false,
103
+ id,
104
+ 'aria-label': ariaLabel,
105
+ 'aria-labelledby': ariaLabelledBy,
106
+ 'aria-describedby': ariaDescribedBy,
107
+ 'aria-invalid': ariaInvalid,
108
+ 'aria-errormessage': ariaErrorMessage,
109
+ 'aria-required': ariaRequired,
110
+ testID,
87
111
  tagLimitVariant = 'hidden',
88
112
  TagComponent = DefaultTag,
89
113
  InputComponent,
@@ -167,12 +191,16 @@ function MultiSelect ({
167
191
  const { label, value: itemValue } = item
168
192
  const selected = value.includes(itemValue)
169
193
  const onPress = onItemPress(itemValue, selected)
194
+ const optionLabel = label != null ? String(label) : String(itemValue)
170
195
 
171
196
  return pug`
172
197
  Div(
173
198
  key=itemValue
174
199
  vAlign='center'
175
200
  disabled=selected ? false : shouldDisableSelection
201
+ role='option'
202
+ aria-label=optionLabel
203
+ aria-selected=selected
176
204
  onPress=() => onPress(!selected)
177
205
  )
178
206
  if renderListItem
@@ -188,7 +216,7 @@ function MultiSelect ({
188
216
 
189
217
  function renderContent (): ReactNode {
190
218
  return pug`
191
- ScrollView.suggestions-web
219
+ ScrollView.suggestions-web(role='listbox')
192
220
  each option, index in normalizedOptions
193
221
  = _renderListItem({ item: option, index })
194
222
  `
@@ -214,6 +242,14 @@ function MultiSelect ({
214
242
  focused=focused
215
243
  value=value
216
244
  placeholder=placeholder
245
+ id=id
246
+ aria-label=ariaLabel
247
+ aria-labelledby=ariaLabelledBy
248
+ aria-describedby=ariaDescribedBy
249
+ aria-invalid=ariaInvalid
250
+ aria-errormessage=ariaErrorMessage
251
+ aria-required=ariaRequired
252
+ testID=testID
217
253
  tagLimit=tagLimit
218
254
  tagLimitVariant=tagLimitVariant
219
255
  options=normalizedOptions
@@ -235,6 +271,14 @@ function MultiSelect ({
235
271
  focused=focused
236
272
  value=value
237
273
  placeholder=placeholder
274
+ id=id
275
+ aria-label=ariaLabel
276
+ aria-labelledby=ariaLabelledBy
277
+ aria-describedby=ariaDescribedBy
278
+ aria-invalid=ariaInvalid
279
+ aria-errormessage=ariaErrorMessage
280
+ aria-required=ariaRequired
281
+ testID=testID
238
282
  tagLimit=tagLimit
239
283
  tagLimitVariant=tagLimitVariant
240
284
  options=normalizedOptions
@@ -261,6 +305,14 @@ function MultiSelectInput ({
261
305
  value,
262
306
  placeholder,
263
307
  options,
308
+ id,
309
+ 'aria-label': ariaLabel,
310
+ 'aria-labelledby': ariaLabelledBy,
311
+ 'aria-describedby': ariaDescribedBy,
312
+ 'aria-invalid': ariaInvalid,
313
+ 'aria-errormessage': ariaErrorMessage,
314
+ 'aria-required': ariaRequired,
315
+ testID,
264
316
  disabled,
265
317
  readonly,
266
318
  focused,
@@ -276,6 +328,14 @@ function MultiSelectInput ({
276
328
  value: any[]
277
329
  placeholder?: string
278
330
  options: MultiSelectOption[]
331
+ id?: string
332
+ 'aria-label'?: string
333
+ 'aria-labelledby'?: string
334
+ 'aria-describedby'?: string
335
+ 'aria-invalid'?: boolean
336
+ 'aria-errormessage'?: string
337
+ 'aria-required'?: boolean
338
+ testID?: string
279
339
  disabled?: boolean
280
340
  readonly?: boolean
281
341
  focused?: boolean
@@ -293,6 +353,12 @@ function MultiSelectInput ({
293
353
  : 0
294
354
 
295
355
  const EffectiveInputComponent = InputComponent ?? DefaultInput
356
+ const inferredAccessibleName = value.length
357
+ ? value
358
+ .map(value => options.find(option => option.value === value)?.label ?? value)
359
+ .join(', ')
360
+ : placeholder
361
+ const accessibleName = ariaLabel ?? (ariaLabelledBy ? undefined : inferredAccessibleName)
296
362
 
297
363
  return pug`
298
364
  EffectiveInputComponent(
@@ -300,6 +366,14 @@ function MultiSelectInput ({
300
366
  style=style
301
367
  value=values
302
368
  placeholder=placeholder
369
+ id=id
370
+ aria-label=accessibleName != null ? String(accessibleName) : undefined
371
+ aria-labelledby=ariaLabelledBy
372
+ aria-describedby=ariaDescribedBy
373
+ aria-invalid=ariaInvalid
374
+ aria-errormessage=ariaErrorMessage
375
+ aria-required=ariaRequired
376
+ testID=testID
303
377
  disabled=disabled
304
378
  focused=focused
305
379
  readonly=readonly
@@ -329,6 +403,14 @@ function DefaultInput ({
329
403
  style,
330
404
  value = [],
331
405
  placeholder,
406
+ id,
407
+ 'aria-label': ariaLabel,
408
+ 'aria-labelledby': ariaLabelledBy,
409
+ 'aria-describedby': ariaDescribedBy,
410
+ 'aria-invalid': ariaInvalid,
411
+ 'aria-errormessage': ariaErrorMessage,
412
+ 'aria-required': ariaRequired,
413
+ testID,
332
414
  disabled,
333
415
  focused,
334
416
  readonly,
@@ -341,6 +423,14 @@ function DefaultInput ({
341
423
  style?: StyleProp<ViewStyle>
342
424
  value?: any[]
343
425
  placeholder?: string
426
+ id?: string
427
+ 'aria-label'?: string
428
+ 'aria-labelledby'?: string
429
+ 'aria-describedby'?: string
430
+ 'aria-invalid'?: boolean
431
+ 'aria-errormessage'?: string
432
+ 'aria-required'?: boolean
433
+ testID?: string
344
434
  disabled?: boolean
345
435
  focused?: boolean
346
436
  readonly?: boolean
@@ -366,7 +456,17 @@ function DefaultInput ({
366
456
  Div.input(
367
457
  style=style
368
458
  styleName={ disabled, focused, readonly, error: _hasError }
369
- role='button'
459
+ id=id
460
+ role='combobox'
461
+ aria-label=ariaLabel
462
+ aria-labelledby=ariaLabelledBy
463
+ aria-describedby=ariaDescribedBy
464
+ aria-invalid=ariaInvalid
465
+ aria-errormessage=ariaErrorMessage
466
+ aria-required=ariaRequired
467
+ testID=testID
468
+ aria-expanded=!!focused
469
+ aria-haspopup='listbox'
370
470
  aria-disabled=disabled
371
471
  aria-readonly=readonly
372
472
  onPress=disabled || readonly ? undefined : onOpen
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startupjs-ui/multi-select",
3
- "version": "0.2.0-alpha.1",
3
+ "version": "0.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -8,19 +8,19 @@
8
8
  "types": "index.d.ts",
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "@startupjs-ui/core": "^0.2.0-alpha.1",
12
- "@startupjs-ui/div": "^0.2.0-alpha.1",
13
- "@startupjs-ui/drawer": "^0.2.0-alpha.1",
14
- "@startupjs-ui/icon": "^0.2.0-alpha.1",
15
- "@startupjs-ui/popover": "^0.2.0-alpha.1",
16
- "@startupjs-ui/scroll-view": "^0.2.0-alpha.1",
17
- "@startupjs-ui/span": "^0.2.0-alpha.1",
18
- "@startupjs-ui/tag": "^0.2.0-alpha.1"
11
+ "@startupjs-ui/core": "^0.3.0",
12
+ "@startupjs-ui/div": "^0.3.0",
13
+ "@startupjs-ui/drawer": "^0.3.0",
14
+ "@startupjs-ui/icon": "^0.3.0",
15
+ "@startupjs-ui/popover": "^0.3.0",
16
+ "@startupjs-ui/scroll-view": "^0.3.0",
17
+ "@startupjs-ui/span": "^0.3.0",
18
+ "@startupjs-ui/tag": "^0.3.0"
19
19
  },
20
20
  "peerDependencies": {
21
21
  "react": "*",
22
22
  "react-native": "*",
23
23
  "startupjs": "*"
24
24
  },
25
- "gitHead": "b48004779559b16c96a2a1995dab13b998eafce9"
25
+ "gitHead": "8d212b47680af1dfe582f9759b38724b46488e25"
26
26
  }