@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 +11 -0
- package/README.mdx +1 -1
- package/index.d.ts +8 -6
- package/index.tsx +27 -37
- package/package.json +5 -5
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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?:
|
|
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
|
|
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
|
-
|
|
333
|
-
deferredRole?: AccessibilityRole | string
|
|
325
|
+
deferredRole?: string
|
|
334
326
|
} {
|
|
335
327
|
let pressableStyle: StyleProp<ViewStyle> = {}
|
|
336
|
-
let deferredRole:
|
|
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 ??
|
|
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 ??=
|
|
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,
|
|
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.
|
|
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.
|
|
16
|
-
"@startupjs-ui/core": "^0.
|
|
17
|
-
"@startupjs-ui/span": "^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": "
|
|
25
|
+
"gitHead": "8d212b47680af1dfe582f9759b38724b46488e25"
|
|
26
26
|
}
|