@startupjs-ui/div 0.2.0 → 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,17 @@
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
+
6
17
  # [0.2.0](https://github.com/startupjs/startupjs-ui/compare/v0.1.23...v0.2.0) (2026-05-04)
7
18
 
8
19
 
package/README.mdx CHANGED
@@ -8,7 +8,7 @@ import Icon from '@startupjs-ui/icon'
8
8
 
9
9
  # Div
10
10
 
11
- Div is a basic building block for layouts. It is a wrapper around `View` with extra features like press handling (`onPress`, `onLongPress`, `onPressIn`, `onPressOut`), layout helpers (`row`, `wrap`, `reverse`, `align`, `vAlign`, `gap`), shadows (`level`), corner shapes (`shape`), spacing (`pushed`, `bleed`), tooltips (`tooltip`, `tooltipStyle`), and built-in animation support. You can pass custom `style`, provide content as `children`, and use `ref` to access the underlying native view. Accessibility is supported via `accessible` and `accessibilityRole`, and `data-testid` can be used for testing.
11
+ Div is a basic building block for layouts. It is a wrapper around `View` with extra features like press handling (`onPress`, `onLongPress`, `onPressIn`, `onPressOut`), layout helpers (`row`, `wrap`, `reverse`, `align`, `vAlign`, `gap`), shadows (`level`), corner shapes (`shape`), spacing (`pushed`, `bleed`), tooltips (`tooltip`, `tooltipStyle`), and built-in animation support. You can pass custom `style`, provide content as `children`, and use `ref` to access the underlying native view. Accessibility and E2E semantics are exposed with `role`, `aria-*`, and `testID`.
12
12
 
