@applicaster/zapp-react-native-ui-components 16.0.0-rc.82 → 16.0.0-rc.84
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/Components/MasterCell/DefaultComponents/ActionButton.tsx +1 -0
- package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +23 -6
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +66 -0
- package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
- package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
- package/Components/ModalComponent/presets/__tests__/getCell.test.ts +24 -0
- package/Components/ModalComponent/presets/selectableCell.tsx +1 -1
- package/package.json +5 -5
|
@@ -105,6 +105,7 @@ export const ActionButton = React.memo(function ActionButtonComponent(
|
|
|
105
105
|
testID={props?.testID || `${item?.id}`}
|
|
106
106
|
accessibilityLabel={props?.accessibilityLabel || `${item?.id}`}
|
|
107
107
|
accessibilityHint={props?.accessibilityHint}
|
|
108
|
+
accessibilityRole="button"
|
|
108
109
|
accessible={!!(props?.testID || props?.accessibilityLabel)}
|
|
109
110
|
style={props?.style}
|
|
110
111
|
>
|
|
@@ -91,6 +91,7 @@ function ButtonComponent(props: Props) {
|
|
|
91
91
|
onPress={onPress}
|
|
92
92
|
testID={props?.testID || `${item?.id}`}
|
|
93
93
|
accessibilityLabel={props?.accessibilityLabel || `${item?.id}`}
|
|
94
|
+
accessibilityRole="button"
|
|
94
95
|
accessible={!!(props?.testID || props?.accessibilityLabel)}
|
|
95
96
|
style={props?.style}
|
|
96
97
|
>
|
|
@@ -706,18 +706,31 @@ function ItemButton({
|
|
|
706
706
|
}
|
|
707
707
|
};
|
|
708
708
|
|
|
709
|
+
const buttonPadding = {
|
|
710
|
+
paddingTop: layout.paddingTop,
|
|
711
|
+
paddingRight: layout.paddingRight,
|
|
712
|
+
paddingBottom: layout.paddingBottom,
|
|
713
|
+
paddingLeft: layout.paddingLeft,
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
// A nested Pressable with no handler swallows the parent row press.
|
|
717
|
+
if (!onPress) {
|
|
718
|
+
return (
|
|
719
|
+
<View pointerEvents="none" style={buttonPadding}>
|
|
720
|
+
{renderIcon()}
|
|
721
|
+
</View>
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
|
|
709
725
|
return (
|
|
710
726
|
<Pressable
|
|
711
727
|
onPress={(e: any) => {
|
|
712
728
|
e?.stopPropagation?.();
|
|
713
|
-
onPress
|
|
729
|
+
onPress();
|
|
714
730
|
}}
|
|
715
731
|
style={({ pressed }) => [
|
|
732
|
+
buttonPadding,
|
|
716
733
|
{
|
|
717
|
-
paddingTop: layout.paddingTop,
|
|
718
|
-
paddingRight: layout.paddingRight,
|
|
719
|
-
paddingBottom: layout.paddingBottom,
|
|
720
|
-
paddingLeft: layout.paddingLeft,
|
|
721
734
|
opacity: pressed || focused ? 0.7 : 1,
|
|
722
735
|
},
|
|
723
736
|
]}
|
|
@@ -849,6 +862,10 @@ export function AudioPlayerTrackItem({
|
|
|
849
862
|
|
|
850
863
|
const container = resolveContainerSpec(image);
|
|
851
864
|
|
|
865
|
+
const handleTrailingButtonPress =
|
|
866
|
+
onTrailingButtonPress ??
|
|
867
|
+
(data.trailingButton === "multiSelect" ? onPress : undefined);
|
|
868
|
+
|
|
852
869
|
return (
|
|
853
870
|
<ItemContainer
|
|
854
871
|
image={image}
|
|
@@ -884,7 +901,7 @@ export function AudioPlayerTrackItem({
|
|
|
884
901
|
focusedLeadingButton={focusedLeadingButton}
|
|
885
902
|
focusedTrailingButton={focusedTrailingButton}
|
|
886
903
|
onLeadingButtonPress={onLeadingButtonPress}
|
|
887
|
-
onTrailingButtonPress={
|
|
904
|
+
onTrailingButtonPress={handleTrailingButtonPress}
|
|
888
905
|
isSelected={data.isSelected}
|
|
889
906
|
renderHandle={renderHandle}
|
|
890
907
|
checkboxAssets={checkboxAssets}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Pressable } from "react-native";
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react-native";
|
|
4
|
+
import { AudioPlayerTrackItem, VARIANT_PLAYLIST_ITEM } from "../Item";
|
|
5
|
+
|
|
6
|
+
const playlistItemData = {
|
|
7
|
+
textLabel1: "Queue",
|
|
8
|
+
textLabel2: "",
|
|
9
|
+
trailingButton: "multiSelect" as const,
|
|
10
|
+
isSelected: false,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe("AudioPlayerTrackItem multiSelect checkbox", () => {
|
|
14
|
+
it("invokes onPress when the checkbox is pressed even if onTrailingButtonPress is omitted", () => {
|
|
15
|
+
const onPress = jest.fn();
|
|
16
|
+
|
|
17
|
+
render(
|
|
18
|
+
<AudioPlayerTrackItem
|
|
19
|
+
configuration={VARIANT_PLAYLIST_ITEM}
|
|
20
|
+
data={playlistItemData}
|
|
21
|
+
onPress={onPress}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const pressables = screen.UNSAFE_getAllByType(Pressable);
|
|
26
|
+
fireEvent.press(pressables[pressables.length - 1]);
|
|
27
|
+
|
|
28
|
+
expect(onPress).toHaveBeenCalledTimes(1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("invokes onTrailingButtonPress instead of onPress when a dedicated trailing handler is provided", () => {
|
|
32
|
+
const onPress = jest.fn();
|
|
33
|
+
const onTrailingButtonPress = jest.fn();
|
|
34
|
+
|
|
35
|
+
render(
|
|
36
|
+
<AudioPlayerTrackItem
|
|
37
|
+
configuration={VARIANT_PLAYLIST_ITEM}
|
|
38
|
+
data={playlistItemData}
|
|
39
|
+
onPress={onPress}
|
|
40
|
+
onTrailingButtonPress={onTrailingButtonPress}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const pressables = screen.UNSAFE_getAllByType(Pressable);
|
|
45
|
+
fireEvent.press(pressables[pressables.length - 1]);
|
|
46
|
+
|
|
47
|
+
expect(onTrailingButtonPress).toHaveBeenCalledTimes(1);
|
|
48
|
+
expect(onPress).not.toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("invokes onPress when the row is pressed", () => {
|
|
52
|
+
const onPress = jest.fn();
|
|
53
|
+
|
|
54
|
+
render(
|
|
55
|
+
<AudioPlayerTrackItem
|
|
56
|
+
configuration={VARIANT_PLAYLIST_ITEM}
|
|
57
|
+
data={playlistItemData}
|
|
58
|
+
onPress={onPress}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
fireEvent.press(screen.UNSAFE_getAllByType(Pressable)[0]);
|
|
63
|
+
|
|
64
|
+
expect(onPress).toHaveBeenCalledTimes(1);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { TouchableOpacity } from "react-native";
|
|
3
3
|
import { QBImage } from "@applicaster/zapp-react-native-ui-components/Components";
|
|
4
|
+
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
|
|
4
5
|
// @ts-ignore
|
|
5
6
|
import { defaultCloseAsset } from "./assets";
|
|
6
7
|
|
|
@@ -40,9 +41,20 @@ export function CloseButton(props: CloseButtonProps) {
|
|
|
40
41
|
|
|
41
42
|
if (!enabled) return null;
|
|
42
43
|
|
|
44
|
+
// Expose the close button as its own accessible control so screen readers
|
|
45
|
+
// (e.g. iOS VoiceOver) can focus and activate it. The image-only button
|
|
46
|
+
// otherwise has no label/role, leaving it unreachable/unannounced. Uses the
|
|
47
|
+
// framework's localized "close" button config (label + hint + button role).
|
|
48
|
+
// Computed each render (not memoized) so runtime localization updates via
|
|
49
|
+
// AccessibilityManager.updateLocalizations() are picked up on re-render.
|
|
50
|
+
const accessibilityProps =
|
|
51
|
+
AccessibilityManager.getInstance().getButtonAccessibilityProps("close");
|
|
52
|
+
|
|
43
53
|
return (
|
|
44
54
|
<TouchableOpacity
|
|
45
55
|
activeOpacity={1}
|
|
56
|
+
testID="closeButton"
|
|
57
|
+
{...accessibilityProps}
|
|
46
58
|
style={{
|
|
47
59
|
marginBottom,
|
|
48
60
|
marginTop,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react-native";
|
|
3
|
+
import { CloseButton, CloseButtonProps } from "../CloseButton";
|
|
4
|
+
|
|
5
|
+
jest.mock("@applicaster/zapp-react-native-ui-components/Components", () => {
|
|
6
|
+
const { Image } = require("react-native");
|
|
7
|
+
|
|
8
|
+
return { QBImage: Image };
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const mockGetButtonA11yProps = jest.fn(() => ({
|
|
12
|
+
accessible: true,
|
|
13
|
+
accessibilityLabel: "Close",
|
|
14
|
+
accessibilityHint: "Closes the sheet",
|
|
15
|
+
accessibilityRole: "button",
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.mock(
|
|
19
|
+
"@applicaster/zapp-react-native-utils/appUtils/accessibilityManager",
|
|
20
|
+
() => ({
|
|
21
|
+
AccessibilityManager: {
|
|
22
|
+
getInstance: () => ({
|
|
23
|
+
getButtonAccessibilityProps: mockGetButtonA11yProps,
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const baseProps: CloseButtonProps = {
|
|
30
|
+
enabled: true,
|
|
31
|
+
close: jest.fn(),
|
|
32
|
+
asset: "close.png",
|
|
33
|
+
assetFocused: "close_focused.png",
|
|
34
|
+
height: 24,
|
|
35
|
+
width: 24,
|
|
36
|
+
marginTop: 0,
|
|
37
|
+
marginBottom: 0,
|
|
38
|
+
marginLeft: 0,
|
|
39
|
+
marginRight: 0,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
describe("ModalComponent Header CloseButton", () => {
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
jest.clearAllMocks();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("renders as an accessible button reachable by screen readers", () => {
|
|
48
|
+
render(<CloseButton {...baseProps} />);
|
|
49
|
+
|
|
50
|
+
const button = screen.getByTestId("closeButton");
|
|
51
|
+
|
|
52
|
+
expect(button.props.accessible).toBe(true);
|
|
53
|
+
expect(button.props.accessibilityRole).toBe("button");
|
|
54
|
+
expect(button.props.accessibilityLabel).toBe("Close");
|
|
55
|
+
expect(mockGetButtonA11yProps).toHaveBeenCalledWith("close");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("invokes close on press", () => {
|
|
59
|
+
const close = jest.fn();
|
|
60
|
+
render(<CloseButton {...baseProps} close={close} />);
|
|
61
|
+
|
|
62
|
+
fireEvent.press(screen.getByTestId("closeButton"));
|
|
63
|
+
|
|
64
|
+
expect(close).toHaveBeenCalledTimes(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("renders nothing when disabled", () => {
|
|
68
|
+
render(<CloseButton {...baseProps} enabled={false} />);
|
|
69
|
+
|
|
70
|
+
expect(screen.queryByTestId("closeButton")).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Pressable } from "react-native";
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react-native";
|
|
1
4
|
import { getCell } from "../index";
|
|
2
5
|
import { standardCell } from "../standardCell";
|
|
3
6
|
import { selectableCell } from "../selectableCell";
|
|
@@ -64,3 +67,24 @@ describe("mapMenuItemToPlaylistData", () => {
|
|
|
64
67
|
expect(data.trailingButton).toBeUndefined();
|
|
65
68
|
});
|
|
66
69
|
});
|
|
70
|
+
|
|
71
|
+
describe("selectableCell checkbox press", () => {
|
|
72
|
+
it("invokes onPress with the playlist item when the checkbox is pressed", () => {
|
|
73
|
+
const onPress = jest.fn();
|
|
74
|
+
const item = { id: "queue", title: "Queue" };
|
|
75
|
+
|
|
76
|
+
render(
|
|
77
|
+
selectableCell.renderItem({
|
|
78
|
+
item,
|
|
79
|
+
index: 0,
|
|
80
|
+
onPress,
|
|
81
|
+
context: { behavior: { selectMode: "multi" } } as any,
|
|
82
|
+
}) as React.ReactElement
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const pressables = screen.UNSAFE_getAllByType(Pressable);
|
|
86
|
+
fireEvent.press(pressables[pressables.length - 1]);
|
|
87
|
+
|
|
88
|
+
expect(onPress).toHaveBeenCalledWith(item);
|
|
89
|
+
});
|
|
90
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "16.0.0-rc.
|
|
3
|
+
"version": "16.0.0-rc.84",
|
|
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",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "16.0.0-rc.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-rc.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-rc.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-rc.84",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.84",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.84",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.84",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"react-native-sortables": "1.7.1",
|