@applicaster/zapp-react-native-ui-components 13.0.10-rc.0 → 13.0.10-rc.2

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.
@@ -8,6 +8,7 @@ import { getItemType } from "@applicaster/zapp-react-native-utils/navigationUtil
8
8
  import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
9
9
  import { sendSelectCellEvent } from "@applicaster/zapp-react-native-utils/analyticsUtils";
10
10
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
11
+ import { CellFocusedStateContextProvider } from "@applicaster/zapp-react-native-ui-components/Contexts/CellFocusedStateContext";
11
12
 
12
13
  import { CellWithFocusable } from "./CellWithFocusable";
13
14
  import { BaseFocusable } from "../BaseFocusable";
@@ -15,6 +16,7 @@ import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUt
15
16
  import { styles } from "./styles";
16
17
 
17
18
  type Props = {
19
+ dataLength: number;
18
20
  item: ZappEntry;
19
21
  index: number;
20
22
  shouldScrollHorizontally: (arg1: [any]) => boolean | null | undefined;
@@ -67,9 +69,12 @@ type Props = {
67
69
 
68
70
  type State = {
69
71
  hasFocusableInside: boolean;
72
+ cellFocused: boolean;
70
73
  };
71
74
 
72
75
  export class CellComponent extends React.Component<Props, State> {
76
+ accessibilityManager: AccessibilityManager;
77
+
73
78
  constructor(props) {
74
79
  super(props);
75
80
  this.onPress = this.onPress.bind(this);
@@ -79,10 +84,14 @@ export class CellComponent extends React.Component<Props, State> {
79
84
  this.hasReceivedFocus = this.hasReceivedFocus.bind(this);
80
85
  this.scrollVertically = this.scrollVertically.bind(this);
81
86
  this.scrollToIndex = this.scrollToIndex.bind(this);
87
+ this.handleAccessibilityFocus = this.handleAccessibilityFocus.bind(this);
82
88
 
83
89
  this.state = {
84
90
  hasFocusableInside: props.CellRenderer.hasFocusableInside?.(props.item),
91
+ cellFocused: false,
85
92
  };
93
+
94
+ this.accessibilityManager = AccessibilityManager.getInstance();
86
95
  }
87
96
 
88
97
  setScreenLayout(componentAnchorPointY, screenLayout) {
@@ -130,6 +139,8 @@ export class CellComponent extends React.Component<Props, State> {
130
139
  } = this.props;
131
140
 
132
141
  if (isFocusable) {
142
+ this.setState({ cellFocused: true });
143
+
133
144
  if (
134
145
  shouldUpdate &&
135
146
  shouldScrollVertically?.(mouse, focusable, id, title)
@@ -139,7 +150,9 @@ export class CellComponent extends React.Component<Props, State> {
139
150
  }
140
151
  }
141
152
 
142
- onBlur() {}
153
+ onBlur() {
154
+ this.setState({ cellFocused: false });
155
+ }
143
156
 
144
157
  willReceiveFocus() {}
145
158
 
@@ -183,6 +196,25 @@ export class CellComponent extends React.Component<Props, State> {
183
196
  return !isFocusable ? false : focused || focusableFocused;
184
197
  }
185
198
 
199
+ handleAccessibilityFocus(index, dataLength) {
200
+ // For loop scrolling, calculate the correct logical index
201
+ const logicalIndex = dataLength ? index % dataLength : index;
202
+
203
+ const positionLabel = dataLength
204
+ ? `item ${logicalIndex + 1} of ${dataLength}`
205
+ : "";
206
+
207
+ if (this.state.hasFocusableInside) {
208
+ this.accessibilityManager.readText({
209
+ text: " ",
210
+ });
211
+ } else {
212
+ this.accessibilityManager.readText({
213
+ text: `${positionLabel}`,
214
+ });
215
+ }
216
+ }
217
+
186
218
  componentDidUpdate(prevProps: Readonly<Props>) {
187
219
  if (prevProps.item !== this.props.item) {
188
220
  this.setState({
@@ -191,6 +223,8 @@ export class CellComponent extends React.Component<Props, State> {
191
223
  ),
192
224
  });
193
225
  }
226
+
227
+ this.handleAccessibilityFocus(this.props.index, this.props.dataLength);
194
228
  }
195
229
 
196
230
  render() {
@@ -212,7 +246,6 @@ export class CellComponent extends React.Component<Props, State> {
212
246
  } = this.props;
213
247
 
214
248
  const { id } = item;
215
-
216
249
  const focusableId = join("-", [component?.id, id, index]);
217
250
 
218
251
  const handleFocus = (focusable, mouse) => {
@@ -223,73 +256,67 @@ export class CellComponent extends React.Component<Props, State> {
223
256
 
224
257
  if (this.state.hasFocusableInside) {
225
258
  return (
226
- <CellWithFocusable
227
- CellRenderer={CellRenderer}
228
- item={item}
229
- id={focusableId}
230
- groupId={groupId || component?.id}
231
- onFocus={handleFocus}
232
- index={index}
233
- scrollTo={this.scrollToIndex()}
234
- isFocusable={isFocusable}
235
- skipFocusManagerRegistration={skipFocusManagerRegistration}
236
- behavior={behavior}
237
- />
259
+ <CellFocusedStateContextProvider cellFocused={this.state.cellFocused}>
260
+ <CellWithFocusable
261
+ CellRenderer={CellRenderer}
262
+ item={item}
263
+ id={focusableId}
264
+ groupId={groupId || component?.id}
265
+ onFocus={handleFocus}
266
+ onBlur={onBlur || this.onBlur}
267
+ index={index}
268
+ scrollTo={this.scrollToIndex()}
269
+ isFocusable={isFocusable}
270
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
271
+ behavior={behavior}
272
+ />
273
+ </CellFocusedStateContextProvider>
238
274
  );
239
275
  }
240
276
 
241
277
  return (
242
- <View
243
- testID={`${component?.id}-${id}`}
244
- accessible={false}
245
- style={styles.touchableCell}
246
- >
247
- <Focusable
248
- id={focusableId}
249
- groupId={groupId || component?.id}
250
- onFocus={handleFocus}
251
- onBlur={onBlur || this.onBlur}
252
- onPress={this.onPress}
253
- willReceiveFocus={willReceiveFocus || this.willReceiveFocus}
254
- hasReceivedFocus={hasReceivedFocus || this.hasReceivedFocus}
255
- preferredFocus={preferredFocus}
256
- offsetUpdater={offsetUpdater}
257
- style={styles.baseCell}
258
- isFocusable={isFocusable}
259
- skipFocusManagerRegistration={skipFocusManagerRegistration}
278
+ <CellFocusedStateContextProvider cellFocused={this.state.cellFocused}>
279
+ <View
280
+ testID={`${component?.id}-${id}`}
281
+ accessible={false}
282
+ style={styles.touchableCell}
260
283
  >
261
- {(focused, event) => {
262
- const isFocused = this.isCellFocused(focused);
263
-
264
- if (isFocused) {
265
- const accessibilityManager = AccessibilityManager.getInstance();
266
-
267
- const accessibilityTitle =
268
- item?.extensions?.accessibility?.label || item?.title || "";
269
-
270
- const accessibilityHint =
271
- item?.extensions?.accessibility?.hint || "";
272
-
273
- accessibilityManager.readText({
274
- text: `${accessibilityTitle} ${accessibilityHint}`,
275
- });
276
- }
277
-
278
- return (
279
- <FocusableCell
280
- {...{
281
- index,
282
- CellRenderer,
283
- item,
284
- focused: isFocused,
285
- scrollTo: this.scrollToIndex(event),
286
- behavior,
287
- }}
288
- />
289
- );
290
- }}
291
- </Focusable>
292
- </View>
284
+ <Focusable
285
+ id={focusableId}
286
+ groupId={groupId || component?.id}
287
+ onFocus={handleFocus}
288
+ onBlur={onBlur || this.onBlur}
289
+ onPress={this.onPress}
290
+ willReceiveFocus={willReceiveFocus || this.willReceiveFocus}
291
+ hasReceivedFocus={hasReceivedFocus || this.hasReceivedFocus}
292
+ preferredFocus={preferredFocus}
293
+ offsetUpdater={offsetUpdater}
294
+ style={styles.baseCell}
295
+ isFocusable={isFocusable}
296
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
297
+ {...this.accessibilityManager.getButtonAccessibilityProps(
298
+ item?.extensions?.accessibility?.label || item?.title
299
+ )}
300
+ >
301
+ {(focused, event) => {
302
+ const isFocused = this.isCellFocused(focused);
303
+
304
+ return (
305
+ <FocusableCell
306
+ {...{
307
+ index,
308
+ CellRenderer,
309
+ item,
310
+ focused: isFocused,
311
+ scrollTo: this.scrollToIndex(event),
312
+ behavior,
313
+ }}
314
+ />
315
+ );
316
+ }}
317
+ </Focusable>
318
+ </View>
319
+ </CellFocusedStateContextProvider>
293
320
  );
294
321
  }
295
322
  }
@@ -14,6 +14,7 @@ type Props = {
14
14
  id: string;
15
15
  groupId: string;
16
16
  onFocus: Function;
17
+ onBlur?: Function;
17
18
  index: number;
18
19
  scrollTo: Function;
19
20
  preferredFocus?: boolean;
@@ -33,6 +34,7 @@ export function CellWithFocusable(props: Props) {
33
34
  id,
34
35
  groupId,
35
36
  onFocus,
37
+ onBlur = noop,
36
38
  scrollTo = noop,
37
39
  preferredFocus,
38
40
  skipFocusManagerRegistration,
@@ -78,6 +80,7 @@ export function CellWithFocusable(props: Props) {
78
80
  const onGroupBlur = React.useCallback(() => {
79
81
  if (!skipFocusManagerRegistration) {
80
82
  setIsFocused(false);
83
+ onBlur?.();
81
84
  }
82
85
  }, [skipFocusManagerRegistration]);
83
86
 
@@ -5,6 +5,7 @@ import { BaseFocusable } from "../BaseFocusable";
5
5
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
6
6
  import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
7
7
  import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
8
+ import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
8
9
 
9
10
  type Props = {
10
11
  initialFocus?: boolean;
@@ -28,6 +29,7 @@ class Focusable extends BaseFocusable<Props> {
28
29
  isGroup: boolean;
29
30
  mouse: boolean;
30
31
  longPressTimeout = null;
32
+ accessibilityManager: AccessibilityManager;
31
33
 
32
34
  constructor(props) {
33
35
  super(props);
@@ -42,6 +44,8 @@ class Focusable extends BaseFocusable<Props> {
42
44
  this.resetLongPressTimeout = this.resetLongPressTimeout.bind(this);
43
45
  this.longPress = this.longPress.bind(this);
44
46
  this.press = this.press.bind(this);
47
+
48
+ this.accessibilityManager = AccessibilityManager.getInstance();
45
49
  }
46
50
 
47
51
  /**
@@ -128,6 +132,9 @@ class Focusable extends BaseFocusable<Props> {
128
132
  const id = this.getId();
129
133
  const focusableId = `focusable-${id}`;
130
134
 
135
+ const accessibilityProps =
136
+ this.accessibilityManager.getWebAccessibilityProps(this.props);
137
+
131
138
  return (
132
139
  <div
133
140
  id={focusableId}
@@ -140,6 +147,7 @@ class Focusable extends BaseFocusable<Props> {
140
147
  data-testid={focusableId}
141
148
  focused-teststate={focused ? "focused" : "default"}
142
149
  style={style}
150
+ {...accessibilityProps}
143
151
  >
144
152
  {children(focused, { mouse: this.mouse })}
145
153
  </div>
@@ -5,7 +5,6 @@ import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/acti
5
5
  import * as R from "ramda";
6
6
  import { getXray } from "@applicaster/zapp-react-native-utils/logger";
7
7
  import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
8
- import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
9
8
 
10
9
  const { Logger } = getXray();
11
10
 
@@ -45,11 +44,6 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
45
44
 
46
45
  const actionContext = useActions(pluginIdentifier);
47
46
 
48
- const accessibilityManager = useAccessibilityManager({});
49
-
50
- const { ttsLabel = "" } =
51
- actionContext?.initialEntryState(item)?.getAccessibility?.(item) || {};
52
-
53
47
  const onPress = (event) => {
54
48
  event?.preventDefault?.();
55
49
 
@@ -94,12 +88,6 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
94
88
  mouse: focusable.mouse,
95
89
  });
96
90
  });
97
-
98
- if (ttsLabel) {
99
- accessibilityManager.readText({
100
- text: ttsLabel,
101
- });
102
- }
103
91
  };
104
92
 
105
93
  const handleBlur = (_focusable, _direction) => {
@@ -11,6 +11,8 @@ import { withScaledLineHeight } from "./utils";
11
11
  import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
12
12
  import { MeasurementPortalContext } from "../../../MeasurmentsPortal";
13
13
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
14
+ import { CellFocusedStateContext } from "@applicaster/zapp-react-native-ui-components/Contexts/CellFocusedStateContext";
15
+ import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
14
16
 
15
17
  type Props = {
16
18
  style: any;
@@ -35,10 +37,9 @@ const _Text = ({
35
37
  }: Props) => {
36
38
  const _label = useTextLabel({ label, entry });
37
39
  const isMeasurement = React.useContext(MeasurementPortalContext);
40
+ const cellFocused = React.useContext(CellFocusedStateContext);
38
41
 
39
- if (isNilOrEmpty(_label)) {
40
- return null;
41
- }
42
+ const accessibilityManager = useAccessibilityManager({});
42
43
 
43
44
  // set maximum possible height for the text in case of measurement
44
45
  const height =
@@ -46,6 +47,27 @@ const _Text = ({
46
47
  ? toNumber(otherProps.numberOfLines) * toNumber(style.lineHeight)
47
48
  : undefined;
48
49
 
50
+ const textLabel = dateTransformEnabled
51
+ ? dateFormat(dateTransform, label)
52
+ : textTransform(transformText, _label);
53
+
54
+ React.useLayoutEffect(() => {
55
+ // For FocusableCells with action buttons
56
+ if (otherProps.state) {
57
+ if (otherProps.state === "focused" && cellFocused === true) {
58
+ accessibilityManager.addHeading(textLabel);
59
+ }
60
+ } else {
61
+ if (cellFocused === true) {
62
+ accessibilityManager.addHeading(textLabel);
63
+ }
64
+ }
65
+ }, [cellFocused, otherProps.state, textLabel]);
66
+
67
+ if (isNilOrEmpty(_label)) {
68
+ return null;
69
+ }
70
+
49
71
  return (
50
72
  <Text
51
73
  style={[
@@ -54,9 +76,7 @@ const _Text = ({
54
76
  ]}
55
77
  {...withoutLabel(otherProps)}
56
78
  >
57
- {dateTransformEnabled
58
- ? dateFormat(dateTransform, label)
59
- : textTransform(transformText, _label)}
79
+ {textLabel}
60
80
  </Text>
61
81
  );
62
82
  };
@@ -2,6 +2,19 @@
2
2
 
3
3
  exports[`<TextInputTv /> renders 1`] = `
4
4
  <input
5
+ accessibilityProps={
6
+ {
7
+ "accessibilityHint": "Enter text into Search",
8
+ "accessibilityLabel": "Search",
9
+ "accessibilityRole": "text",
10
+ "accessible": true,
11
+ "aria-description": "Enter text into Search",
12
+ "aria-label": "Search",
13
+ "aria-role": "text",
14
+ "role": "text",
15
+ "tabindex": 0,
16
+ }
17
+ }
5
18
  testID="TextInput-tv"
6
19
  />
7
20
  `;
@@ -4,6 +4,7 @@ import { Appearance, Platform, StyleSheet, TextInput } from "react-native";
4
4
  import { isFunction } from "@applicaster/zapp-react-native-utils/functionUtils";
5
5
  import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
6
6
  import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
7
+ import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
7
8
 
8
9
  type Props = Partial<{
9
10
  style: any;
@@ -42,6 +43,8 @@ function TextInputTV(props: Props, ref) {
42
43
  const [colorScheme, setColorScheme] = useState(getInitialColorScheme());
43
44
  const isRTL = useIsRTL();
44
45
 
46
+ const accessibilityManager = useAccessibilityManager({});
47
+
45
48
  const onColorChange = useCallback(
46
49
  ({ colorScheme: color }) => {
47
50
  if (color !== colorScheme) {
@@ -153,6 +156,13 @@ function TextInputTV(props: Props, ref) {
153
156
  ])
154
157
  )(props);
155
158
 
159
+ const getAccessibilityProps = () => {
160
+ return {
161
+ accessibilityProps:
162
+ accessibilityManager.getInputAccessibilityProps("Search"),
163
+ };
164
+ };
165
+
156
166
  const inputProps = {
157
167
  ...getProps(),
158
168
  ...getStyle(),
@@ -161,6 +171,7 @@ function TextInputTV(props: Props, ref) {
161
171
  ...getSecureTextEntry(),
162
172
  ...getOnEndEditing(),
163
173
  ...getOnPress(),
174
+ ...getAccessibilityProps(),
164
175
  };
165
176
 
166
177
  if (
@@ -0,0 +1,27 @@
1
+ import React, { ReactNode } from "react";
2
+
3
+ export const CellFocusedStateContext = React.createContext<boolean>(false);
4
+
5
+ export const CellFocusedStateContextProvider = ({
6
+ cellFocused,
7
+ children,
8
+ }: {
9
+ cellFocused: boolean;
10
+ children: ReactNode;
11
+ }) => {
12
+ return (
13
+ <CellFocusedStateContext.Provider value={cellFocused}>
14
+ {children}
15
+ </CellFocusedStateContext.Provider>
16
+ );
17
+ };
18
+
19
+ export function withCellFocusedStateContext(
20
+ Component: React.ComponentType<any>
21
+ ) {
22
+ return function WithCellFocusedStateContextWrapper(props) {
23
+ const cellFocusedState = React.useContext(CellFocusedStateContext);
24
+
25
+ return <Component {...props} cellFocusedState={cellFocusedState} />;
26
+ };
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-ui-components",
3
- "version": "13.0.10-rc.0",
3
+ "version": "13.0.10-rc.2",
4
4
  "description": "Applicaster Zapp React Native ui components for the Quick Brick App",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -31,10 +31,10 @@
31
31
  "redux-mock-store": "^1.5.3"
32
32
  },
33
33
  "dependencies": {
34
- "@applicaster/applicaster-types": "13.0.10-rc.0",
35
- "@applicaster/zapp-react-native-bridge": "13.0.10-rc.0",
36
- "@applicaster/zapp-react-native-redux": "13.0.10-rc.0",
37
- "@applicaster/zapp-react-native-utils": "13.0.10-rc.0",
34
+ "@applicaster/applicaster-types": "13.0.10-rc.2",
35
+ "@applicaster/zapp-react-native-bridge": "13.0.10-rc.2",
36
+ "@applicaster/zapp-react-native-redux": "13.0.10-rc.2",
37
+ "@applicaster/zapp-react-native-utils": "13.0.10-rc.2",
38
38
  "promise": "^8.3.0",
39
39
  "react-router-native": "^5.1.2",
40
40
  "url": "^0.11.0",