13
13
  ```jsx
14
14
  import { Div } from 'startupjs-ui'
package/index.d.ts CHANGED
@@ -2,13 +2,19 @@
2
2
  // DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
3
3
 
4
4
  import { type ReactNode, type RefObject } from 'react';
5
- import { type StyleProp, type ViewStyle, type ViewProps, type AccessibilityRole } from 'react-native';
5
+ import { type StyleProp, type ViewStyle, type ViewProps } from 'react-native';
6
+ import { type UIRole } from '@startupjs-ui/core';
7
+ type AriaHasPopup = boolean | 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree';
6
8
  declare const _default: import("react").ComponentType<DivProps>;
7
9
  export default _default;
8
10
  export declare const _PropsJsonSchema: {};
9
- export interface DivProps extends ViewProps {
11
+ export interface DivProps extends Omit<ViewProps, 'role'> {
10
12
  /** Ref to access underlying <View> or <Pressable> */
11
13
  ref?: RefObject<any>;
14
+ /** Accessibility role. Includes RN roles plus web-only ARIA roles used by RNW. */
15
+ role?: UIRole;
16
+ /** Web popup type exposed through aria-haspopup */
17
+ 'aria-haspopup'?: AriaHasPopup;
12
18
  /** Custom styles applied to the root view */
13
19
  style?: StyleProp<ViewStyle>;
14
20
  /** Content rendered inside Div */
@@ -59,12 +65,8 @@ export interface DivProps extends ViewProps {
59
65
  onPressOut?: (e: any) => void;
60
66
  /** Whether view is accessible and focusable (if you can press it it's focusable by default) */
61
67
  accessible?: boolean;
62
- /** Accessibility role passed to native view (if you can press it it's a 'button') */
63
- accessibilityRole?: AccessibilityRole;
64
68
  /** Deprecated custom tooltip renderer @deprecated */
65
69
  renderTooltip?: any;
66
70
  /** Internal: render a native <button> host on web when the resolved role is button */
67
71
  _webNativeButton?: boolean;
68
- /** Test ID for testing purposes */
69
- 'data-testid'?: string;
70
72
  }
package/index.tsx CHANGED
@@ -6,17 +6,18 @@ import {
6
6
  StyleSheet,
7
7
  type StyleProp,
8
8
  type ViewStyle,
9
- type ViewProps,
10
- type AccessibilityRole
9
+ type ViewProps
11
10
  } from 'react-native'
12
11
  import Animated from 'react-native-reanimated'
13
12
  import { pug, observer, u, useDidUpdate } from 'startupjs'
14
- import { colorToRGBA, getCssVariable, themed } from '@startupjs-ui/core'
13
+ import { colorToRGBA, getCssVariable, themed, type UIRole } from '@startupjs-ui/core'
15
14
  import { useDecorateTooltipProps } from './useTooltip'
16
15
  import STYLES from './index.cssx.styl'
17
16
 
18
17
  const AnimatedPressable = Animated.createAnimatedComponent(Pressable)
19
18
 
19
+ type AriaHasPopup = boolean | 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree'
20
+
20
21
  const DEPRECATED_PUSHED_VALUES = ['xs', 'xl', 'xxl']
21
22
  const PRESSABLE_PROPS = ['onPress', 'onLongPress', 'onPressIn', 'onPressOut']
22
23
  const isWeb = Platform.OS === 'web'
@@ -33,9 +34,13 @@ export default observer(themed('Div', Div))
33
34
 
34
35
  export const _PropsJsonSchema = {/* DivProps */}
35
36
 
36
- export interface DivProps extends ViewProps {
37
+ export interface DivProps extends Omit<ViewProps, 'role'> {
37
38
  /** Ref to access underlying <View> or <Pressable> */
38
39
  ref?: RefObject<any>
40
+ /** Accessibility role. Includes RN roles plus web-only ARIA roles used by RNW. */
41
+ role?: UIRole
42
+ /** Web popup type exposed through aria-haspopup */
43
+ 'aria-haspopup'?: AriaHasPopup
39
44
  /** Custom styles applied to the root view */
40
45
  style?: StyleProp<ViewStyle>
41
46
  /** Content rendered inside Div */
@@ -86,14 +91,10 @@ export interface DivProps extends ViewProps {
86
91
  onPressOut?: (e: any) => void
87
92
  /** Whether view is accessible and focusable (if you can press it it's focusable by default) */
88
93
  accessible?: boolean
89
- /** Accessibility role passed to native view (if you can press it it's a 'button') */
90
- accessibilityRole?: AccessibilityRole
91
94
  /** Deprecated custom tooltip renderer @deprecated */
92
95
  renderTooltip?: any // Deprecated
93
96
  /** Internal: render a native <button> host on web when the resolved role is button */
94
97
  _webNativeButton?: boolean
95
- /** Test ID for testing purposes */
96
- 'data-testid'?: string
97
98
  }
98
99
 
99
100
  function Div ({
@@ -116,7 +117,6 @@ function Div ({
116
117
  bleed,
117
118
  full,
118
119
  accessible,
119
- accessibilityRole,
120
120
  tooltip,
121
121
  tooltipStyle,
122
122
  renderTooltip,
@@ -134,11 +134,10 @@ function Div ({
134
134
  const rootRef = ref ?? fallbackRef
135
135
 
136
136
  let pressableStyle: StyleProp<ViewStyle> = {}
137
- let deferredRole: AccessibilityRole | string | undefined
137
+ let deferredRole: string | undefined
138
138
  ;({
139
139
  props,
140
140
  pressableStyle,
141
- accessibilityRole,
142
141
  deferredRole
143
142
  } = useDecoratePressableProps({
144
143
  props,
@@ -148,22 +147,18 @@ function Div ({
148
147
  variant,
149
148
  isPressable,
150
149
  disabled,
151
- accessibilityRole,
152
150
  feedback,
153
151
  webNativeButton: _webNativeButton
154
152
  }))
155
153
 
156
154
  ;({
157
155
  props,
158
- accessible,
159
- accessibilityRole,
160
- deferredRole
156
+ accessible
161
157
  } = useDecorateAccessibilityProps({
162
158
  props,
163
159
  rootRef,
164
160
  disabled,
165
161
  accessible,
166
- accessibilityRole,
167
162
  isPressable,
168
163
  deferredRole,
169
164
  webNativeButton: _webNativeButton
@@ -193,7 +188,7 @@ function Div ({
193
188
  const Component = isPressable
194
189
  ? (isAnimated ? AnimatedPressable : Pressable)
195
190
  : (isAnimated ? Animated.View : View)
196
- const testID = props.testID ?? props['data-testid']
191
+ const renderProps = props as Omit<typeof props, 'role'> & { role?: ViewProps['role'] }
197
192
  const divElement = pug`
