@react-native-ama/lists 1.0.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/dist/core/src/components/AMAProvider.d.ts +26 -0
- package/dist/core/src/components/AMAProvider.js +147 -0
- package/dist/core/src/components/AutofocusContainer.d.ts +9 -0
- package/dist/core/src/components/AutofocusContainer.js +58 -0
- package/dist/core/src/components/HideChildrenFromAccessibilityTree.d.ts +6 -0
- package/dist/core/src/components/HideChildrenFromAccessibilityTree.js +40 -0
- package/dist/core/src/hooks/useButtonChecks.d.ts +6 -0
- package/dist/core/src/hooks/useButtonChecks.js +51 -0
- package/dist/core/src/hooks/useChecks.d.ts +15 -0
- package/dist/core/src/hooks/useChecks.js +152 -0
- package/dist/core/src/hooks/useFocus.d.ts +4 -0
- package/dist/core/src/hooks/useFocus.js +55 -0
- package/dist/core/src/hooks/useTimedAction.d.ts +3 -0
- package/dist/core/src/hooks/useTimedAction.js +63 -0
- package/dist/core/src/index.d.ts +7 -0
- package/dist/core/src/index.js +20 -0
- package/dist/internal/src/checks/checkAccessibilityRole.d.ts +3 -0
- package/dist/internal/src/checks/checkAccessibilityRole.js +32 -0
- package/dist/internal/src/checks/checkFocusTrap.d.ts +8 -0
- package/dist/internal/src/checks/checkFocusTrap.js +34 -0
- package/dist/internal/src/checks/checkForAccessibilityState.d.ts +7 -0
- package/dist/internal/src/checks/checkForAccessibilityState.js +48 -0
- package/dist/internal/src/checks/checkMinimumSize.d.ts +3 -0
- package/dist/internal/src/checks/checkMinimumSize.js +29 -0
- package/dist/internal/src/checks/contrastChecker.d.ts +8 -0
- package/dist/internal/src/checks/contrastChecker.js +77 -0
- package/dist/internal/src/checks/noUndefinedProperty.d.ts +8 -0
- package/dist/internal/src/checks/noUndefinedProperty.js +14 -0
- package/dist/internal/src/checks/uppercaseChecker.d.ts +8 -0
- package/dist/internal/src/checks/uppercaseChecker.js +18 -0
- package/dist/internal/src/checks/uppercaseStringChecker.d.ts +8 -0
- package/dist/internal/src/checks/uppercaseStringChecker.js +29 -0
- package/dist/internal/src/index.d.ts +20 -0
- package/dist/internal/src/index.js +56 -0
- package/dist/internal/src/types.d.ts +17 -0
- package/dist/internal/src/types.js +2 -0
- package/dist/internal/src/utils/applyStyle.d.ts +9 -0
- package/dist/internal/src/utils/applyStyle.js +33 -0
- package/dist/internal/src/utils/constants.d.ts +3 -0
- package/dist/internal/src/utils/constants.js +32 -0
- package/dist/internal/src/utils/error.style.d.ts +6 -0
- package/dist/internal/src/utils/error.style.js +11 -0
- package/dist/internal/src/utils/generateAccessibilityStateFromProp.d.ts +9 -0
- package/dist/internal/src/utils/generateAccessibilityStateFromProp.js +35 -0
- package/dist/internal/src/utils/getPropertyFromStyle.d.ts +2 -0
- package/dist/internal/src/utils/getPropertyFromStyle.js +10 -0
- package/dist/internal/src/utils/interpolateAnimationStates.d.ts +2 -0
- package/dist/internal/src/utils/interpolateAnimationStates.js +43 -0
- package/dist/internal/src/utils/isFocused.d.ts +2 -0
- package/dist/internal/src/utils/isFocused.js +7 -0
- package/dist/internal/src/utils/logger.d.ts +16 -0
- package/dist/internal/src/utils/logger.js +53 -0
- package/dist/internal/src/utils/logger.rules.d.ts +15 -0
- package/dist/internal/src/utils/logger.rules.js +74 -0
- package/dist/internal/src/utils/maybeGenerateStringFromElement.d.ts +2 -0
- package/dist/internal/src/utils/maybeGenerateStringFromElement.js +51 -0
- package/dist/internal/src/utils/minimumTouchableSize.d.ts +3 -0
- package/dist/internal/src/utils/minimumTouchableSize.js +9 -0
- package/dist/lists/src/components/DynamicFlatList.d.ts +7 -0
- package/dist/lists/src/components/DynamicFlatList.js +36 -0
- package/dist/lists/src/components/FlatList.d.ts +10 -0
- package/dist/lists/src/components/FlatList.js +24 -0
- package/dist/lists/src/components/ListWrapper.d.ts +7 -0
- package/dist/lists/src/components/ListWrapper.js +45 -0
- package/dist/lists/src/components/StaticFlatList.d.ts +7 -0
- package/dist/lists/src/components/StaticFlatList.js +34 -0
- package/dist/lists/src/hooks/useDynamicList.d.ts +16 -0
- package/dist/lists/src/hooks/useDynamicList.js +62 -0
- package/dist/lists/src/index.d.ts +5 -0
- package/dist/lists/src/index.js +15 -0
- package/package.json +58 -0
- package/src/components/DynamicFlatList.test.tsx +58 -0
- package/src/components/DynamicFlatList.tsx +48 -0
- package/src/components/FlatList.test.tsx +378 -0
- package/src/components/FlatList.tsx +29 -0
- package/src/components/ListWrapper.android.test.tsx +20 -0
- package/src/components/ListWrapper.ios.test.tsx +15 -0
- package/src/components/ListWrapper.tsx +35 -0
- package/src/components/StaticFlatList.tsx +32 -0
- package/src/hooks/useDynamicFlatList.test.ts +50 -0
- package/src/hooks/useDynamicList.ts +84 -0
- package/src/index.ts +8 -0
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@react-native-ama/lists",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"react-native": "src/index",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": [
|
|
10
|
+
{
|
|
11
|
+
"imports": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./dist/index.js"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "rm -rf dist && tsc -p ./tsconfig.build.json",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@react-native-ama/internal": "*",
|
|
27
|
+
"@react-native-ama/core": "*"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"expo": ">=47.0.0",
|
|
31
|
+
"react": "*",
|
|
32
|
+
"react-native": "*"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"expo": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"react-native",
|
|
41
|
+
"a11y",
|
|
42
|
+
"accessibility"
|
|
43
|
+
],
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/FormidableLabs/react-native-ama",
|
|
47
|
+
"directory": "packages/lists"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/FormidableLabs/react-native-ama/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/FormidableLabs/react-native-ama#readme",
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"provenance": true
|
|
55
|
+
},
|
|
56
|
+
"author": "",
|
|
57
|
+
"license": "MIT"
|
|
58
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { render } from '@testing-library/react-native';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import * as UseDynamicList from '../hooks/useDynamicList';
|
|
5
|
+
import { DynamicFlatList } from './DynamicFlatList';
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
jest.clearAllMocks();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe('DynamicFlatList', () => {
|
|
12
|
+
it('uses the style provided by useDynamicFlatList when __DEV__ is true', () => {
|
|
13
|
+
jest.spyOn(UseDynamicList, 'useDynamicList').mockReturnValue({
|
|
14
|
+
style: { backgroundColor: 'formidable' },
|
|
15
|
+
} as any);
|
|
16
|
+
|
|
17
|
+
const { getByTestId } = render(
|
|
18
|
+
<DynamicFlatList
|
|
19
|
+
data={[]}
|
|
20
|
+
singularMessage="%count% singular"
|
|
21
|
+
pluralMessage="%count% plurarl"
|
|
22
|
+
renderItem={() => null}
|
|
23
|
+
testID={'test-id'}
|
|
24
|
+
/>,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
expect(getByTestId('test-id').props.style).toEqual({
|
|
28
|
+
backgroundColor: 'formidable',
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('does not uses the style provided by useDynamicFlatList when __DEV__ is false', () => {
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
global.__DEV__ = false;
|
|
35
|
+
|
|
36
|
+
jest.spyOn(UseDynamicList, 'useDynamicList').mockReturnValue({
|
|
37
|
+
style: { backgroundColor: 'formidable' },
|
|
38
|
+
} as any);
|
|
39
|
+
|
|
40
|
+
const {
|
|
41
|
+
DynamicFlatList: OriginalDynamicFlatList,
|
|
42
|
+
} = require('./DynamicFlatList');
|
|
43
|
+
|
|
44
|
+
const { getByTestId } = render(
|
|
45
|
+
<OriginalDynamicFlatList
|
|
46
|
+
data={[]}
|
|
47
|
+
singularMessage="%count% singular"
|
|
48
|
+
pluralMessage="%count% plurarl"
|
|
49
|
+
renderItem={() => null}
|
|
50
|
+
testID={'test-id'}
|
|
51
|
+
/>,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
expect(getByTestId('test-id').props.style).toEqual(undefined);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
jest.mock('../hooks/useDynamicList');
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FlatList, FlatListProps } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { useDynamicList } from '../hooks/useDynamicList';
|
|
5
|
+
import { ListWrapper } from './ListWrapper';
|
|
6
|
+
|
|
7
|
+
type DynamicFlatListProps<T> = FlatListProps<T> & {
|
|
8
|
+
singularMessage: string;
|
|
9
|
+
pluralMessage: string;
|
|
10
|
+
isPlural?: (count: number) => boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const DynamicFlatList = React.forwardRef<
|
|
14
|
+
FlatList,
|
|
15
|
+
DynamicFlatListProps<any>
|
|
16
|
+
>(
|
|
17
|
+
(
|
|
18
|
+
{ data, singularMessage, pluralMessage, isPlural, ...rest },
|
|
19
|
+
forwardedRef,
|
|
20
|
+
) => {
|
|
21
|
+
const dynamicList = useDynamicList({
|
|
22
|
+
data: data ?? [],
|
|
23
|
+
pluralMessage,
|
|
24
|
+
singularMessage,
|
|
25
|
+
numColumns: rest.numColumns,
|
|
26
|
+
isPlural,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return __DEV__ ? (
|
|
30
|
+
<ListWrapper
|
|
31
|
+
rowsCount={dynamicList.rowsCount}
|
|
32
|
+
columnsCount={dynamicList.columnsCount}>
|
|
33
|
+
<FlatList
|
|
34
|
+
data={data}
|
|
35
|
+
{...rest}
|
|
36
|
+
ref={forwardedRef}
|
|
37
|
+
style={dynamicList.style}
|
|
38
|
+
/>
|
|
39
|
+
</ListWrapper>
|
|
40
|
+
) : (
|
|
41
|
+
<ListWrapper
|
|
42
|
+
rowsCount={dynamicList.rowsCount}
|
|
43
|
+
columnsCount={dynamicList.columnsCount}>
|
|
44
|
+
<FlatList data={data} {...rest} ref={forwardedRef} />
|
|
45
|
+
</ListWrapper>
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
);
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { render, waitFor } from '@testing-library/react-native';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { AccessibilityInfo, Platform } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { FlatList } from './FlatList';
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
jest.clearAllMocks();
|
|
9
|
+
|
|
10
|
+
AccessibilityInfo.announceForAccessibility = jest.fn();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
Platform.OS = 'android';
|
|
14
|
+
|
|
15
|
+
describe('FlatList', () => {
|
|
16
|
+
describe('listType "dynamic"', () => {
|
|
17
|
+
it('does not announce the number of items on first render', async () => {
|
|
18
|
+
render(
|
|
19
|
+
<FlatList
|
|
20
|
+
data={DATA}
|
|
21
|
+
listType="dynamic"
|
|
22
|
+
singularMessage={'%count% item found'}
|
|
23
|
+
pluralMessage={'%count% items found'}
|
|
24
|
+
renderItem={() => null}
|
|
25
|
+
/>,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
await waitFor(() => {
|
|
29
|
+
expect(
|
|
30
|
+
AccessibilityInfo.announceForAccessibility,
|
|
31
|
+
).not.toHaveBeenCalled();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it.each`
|
|
36
|
+
sliceStart | sliceEnd | count | type
|
|
37
|
+
${0} | ${2} | ${2} | ${'plural'}
|
|
38
|
+
${1} | ${2} | ${1} | ${'singular'}
|
|
39
|
+
`(
|
|
40
|
+
'announce the number of items displayed when the data changes',
|
|
41
|
+
({ sliceStart, sliceEnd, count, type }) => {
|
|
42
|
+
const messages: Record<string, string> = {
|
|
43
|
+
singular: '%count% item found',
|
|
44
|
+
plural: '%count% items found',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const renderAPI = render(
|
|
48
|
+
<FlatList
|
|
49
|
+
data={DATA}
|
|
50
|
+
listType="dynamic"
|
|
51
|
+
singularMessage={messages.singular}
|
|
52
|
+
pluralMessage={messages.plural}
|
|
53
|
+
renderItem={() => null}
|
|
54
|
+
/>,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
renderAPI.update(
|
|
58
|
+
<FlatList
|
|
59
|
+
data={DATA.slice(sliceStart, sliceEnd)}
|
|
60
|
+
listType="dynamic"
|
|
61
|
+
singularMessage={messages.singular}
|
|
62
|
+
pluralMessage={messages.plural}
|
|
63
|
+
renderItem={() => null}
|
|
64
|
+
/>,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(AccessibilityInfo.announceForAccessibility).toHaveBeenCalledWith(
|
|
68
|
+
messages[type].replace('%count%', count),
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
it.each`
|
|
74
|
+
sliceStart | sliceEnd
|
|
75
|
+
${0} | ${2}
|
|
76
|
+
${1} | ${2}
|
|
77
|
+
`('resets the count of announced items', ({ sliceStart, sliceEnd }) => {
|
|
78
|
+
const messages: Record<string, string> = {
|
|
79
|
+
singular: '%count% item found',
|
|
80
|
+
plural: '%count% items found',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const renderAPI = render(
|
|
84
|
+
<FlatList
|
|
85
|
+
data={DATA}
|
|
86
|
+
listType="dynamic"
|
|
87
|
+
singularMessage={messages.singular}
|
|
88
|
+
pluralMessage={messages.plural}
|
|
89
|
+
renderItem={() => null}
|
|
90
|
+
/>,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
renderAPI.update(
|
|
94
|
+
<FlatList
|
|
95
|
+
data={DATA.slice(sliceStart, sliceEnd)}
|
|
96
|
+
listType="dynamic"
|
|
97
|
+
singularMessage={messages.singular}
|
|
98
|
+
pluralMessage={messages.plural}
|
|
99
|
+
renderItem={() => null}
|
|
100
|
+
/>,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
renderAPI.update(
|
|
104
|
+
<FlatList
|
|
105
|
+
data={DATA}
|
|
106
|
+
listType="dynamic"
|
|
107
|
+
singularMessage={messages.singular}
|
|
108
|
+
pluralMessage={messages.plural}
|
|
109
|
+
renderItem={() => null}
|
|
110
|
+
/>,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
renderAPI.update(
|
|
114
|
+
<FlatList
|
|
115
|
+
data={DATA.slice(sliceStart, sliceEnd)}
|
|
116
|
+
listType="dynamic"
|
|
117
|
+
singularMessage={messages.singular}
|
|
118
|
+
pluralMessage={messages.plural}
|
|
119
|
+
renderItem={() => null}
|
|
120
|
+
/>,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
expect(AccessibilityInfo.announceForAccessibility).toHaveBeenCalledTimes(
|
|
124
|
+
2,
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('does not announce if the list shows again all the items', () => {
|
|
129
|
+
const { update } = render(
|
|
130
|
+
<FlatList
|
|
131
|
+
data={DATA}
|
|
132
|
+
listType="dynamic"
|
|
133
|
+
singularMessage={'%count% item found'}
|
|
134
|
+
pluralMessage={'%count% items found'}
|
|
135
|
+
renderItem={() => null}
|
|
136
|
+
/>,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
update(
|
|
140
|
+
<FlatList
|
|
141
|
+
data={DATA}
|
|
142
|
+
listType="dynamic"
|
|
143
|
+
singularMessage={'%count% item found'}
|
|
144
|
+
pluralMessage={'%count% items found'}
|
|
145
|
+
renderItem={() => null}
|
|
146
|
+
/>,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(AccessibilityInfo.announceForAccessibility).not.toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('allows using a custom function to detect if the number of items displayed is plural', () => {
|
|
153
|
+
const isPlural = jest.fn().mockReturnValue(false);
|
|
154
|
+
|
|
155
|
+
const { update } = render(
|
|
156
|
+
<FlatList
|
|
157
|
+
data={DATA}
|
|
158
|
+
listType="dynamic"
|
|
159
|
+
singularMessage={'%count% item found'}
|
|
160
|
+
pluralMessage={'%count% items found'}
|
|
161
|
+
isPlural={isPlural}
|
|
162
|
+
renderItem={() => null}
|
|
163
|
+
/>,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
update(
|
|
167
|
+
<FlatList
|
|
168
|
+
data={DATA.slice(1)}
|
|
169
|
+
listType="dynamic"
|
|
170
|
+
singularMessage={'%count% item found'}
|
|
171
|
+
pluralMessage={'%count% items found'}
|
|
172
|
+
isPlural={isPlural}
|
|
173
|
+
renderItem={() => null}
|
|
174
|
+
/>,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
expect(isPlural).toHaveBeenCalledWith(2);
|
|
178
|
+
expect(AccessibilityInfo.announceForAccessibility).toHaveBeenCalledWith(
|
|
179
|
+
'2 item found',
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('listType="static"', () => {
|
|
185
|
+
it.each`
|
|
186
|
+
sliceStart | sliceEnd | count | type
|
|
187
|
+
${0} | ${2} | ${2} | ${'plural'}
|
|
188
|
+
${1} | ${2} | ${1} | ${'singular'}
|
|
189
|
+
`(
|
|
190
|
+
'does not announce the number of items displayed when the data changes',
|
|
191
|
+
({ sliceStart, sliceEnd }) => {
|
|
192
|
+
const renderAPI = render(
|
|
193
|
+
<FlatList data={DATA} listType="static" renderItem={() => null} />,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
renderAPI.update(
|
|
197
|
+
<FlatList
|
|
198
|
+
data={DATA.slice(sliceStart, sliceEnd)}
|
|
199
|
+
listType="static"
|
|
200
|
+
renderItem={() => null}
|
|
201
|
+
/>,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
expect(
|
|
205
|
+
AccessibilityInfo.announceForAccessibility,
|
|
206
|
+
).not.toHaveBeenCalled();
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
it('uses the given rowCount when specified', () => {
|
|
211
|
+
const { toJSON } = render(
|
|
212
|
+
<FlatList
|
|
213
|
+
data={[]}
|
|
214
|
+
listType="static"
|
|
215
|
+
rowsCount={42}
|
|
216
|
+
renderItem={() => null}
|
|
217
|
+
/>,
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
expect(toJSON()).toMatchInlineSnapshot(`
|
|
221
|
+
<View
|
|
222
|
+
columnsCount={1}
|
|
223
|
+
rowsCount={42}
|
|
224
|
+
>
|
|
225
|
+
<RCTScrollView
|
|
226
|
+
data={Array []}
|
|
227
|
+
getItem={[Function]}
|
|
228
|
+
getItemCount={[Function]}
|
|
229
|
+
keyExtractor={[Function]}
|
|
230
|
+
onContentSizeChange={[Function]}
|
|
231
|
+
onLayout={[Function]}
|
|
232
|
+
onMomentumScrollBegin={[Function]}
|
|
233
|
+
onMomentumScrollEnd={[Function]}
|
|
234
|
+
onScroll={[Function]}
|
|
235
|
+
onScrollBeginDrag={[Function]}
|
|
236
|
+
onScrollEndDrag={[Function]}
|
|
237
|
+
removeClippedSubviews={true}
|
|
238
|
+
renderItem={[Function]}
|
|
239
|
+
scrollEventThrottle={50}
|
|
240
|
+
stickyHeaderIndices={Array []}
|
|
241
|
+
viewabilityConfigCallbackPairs={Array []}
|
|
242
|
+
>
|
|
243
|
+
<View />
|
|
244
|
+
</RCTScrollView>
|
|
245
|
+
</View>
|
|
246
|
+
`);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('divides the number of items by the number of columns', () => {
|
|
250
|
+
const { toJSON } = render(
|
|
251
|
+
<FlatList
|
|
252
|
+
data={DATA}
|
|
253
|
+
listType="static"
|
|
254
|
+
numColumns={2}
|
|
255
|
+
renderItem={() => null}
|
|
256
|
+
/>,
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
expect(toJSON()).toMatchInlineSnapshot(`
|
|
260
|
+
<View
|
|
261
|
+
columnsCount={2}
|
|
262
|
+
rowsCount={2}
|
|
263
|
+
>
|
|
264
|
+
<RCTScrollView
|
|
265
|
+
data={
|
|
266
|
+
Array [
|
|
267
|
+
Object {
|
|
268
|
+
"id": "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
|
|
269
|
+
"title": "Formidable",
|
|
270
|
+
},
|
|
271
|
+
Object {
|
|
272
|
+
"id": "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
|
|
273
|
+
"title": "React Native",
|
|
274
|
+
},
|
|
275
|
+
Object {
|
|
276
|
+
"id": "58694a0f-3da1-471f-bd96-145571e29d72",
|
|
277
|
+
"title": "Typescript",
|
|
278
|
+
},
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
getItem={[Function]}
|
|
282
|
+
getItemCount={[Function]}
|
|
283
|
+
keyExtractor={[Function]}
|
|
284
|
+
onContentSizeChange={[Function]}
|
|
285
|
+
onLayout={[Function]}
|
|
286
|
+
onMomentumScrollBegin={[Function]}
|
|
287
|
+
onMomentumScrollEnd={[Function]}
|
|
288
|
+
onScroll={[Function]}
|
|
289
|
+
onScrollBeginDrag={[Function]}
|
|
290
|
+
onScrollEndDrag={[Function]}
|
|
291
|
+
removeClippedSubviews={true}
|
|
292
|
+
renderItem={[Function]}
|
|
293
|
+
scrollEventThrottle={50}
|
|
294
|
+
stickyHeaderIndices={Array []}
|
|
295
|
+
viewabilityConfigCallbackPairs={Array []}
|
|
296
|
+
>
|
|
297
|
+
<View>
|
|
298
|
+
<View
|
|
299
|
+
onLayout={[Function]}
|
|
300
|
+
style={null}
|
|
301
|
+
/>
|
|
302
|
+
<View
|
|
303
|
+
onLayout={[Function]}
|
|
304
|
+
style={null}
|
|
305
|
+
/>
|
|
306
|
+
<View
|
|
307
|
+
onLayout={[Function]}
|
|
308
|
+
style={null}
|
|
309
|
+
/>
|
|
310
|
+
</View>
|
|
311
|
+
</RCTScrollView>
|
|
312
|
+
</View>
|
|
313
|
+
`);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('sets rowsCount to 0 if the data is undefined', () => {
|
|
317
|
+
const { toJSON } = render(
|
|
318
|
+
<FlatList
|
|
319
|
+
data={undefined}
|
|
320
|
+
listType="static"
|
|
321
|
+
numColumns={2}
|
|
322
|
+
renderItem={() => null}
|
|
323
|
+
/>,
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
expect(toJSON()).toMatchInlineSnapshot(`
|
|
327
|
+
<View
|
|
328
|
+
columnsCount={2}
|
|
329
|
+
rowsCount={0}
|
|
330
|
+
>
|
|
331
|
+
<RCTScrollView
|
|
332
|
+
getItem={[Function]}
|
|
333
|
+
getItemCount={[Function]}
|
|
334
|
+
keyExtractor={[Function]}
|
|
335
|
+
onContentSizeChange={[Function]}
|
|
336
|
+
onLayout={[Function]}
|
|
337
|
+
onMomentumScrollBegin={[Function]}
|
|
338
|
+
onMomentumScrollEnd={[Function]}
|
|
339
|
+
onScroll={[Function]}
|
|
340
|
+
onScrollBeginDrag={[Function]}
|
|
341
|
+
onScrollEndDrag={[Function]}
|
|
342
|
+
removeClippedSubviews={true}
|
|
343
|
+
renderItem={[Function]}
|
|
344
|
+
scrollEventThrottle={50}
|
|
345
|
+
stickyHeaderIndices={Array []}
|
|
346
|
+
viewabilityConfigCallbackPairs={Array []}
|
|
347
|
+
>
|
|
348
|
+
<View />
|
|
349
|
+
</RCTScrollView>
|
|
350
|
+
</View>
|
|
351
|
+
`);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
jest.mock('./ListWrapper', () => {
|
|
357
|
+
const { View } = jest.requireActual('react-native');
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
// @ts-ignore
|
|
361
|
+
ListWrapper: props => <View {...props} />,
|
|
362
|
+
};
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const DATA = [
|
|
366
|
+
{
|
|
367
|
+
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
|
|
368
|
+
title: 'Formidable',
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
|
|
372
|
+
title: 'React Native',
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
id: '58694a0f-3da1-471f-bd96-145571e29d72',
|
|
376
|
+
title: 'Typescript',
|
|
377
|
+
},
|
|
378
|
+
];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
FlatList as RNFlatList,
|
|
4
|
+
FlatListProps as RNFlatListProps,
|
|
5
|
+
} from 'react-native';
|
|
6
|
+
|
|
7
|
+
import type { UseDynamicList } from '../hooks/useDynamicList';
|
|
8
|
+
import { DynamicFlatList } from './DynamicFlatList';
|
|
9
|
+
import { StaticFlatList, StaticFlatListProps } from './StaticFlatList';
|
|
10
|
+
|
|
11
|
+
export type FlatListProps<T> = RNFlatListProps<T> &
|
|
12
|
+
(
|
|
13
|
+
| ({
|
|
14
|
+
listType: 'static';
|
|
15
|
+
} & StaticFlatListProps)
|
|
16
|
+
| ({
|
|
17
|
+
listType: 'dynamic';
|
|
18
|
+
} & UseDynamicList)
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const FlatList = React.forwardRef<RNFlatList, FlatListProps<any>>(
|
|
22
|
+
({ listType, ...rest }, forwardRef) => {
|
|
23
|
+
return listType === 'dynamic' ? (
|
|
24
|
+
<DynamicFlatList ref={forwardRef} {...(rest as any)} />
|
|
25
|
+
) : (
|
|
26
|
+
<StaticFlatList ref={forwardRef} {...(rest as any)} />
|
|
27
|
+
);
|
|
28
|
+
},
|
|
29
|
+
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { render } from '@testing-library/react-native';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
|
|
5
|
+
describe('ListWrapper', () => {
|
|
6
|
+
it('renders the AMAFlatListWrapper is the Platform is Android', () => {
|
|
7
|
+
Platform.OS = 'android';
|
|
8
|
+
|
|
9
|
+
const { ListWrapper } = require('./ListWrapper');
|
|
10
|
+
|
|
11
|
+
const result = render(<ListWrapper rowsCount={1} />);
|
|
12
|
+
|
|
13
|
+
expect(result.toJSON()).toMatchInlineSnapshot(`
|
|
14
|
+
<AmaFlatListWrapper
|
|
15
|
+
columnsCount={1}
|
|
16
|
+
rowsCount={1}
|
|
17
|
+
/>
|
|
18
|
+
`);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { render } from '@testing-library/react-native';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
|
|
5
|
+
describe('ListWrapper', () => {
|
|
6
|
+
it('renders an empty wrapper is the Platform is iOS', () => {
|
|
7
|
+
Platform.OS = 'ios';
|
|
8
|
+
|
|
9
|
+
const { ListWrapper } = require('./ListWrapper');
|
|
10
|
+
|
|
11
|
+
const result = render(<ListWrapper rowsCount={1} />);
|
|
12
|
+
|
|
13
|
+
expect(result.toJSON()).toMatchInlineSnapshot('null');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Platform, requireNativeComponent } from 'react-native';
|
|
3
|
+
|
|
4
|
+
type FlatListWrapperProps = React.PropsWithChildren<{
|
|
5
|
+
rowsCount: number;
|
|
6
|
+
columnsCount?: number;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
const isAndroid = Platform.OS === 'android';
|
|
10
|
+
|
|
11
|
+
export const ListWrapper = ({
|
|
12
|
+
children,
|
|
13
|
+
rowsCount,
|
|
14
|
+
columnsCount = 1,
|
|
15
|
+
}: FlatListWrapperProps) => {
|
|
16
|
+
return (
|
|
17
|
+
<AMAFlatListWrapper rowsCount={rowsCount} columnsCount={columnsCount}>
|
|
18
|
+
{children}
|
|
19
|
+
</AMAFlatListWrapper>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* Used instead of React.Fragment to avoid the warning:
|
|
25
|
+
* ...React.Fragment can only have `key` and `children` props.
|
|
26
|
+
*/
|
|
27
|
+
const EmptyComponent: React.FC = ({
|
|
28
|
+
children,
|
|
29
|
+
}: React.PropsWithChildren<{}>) => {
|
|
30
|
+
return <>{children}</>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const AMAFlatListWrapper = isAndroid
|
|
34
|
+
? requireNativeComponent<FlatListWrapperProps>('AmaFlatListWrapper')
|
|
35
|
+
: EmptyComponent;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FlatList, FlatListProps } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { ListWrapper } from './ListWrapper';
|
|
5
|
+
|
|
6
|
+
export type StaticFlatListProps = {
|
|
7
|
+
rowsCount?: number;
|
|
8
|
+
numColumns?: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const StaticFlatList = React.forwardRef<
|
|
12
|
+
FlatList,
|
|
13
|
+
FlatListProps<any> & StaticFlatListProps
|
|
14
|
+
>(({ data, numColumns, rowsCount, ...rest }, ref) => {
|
|
15
|
+
const columns = numColumns || 1;
|
|
16
|
+
|
|
17
|
+
const rows = React.useMemo(() => {
|
|
18
|
+
if (rowsCount) {
|
|
19
|
+
return rowsCount;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const length = data?.length || 0;
|
|
23
|
+
|
|
24
|
+
return Math.ceil(columns > 1 && length > 0 ? length / columns : length);
|
|
25
|
+
}, [columns, data?.length, rowsCount]);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<ListWrapper rowsCount={rows} columnsCount={columns}>
|
|
29
|
+
<FlatList data={data} {...rest} ref={ref} />
|
|
30
|
+
</ListWrapper>
|
|
31
|
+
);
|
|
32
|
+
});
|