@applicaster/zapp-react-native-utils 13.0.6-alpha.1053258310 → 13.0.6-alpha.4144634915

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,10 +8,8 @@ import _ from "lodash";
8
8
  export class AccessibilityManager {
9
9
  private static _instance: AccessibilityManager | null = null;
10
10
  private headingTimeout: NodeJS.Timeout | null = null;
11
- private announcementDelayTimeout: NodeJS.Timeout | null = null;
12
11
  private WORDS_PER_MINUTE = 160;
13
12
  private MINIMUM_PAUSE = 500;
14
- private ANNOUNCEMENT_DELAY = 200; // 200ms delay to reduce noise during rapid navigation
15
13
  private state$ = new BehaviorSubject<AccessibilityState>({
16
14
  screenReaderEnabled: false,
17
15
  reduceMotionEnabled: false,
@@ -84,9 +82,6 @@ export class AccessibilityManager {
84
82
  /**
85
83
  * text you want to be read, if you want to use localized text pass keyOfLocalizedText instead
86
84
  * keyOfLocalizedText is the key to the localized text
87
- *
88
- * Implements a delay mechanism to reduce noise during rapid navigation.
89
- * Only the most recent announcement will be read after the delay period.
90
85
  */
91
86
  public readText({
92
87
  text,
@@ -117,21 +112,6 @@ export class AccessibilityManager {
117
112
  textToRead = localizedMessage;
118
113
  }
119
114
 
120
- // Clear any existing announcement delay timeout to cancel previous announcements
121
- if (this.announcementDelayTimeout) {
122
- clearTimeout(this.announcementDelayTimeout);
123
- }
124
-
125
- // Set a new delay timeout for this announcement
126
- this.announcementDelayTimeout = setTimeout(() => {
127
- this.executeAnnouncement(textToRead, keyOfLocalizedText);
128
- }, this.ANNOUNCEMENT_DELAY);
129
- }
130
-
131
- /**
132
- * Internal method to execute the actual announcement after the delay
133
- */
134
- private executeAnnouncement(textToRead: string, keyOfLocalizedText?: string) {
135
115
  if (this.headingQueue.length > 0) {
136
116
  const heading = this.headingQueue.shift()!;
137
117
  this.ttsManager?.readText(heading);
@@ -163,15 +143,12 @@ export class AccessibilityManager {
163
143
 
164
144
  if (!buttonConfig) {
165
145
  return {
166
- accessible: true,
167
146
  accessibilityLabel: buttonName,
168
147
  accessibilityHint: `Press button to perform action on ${buttonName}`,
169
148
  "aria-label": buttonName,
170
149
  "aria-description": `Press button to perform action on ${buttonName}`,
171
150
  accessibilityRole: "button" as AccessibilityRole,
172
151
  "aria-role": "button",
173
- role: "button",
174
- tabindex: 0,
175
152
  };
176
153
  }
177
154
 
@@ -185,52 +162,23 @@ export class AccessibilityManager {
185
162
  `Press button to perform action on ${buttonName}`;
186
163
 
187
164
  return {
188
- accessible: true,
189
165
  accessibilityLabel: label,
190
166
  accessibilityHint: hint,
191
167
  "aria-label": label,
192
168
  "aria-description": hint,
193
169
  accessibilityRole: "button" as AccessibilityRole,
194
170
  "aria-role": "button",
195
- role: "button",
196
- tabindex: 0,
197
171
  };
198
172
  }
199
173
 
200
174
  public getInputAccessibilityProps(inputName: string): AccessibilityProps {
201
175
  return {
202
- accessible: true,
203
176
  accessibilityLabel: inputName,
204
177
  accessibilityHint: `Enter text into ${inputName}`,
205
178
  "aria-label": inputName,
206
179
  "aria-description": `Enter text into ${inputName}`,
207
- accessibilityRole: "text" as AccessibilityRole,
208
- "aria-role": "text",
209
- role: "text",
210
- tabindex: 0,
211
- };
212
- }
213
-
214
- /**
215
- * Extracts accessibility props from component props and returns them as HTML attributes
216
- * @param props - Component props containing accessibility properties
217
- * @returns Object with accessibility HTML attributes
218
- */
219
- public getWebAccessibilityProps(props: any): AccessibilityProps {
220
- const {
221
- "aria-label": ariaLabel,
222
- "aria-description": ariaDescription,
223
- "aria-role": ariaRole,
224
- role,
225
- tabindex,
226
- } = props;
227
-
228
- return {
229
- "aria-label": ariaLabel,
230
- "aria-description": ariaDescription,
231
- "aria-role": ariaRole,
232
- role: role || ariaRole,
233
- tabindex,
180
+ accessibilityRole: "textbox" as AccessibilityRole,
181
+ "aria-role": "textbox",
234
182
  };
235
183
  }
236
184
 
@@ -248,39 +196,4 @@ export class AccessibilityManager {
248
196
 
249
197
  return this.localizations[key];
250
198
  }
251
-
252
- /**
253
- * Sets the announcement delay to reduce noise during rapid navigation
254
- * @param delay - Delay in milliseconds (default: 150ms)
255
- */
256
- public setAnnouncementDelay(delay: number) {
257
- this.ANNOUNCEMENT_DELAY = Math.max(0, delay);
258
- }
259
-
260
- /**
261
- * Gets the current announcement delay setting
262
- * @returns Current delay in milliseconds
263
- */
264
- public getAnnouncementDelay(): number {
265
- return this.ANNOUNCEMENT_DELAY;
266
- }
267
-
268
- /**
269
- * Cancels any pending announcement that hasn't been executed yet
270
- * Useful for clearing announcements when navigation is interrupted
271
- */
272
- public cancelPendingAnnouncement() {
273
- if (this.announcementDelayTimeout) {
274
- clearTimeout(this.announcementDelayTimeout);
275
- this.announcementDelayTimeout = null;
276
- }
277
- }
278
-
279
- /**
280
- * Resets the TTSManager instance (mainly for testing purposes)
281
- * @private
282
- */
283
- public _resetTTSManager() {
284
- this.ttsManager = TTSManager.getInstance();
285
- }
286
199
  }
@@ -214,7 +214,6 @@ export class TTSManager {
214
214
  readText(text: string) {
215
215
  if (isSamsungPlatform() && window.speechSynthesis) {
216
216
  const utterance = new SpeechSynthesisUtterance(text);
217
- window.speechSynthesis.cancel(); // Cancel previous speech before speaking new text
218
217
  window.speechSynthesis.speak(utterance);
219
218
  }
220
219
 
@@ -234,7 +233,6 @@ export class TTSManager {
234
233
  },
235
234
  parameters: {
236
235
  text,
237
- clear: true, // Clear any pending speech before speaking new text
238
236
  },
239
237
  });
240
238
  } catch (error) {
package/index.d.ts CHANGED
@@ -139,13 +139,10 @@ declare type AccessibilityState = {
139
139
  };
140
140
 
141
141
  declare type AccessibilityProps = {
142
- accessible?: boolean;
143
142
  accessibilityLabel?: string;
144
143
  accessibilityHint?: string;
145
144
  "aria-label"?: string;
146
145
  "aria-description"?: string;
147
146
  accessibilityRole?: string;
148
147
  "aria-role"?: string;
149
- role?: string;
150
- tabindex?: number;
151
148
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "13.0.6-alpha.1053258310",
3
+ "version": "13.0.6-alpha.4144634915",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "homepage": "https://github.com/applicaster/quickbrick#readme",
29
29
  "dependencies": {
30
- "@applicaster/applicaster-types": "13.0.6-alpha.1053258310",
30
+ "@applicaster/applicaster-types": "13.0.6-alpha.4144634915",
31
31
  "buffer": "^5.2.1",
32
32
  "camelize": "^1.0.0",
33
33
  "dayjs": "^1.11.10",
@@ -5,6 +5,7 @@ import { isFilledArray } from "@applicaster/zapp-react-native-utils/arrayUtils";
5
5
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
6
6
 
7
7
  import { getBoolFromConfigValue } from "../configurationUtils";
8
+ import { Dimensions } from "react-native";
8
9
 
9
10
  export { getPlayerActionButtons } from "./getPlayerActionButtons";
10
11
 
@@ -97,3 +98,53 @@ export const isAudioItem = (item: Option<ZappEntry>) => {
97
98
  export const isInlineTV = (screenData) => {
98
99
  return isTV() && isFilledArray(screenData?.ui_components);
99
100
  };
101
+
102
+ const isPercentage = (value: string | number): boolean => {
103
+ if (typeof value === "string") {
104
+ return value.includes("%");
105
+ }
106
+
107
+ return false;
108
+ };
109
+
110
+ const getPercentageOf = (percent: string, value: number) => {
111
+ const percentageValue = parseFloat(percent.replace("%", ""));
112
+
113
+ if (isNaN(percentageValue)) {
114
+ return value;
115
+ }
116
+
117
+ return (value * percentageValue) / 100;
118
+ };
119
+
120
+ type DimensionsT = {
121
+ width: number | string;
122
+ height: number | string | undefined;
123
+ aspectRatio?: number;
124
+ };
125
+
126
+ export const getTabletWidth = (
127
+ tablet_landscape_sidebar_width,
128
+ dimensions: DimensionsT
129
+ ) => {
130
+ const { width: SCREEN_WIDTH } = Dimensions.get("screen");
131
+
132
+ const { width } = dimensions;
133
+ let widthValue = Number(width);
134
+
135
+ if (isPercentage(width)) {
136
+ widthValue = getPercentageOf(width.toString(), SCREEN_WIDTH);
137
+ }
138
+
139
+ const sidebarWidth = Number(tablet_landscape_sidebar_width?.replace("%", ""));
140
+
141
+ if (tablet_landscape_sidebar_width?.includes("%")) {
142
+ return widthValue * (1 - sidebarWidth / 100);
143
+ }
144
+
145
+ if (Number.isNaN(sidebarWidth)) {
146
+ return widthValue * 0.65;
147
+ }
148
+
149
+ return widthValue - sidebarWidth;
150
+ };
@@ -1,5 +1,11 @@
1
1
  import { NativeModules, Platform } from "react-native";
2
2
 
3
+ export const isAndroidTablet = () => {
4
+ const { initialProps } = NativeModules.QuickBrickCommunicationModule;
5
+
6
+ return initialProps?.is_tablet;
7
+ };
8
+
3
9
  /**
4
10
  * Determines wether the given device is a tablet based on dimensions, orientation, and platform
5
11
  * @param {Object} dimensions - Dimensions object passed to the function
@@ -13,9 +19,7 @@ export const isTablet = (
13
19
  if (Platform?.OS === "ios") {
14
20
  return Platform?.isPad;
15
21
  } else if (Platform?.OS === "android") {
16
- const { initialProps } = NativeModules.QuickBrickCommunicationModule;
17
-
18
- return initialProps?.is_tablet;
22
+ return isAndroidTablet();
19
23
  }
20
24
 
21
25
  const { width } = dimensions || {};
@@ -1,114 +0,0 @@
1
- import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
2
-
3
- const mockReadText = jest.fn();
4
-
5
- jest.mock(
6
- "@applicaster/zapp-react-native-utils/appUtils/platform/platformUtils",
7
- () => ({
8
- TTSManager: {
9
- getInstance: jest.fn(() => ({
10
- readText: mockReadText,
11
- })),
12
- },
13
- })
14
- );
15
-
16
- describe("AccessibilityManager", () => {
17
- let accessibilityManager: AccessibilityManager;
18
-
19
- beforeEach(() => {
20
- jest.clearAllMocks();
21
- jest.useFakeTimers();
22
-
23
- (AccessibilityManager as any)._instance = null;
24
-
25
- accessibilityManager = AccessibilityManager.getInstance();
26
-
27
- accessibilityManager._resetTTSManager();
28
- });
29
-
30
- afterEach(() => {
31
- jest.useRealTimers();
32
- });
33
-
34
- describe("announcement delay functionality", () => {
35
- it("should delay announcements by the configured delay time", () => {
36
- const testText = "Test announcement";
37
-
38
- accessibilityManager.readText({ text: testText });
39
-
40
- expect(mockReadText).not.toHaveBeenCalled();
41
-
42
- jest.advanceTimersByTime(150);
43
- expect(mockReadText).not.toHaveBeenCalled();
44
-
45
- jest.advanceTimersByTime(50);
46
- expect(mockReadText).toHaveBeenCalledWith(testText);
47
- });
48
-
49
- it("should cancel previous announcements when a new one is requested", () => {
50
- const firstText = "First announcement";
51
- const secondText = "Second announcement";
52
-
53
- accessibilityManager.readText({ text: firstText });
54
-
55
- jest.advanceTimersByTime(150);
56
-
57
- accessibilityManager.readText({ text: secondText });
58
-
59
- jest.advanceTimersByTime(200);
60
-
61
- expect(mockReadText).toHaveBeenCalledTimes(1);
62
- expect(mockReadText).toHaveBeenCalledWith(secondText);
63
- });
64
-
65
- it("should allow configuring the announcement delay", () => {
66
- const customDelay = 300;
67
- accessibilityManager.setAnnouncementDelay(customDelay);
68
-
69
- expect(accessibilityManager.getAnnouncementDelay()).toBe(customDelay);
70
-
71
- const testText = "Test announcement";
72
- accessibilityManager.readText({ text: testText });
73
-
74
- expect(mockReadText).not.toHaveBeenCalled();
75
-
76
- jest.advanceTimersByTime(250);
77
- expect(mockReadText).not.toHaveBeenCalled();
78
-
79
- jest.advanceTimersByTime(50);
80
- expect(mockReadText).toHaveBeenCalledWith(testText);
81
- });
82
-
83
- it("should cancel pending announcements when cancelPendingAnnouncement is called", () => {
84
- const testText = "Test announcement";
85
-
86
- accessibilityManager.readText({ text: testText });
87
-
88
- accessibilityManager.cancelPendingAnnouncement();
89
-
90
- jest.advanceTimersByTime(250);
91
-
92
- expect(mockReadText).not.toHaveBeenCalled();
93
- });
94
-
95
- it("should handle localized text with delay", () => {
96
- const localizations = {
97
- test_key: "Localized text",
98
- };
99
-
100
- accessibilityManager.updateLocalizations(localizations);
101
-
102
- accessibilityManager.readText({
103
- text: "fallback",
104
- keyOfLocalizedText: "test_key",
105
- });
106
-
107
- expect(mockReadText).not.toHaveBeenCalled();
108
-
109
- jest.advanceTimersByTime(200);
110
-
111
- expect(mockReadText).toHaveBeenCalledWith("Localized text");
112
- });
113
- });
114
- });
@@ -1,76 +0,0 @@
1
- # Accessibility Manager - Announcement Delay Feature
2
-
3
- ## Overview
4
-
5
- The Accessibility Manager now includes a delay mechanism to reduce noise during rapid navigation. This feature ensures that only elements the user actually lands on are announced, preventing excessive screen reader announcements during fast navigation.
6
-
7
- ## Implementation Details
8
-
9
- ### Key Features
10
-
11
- 1. **Configurable Delay**: Default delay of 200ms (configurable via `setAnnouncementDelay()`)
12
- 2. **Cancellation of Previous Announcements**: New announcements cancel pending ones
13
- 3. **Manual Cancellation**: Ability to cancel pending announcements programmatically
14
-
15
- ### New Methods
16
-
17
- #### `setAnnouncementDelay(delay: number)`
18
- Sets the announcement delay in milliseconds. The delay must be non-negative.
19
-
20
- ```typescript
21
- const accessibilityManager = AccessibilityManager.getInstance();
22
- accessibilityManager.setAnnouncementDelay(200); // Set to 200ms
23
- ```
24
-
25
- #### `getAnnouncementDelay(): number`
26
- Returns the current announcement delay setting.
27
-
28
- ```typescript
29
- const currentDelay = accessibilityManager.getAnnouncementDelay();
30
- console.log(`Current delay: ${currentDelay}ms`);
31
- ```
32
-
33
- #### `cancelPendingAnnouncement()`
34
- Cancels any pending announcement that hasn't been executed yet. Useful for clearing announcements when navigation is interrupted.
35
-
36
- ```typescript
37
- // Cancel any pending announcement
38
- accessibilityManager.cancelPendingAnnouncement();
39
- ```
40
-
41
- ### How It Works
42
-
43
- 1. When `readText()` is called, instead of immediately announcing the text, it schedules the announcement after the configured delay
44
- 2. If `readText()` is called again before the delay expires, the previous announcement is cancelled and a new one is scheduled
45
- 3. Only the most recent announcement will be executed after the delay period
46
- 4. The delay applies to both regular text and localized text announcements
47
-
48
- ### Example Usage
49
-
50
- ```typescript
51
- const accessibilityManager = AccessibilityManager.getInstance();
52
-
53
- // Set a custom delay (optional)
54
- accessibilityManager.setAnnouncementDelay(100);
55
-
56
- // Rapid navigation scenario
57
- accessibilityManager.readText({ text: "Home" }); // Cancelled by next call
58
- accessibilityManager.readText({ text: "Settings" }); // Cancelled by next call
59
- accessibilityManager.readText({ text: "Profile" }); // This will be announced after delay
60
-
61
- // Only "Profile" will be announced, reducing noise
62
- ```
63
-
64
- ### Benefits
65
-
66
- - **Reduced Noise**: Prevents excessive announcements during rapid navigation
67
- - **Better UX**: Users only hear announcements for elements they actually focus on
68
- - **Configurable**: Developers can adjust the delay based on their app's needs
69
- - **Backward Compatible**: Existing code continues to work without changes
70
-
71
- ### Default Behavior
72
-
73
- - Default delay: 200ms
74
- - Delay can be set to 0 to disable the feature (immediate announcements)
75
- - Works with both regular text and localized text keys
76
- - Maintains compatibility with heading queue functionality