198
193
  Component.root(
199
194
  ref=rootRef
@@ -218,9 +213,7 @@ function Div ({
218
213
  levelModifier
219
214
  ]
220
215
  accessible=accessible
221
- accessibilityRole=accessibilityRole
222
- testID=testID
223
- ...props
216
+ ...renderProps
224
217
  )= children
225
218
  `
226
219
 
@@ -232,6 +225,10 @@ function Div ({
232
225
  } else return divElement
233
226
  }
234
227
 
228
+ function isWebOnlyRole (role: unknown): role is Exclude<UIRole, ViewProps['role']> {
229
+ return role === 'listbox' || role === 'gridcell'
230
+ }
231
+
235
232
  function hasAnimatedProperty (style: any): boolean {
236
233
  if (!style) return false
237
234
  return Object.keys(style).some(key => key.startsWith('animation') || key.startsWith('transition'))
@@ -242,7 +239,6 @@ function useDecorateAccessibilityProps ({
242
239
  rootRef,
243
240
  disabled,
244
241
  accessible,
245
- accessibilityRole,
246
242
  isPressable,
247
243
  deferredRole,
248
244
  webNativeButton
@@ -251,19 +247,15 @@ function useDecorateAccessibilityProps ({
251
247
  rootRef: RefObject<any>
252
248
  disabled?: boolean
253
249
  accessible?: boolean
254
- accessibilityRole?: AccessibilityRole
255
250
  isPressable: boolean
256
- deferredRole?: AccessibilityRole | string
251
+ deferredRole?: string
257
252
  webNativeButton?: boolean
258
253
  }): {
259
254
  props: Record<string, any>
260
255
  accessible?: boolean
261
- accessibilityRole?: AccessibilityRole
262
- deferredRole?: AccessibilityRole | string
263
256
  } {
264
257
  if (accessible == null && isPressable) accessible = true
265
258
  if (accessible === false) {
266
- accessibilityRole = undefined
267
259
  deferredRole = undefined
268
260
  props.role = undefined
269
261
  }
@@ -272,6 +264,9 @@ function useDecorateAccessibilityProps ({
272
264
  props['aria-disabled'] = disabled
273
265
  }
274
266
 
267
+ if (isNative && isWebOnlyRole(props.role)) delete props.role
268
+ if (isNative) delete props['aria-haspopup']
269
+
275
270
  const roleProp = props.role
276
271
  const ariaDisabled = props['aria-disabled']
277
272
 
@@ -301,7 +296,7 @@ function useDecorateAccessibilityProps ({
301
296
  }
302
297
  }, [rootRef, deferredRole, roleProp, ariaDisabled, webNativeButton, disabled])
303
298
 
304
- return { props, accessible, accessibilityRole, deferredRole }
299
+ return { props, accessible }
305
300
  }
306
301
 
307
302
  function useDecoratePressableProps ({
@@ -312,7 +307,6 @@ function useDecoratePressableProps ({
312
307
  variant,
313
308
  isPressable,
314
309
  disabled,
315
- accessibilityRole,
316
310
  feedback,
317
311
  webNativeButton
318
312
  }: {
@@ -323,17 +317,15 @@ function useDecoratePressableProps ({
323
317
  variant: 'opacity' | 'highlight'
324
318
  isPressable: boolean
325
319
  disabled?: boolean
326
- accessibilityRole?: AccessibilityRole
327
320
  feedback?: boolean
328
321
  webNativeButton?: boolean
329
322
  }): {
330
323
  props: Record<string, any>
331
324
  pressableStyle?: StyleProp<ViewStyle>
332
- accessibilityRole?: AccessibilityRole
333
- deferredRole?: AccessibilityRole | string
325
+ deferredRole?: string
334
326
  } {
335
327
  let pressableStyle: StyleProp<ViewStyle> = {}
336
- let deferredRole: AccessibilityRole | string | undefined
328
+ let deferredRole: string | undefined
337
329
  const [hover, setHover] = useState(false)
338
330
  const [active, setActive] = useState(false)
339
331
 
@@ -350,16 +342,14 @@ function useDecoratePressableProps ({
350
342
  // decorate the element state (hover, active) only if it's pressable
351
343
  if (!isPressable) return { props }
352
344
 
353
- const resolvedRole = props.role ?? accessibilityRole ?? 'button'
354
- accessibilityRole ??= typeof resolvedRole === 'string' ? resolvedRole as AccessibilityRole : undefined
345
+ const resolvedRole = props.role ?? 'button'
355
346
  props.focusable ??= true
356
347
 
357
348
  if (isWeb && resolvedRole === 'button' && !webNativeButton) {
358
349
  delete props.role
359
- accessibilityRole = undefined
360
350
  deferredRole = 'button'
361
351
  } else {
362
- props.role ??= accessibilityRole
352
+ props.role ??= resolvedRole
363
353
  }
364
354
 
365
355
  // setup hover and active states styles and props
@@ -406,7 +396,7 @@ function useDecoratePressableProps ({
406
396
  }
407
397
  }
408
398
 
409
- return { props, pressableStyle, accessibilityRole, deferredRole }
399
+ return { props, pressableStyle, deferredRole }
410
400
  }
411
401
 
412
402
  function getDefaultStyle (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startupjs-ui/div",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -12,9 +12,9 @@
12
12
  "./useTooltip": "./useTooltip.tsx"
13
13
  },
14
14
  "dependencies": {
15
- "@startupjs-ui/abstract-popover": "^0.2.0",
16
- "@startupjs-ui/core": "^0.2.0",
17
- "@startupjs-ui/span": "^0.2.0"
15
+ "@startupjs-ui/abstract-popover": "^0.3.0",
16
+ "@startupjs-ui/core": "^0.3.0",
17
+ "@startupjs-ui/span": "^0.3.0"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "react": "*",
@@ -22,5 +22,5 @@
22
22
  "react-native-reanimated": ">=4.0.0",
23
23
  "startupjs": "*"
24
24
  },
25
- "gitHead": "0c586b841cba1c9d820542f6eca07470f5ea2659"
25
+ "gitHead": "8d212b47680af1dfe582f9759b38724b46488e25"
26
26
  }