Skip to content

Commit 238aa8d

Browse files
committed
Merge remote-tracking branch 'origin' into release
2 parents f710846 + c4098b8 commit 238aa8d

39 files changed

+3781
-3568
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ yarn-error.log
6565
dist
6666
dist-ts
6767
package-lock.json
68-
docs/**/*.md
68+
docs/components/**
6969

7070
# Ruby / CocoaPods
7171
/ios/Pods/

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
14.17.0
1+
16

.yarnrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--registry "https://registry.npmjs.org/"

demo/src/screens/componentScreens/GridListScreen.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,40 @@ import {
1212
GridListItem
1313
} from 'react-native-ui-lib';
1414
import products from '../../data/products';
15-
import {renderBooleanOption} from '../ExampleScreenPresenter';
15+
import {renderBooleanOption, renderMultipleSegmentOptions} from '../ExampleScreenPresenter';
1616

1717
class GridListScreen extends Component {
1818
state = {
1919
orientation: Constants.orientation,
20-
useGridListItem: false
20+
useGridListItem: true,
21+
horizontalAlignment: GridListItem.horizontalAlignment.left,
22+
overlayText: false,
23+
alignToStart: false
2124
};
2225

2326
renderHeader = () => {
2427
return (
2528
<View>
26-
<Text h1 marginB-s5>
29+
<Text h1 marginV-s3>
2730
GridList
2831
</Text>
2932
{renderBooleanOption.call(this, 'UseGridListItem', 'useGridListItem')}
33+
<Text h3 marginV-s2>
34+
GridListItem props
35+
</Text>
36+
{renderMultipleSegmentOptions.call(this, 'Horizontal Alignment:', 'horizontalAlignment', [
37+
{label: 'left', value: GridListItem.horizontalAlignment.left},
38+
{label: 'center', value: GridListItem.horizontalAlignment.center},
39+
{label: 'right', value: GridListItem.horizontalAlignment.right}
40+
])}
41+
{renderBooleanOption.call(this, 'Align to start:', 'alignToStart')}
42+
{renderBooleanOption.call(this, 'Use overlay text:', 'overlayText')}
3043
</View>
3144
);
3245
};
3346

3447
renderItem: GridListProps<(typeof products)[0]>['renderItem'] = ({item}) => {
35-
const {useGridListItem} = this.state;
48+
const {useGridListItem, horizontalAlignment, overlayText, alignToStart} = this.state;
3649

3750
if (useGridListItem) {
3851
return (
@@ -41,8 +54,10 @@ class GridListScreen extends Component {
4154
itemSize={{width: '100%', height: 200}}
4255
imageProps={{source: {uri: item.mediaUrl}}}
4356
title="Title"
44-
subtitle="Subitle"
45-
overlayText
57+
subtitle="Subtile"
58+
alignToStart={alignToStart}
59+
overlayText={overlayText}
60+
horizontalAlignment={horizontalAlignment}
4661
/>
4762
);
4863
} else {

demo/src/screens/componentScreens/SectionsWheelPickerScreen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ const SectionsWheelPickerScreen = () => {
7474
setSelectedMinutes(0);
7575
}, []);
7676

77-
const sections: WheelPickerProps[] = useMemo(() => {
77+
const sections: WheelPickerProps<string | number>[] = useMemo(() => {
7878
return [
7979
{
8080
items: getItems(DAYS),

demo/src/screens/incubatorScreens/IncubatorSliderScreen.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const MAX = 350;
1010
const INITIAL_MIN = 30;
1111
const INITIAL_MAX = 270;
1212
const COLOR = Colors.blue30;
13+
const GROUP_COLOR = Colors.yellow30;
1314

1415
const IncubatorSliderScreen = () => {
1516
const [disableRTL, setDisableRTL] = useState<boolean>(false);
@@ -21,7 +22,7 @@ const IncubatorSliderScreen = () => {
2122
const [sliderMaxValue, setSliderMaxValue] = useState(INITIAL_MAX);
2223

2324
const [color, setColor] = useState(COLOR);
24-
const [groupColor, setGroupColor] = useState(Colors.yellow30);
25+
const [groupColor, setGroupColor] = useState(GROUP_COLOR);
2526
const [alpha, setAlpha] = useState(1);
2627

2728
const slider = useRef<Incubator.SliderRef>(null);
@@ -53,8 +54,11 @@ const IncubatorSliderScreen = () => {
5354
setSliderMinValue(value.min);
5455
}, []);
5556

56-
const onGradientValueChange = useCallback((value: string, alpha: number) => {
57+
const onGradientValueChange = useCallback((value: string, _: number) => {
5758
setColor(value);
59+
}, []);
60+
61+
const onGradientAlphaValueChange = useCallback((_: string, alpha: number) => {
5862
setAlpha(alpha);
5963
}, []);
6064

@@ -201,7 +205,7 @@ const IncubatorSliderScreen = () => {
201205
<GradientSlider
202206
color={color}
203207
containerStyle={styles.gradientSliderContainer}
204-
onValueChange={onGradientValueChange}
208+
onValueChange={onGradientAlphaValueChange}
205209
// @ts-expect-error
206210
ref={this.gradientSlider}
207211
migrate
@@ -216,7 +220,7 @@ const IncubatorSliderScreen = () => {
216220
</Text>
217221
<GradientSlider
218222
type={GradientSlider.types.HUE}
219-
color={COLOR}
223+
color={color}
220224
containerStyle={styles.gradientSliderContainer}
221225
onValueChange={onGradientValueChange}
222226
migrate
@@ -236,7 +240,7 @@ const IncubatorSliderScreen = () => {
236240
Color Slider Group
237241
</Text>
238242
<ColorSliderGroup
239-
initialColor={groupColor}
243+
initialColor={GROUP_COLOR}
240244
sliderContainerStyle={styles.slider}
241245
containerStyle={[styles.group, {borderWidth: 12, borderColor: groupColor}]}
242246
showLabels
@@ -283,6 +287,7 @@ const styles = StyleSheet.create({
283287
customThumb: {
284288
width: 14,
285289
height: 14,
290+
borderWidth: 0.5,
286291
borderRadius: 7,
287292
backgroundColor: Colors.black,
288293
borderColor: Colors.black

docs/foundation/testing.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
index: 6
3+
path: "/foundation/testing"
4+
title: 'Testing'
5+
---
6+
#
7+
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center"}}>
8+
<h1>Testing</h1>
9+
<label style={{
10+
backgroundColor: "#5848ff",
11+
color: "#ffffff",
12+
borderRadius: "5px",
13+
padding: "5px 10px",
14+
margin: "10px",
15+
display: "flex",
16+
alignItems: "center"
17+
}}>
18+
<span>Experimental</span>
19+
</label>
20+
</div>
21+
22+
Testkits allows us to test components without knowing the internal implementation, making it easier to test and reduce over head from migrations and changes in implementation. For example:
23+
* Changing the input of a `TextField` component can be done using the driver's `changeText`
24+
* Pressing a button could be achieved using the Button driver's press function.
25+
## How to use the testkits
26+
### Initializing the driver
27+
In order to initialize a test driver you pass it the renderTree and the component's testId as an object.
28+
29+
### Example
30+
Suppose we have a form that takes a `first name`, `last name` and an `address` and we want to test the submitting of this form. Our form component will look something like this:
31+
```jsx
32+
import {Button, TextField, View} from '@wix/wix-react-native-ui-lib';
33+
34+
type OnSubmitHandler = (firstName: string, lastName: string, address: string) => void;
35+
const MyForm = (props: {onSubmit: OnSubmitHandler}) => {
36+
const {onSubmit} = props;
37+
const [firstName, setFirstName] = useState('');
38+
const [lastName, setLastName] = useState('');
39+
const [address, setAddress] = useState('');
40+
return (
41+
<View>
42+
<TextField label="First name" onChangeText={(value) => setFirstName(value)} value={firstName}/>
43+
<TextField label="Last name" onChangeText={(value) => setLastName(value)} value={lastName}/>
44+
<TextField label="Address" onChangeText={(value) => setAddress(value)} value={address}/>
45+
<Button label="Submit" onPress={() => onSubmit(firstName, lastName, address)}/>
46+
</View>
47+
);
48+
};
49+
```
50+
### Testing our flow
51+
#### In order to test our flow we would do the following steps:
52+
1. Import the TextField and Button driver from UI-LIB's testkit
53+
```javascript
54+
import {TextFieldDriver, ButtonDriver} from '@wix/react-native-ui-lib/testkit';
55+
```
56+
2. render our test case
57+
```javascript
58+
const renderTree = render(<MyForm onSubmit={onSubmit}/>);
59+
```
60+
3. Initialize our drivers for the TextFields and submit button
61+
```javascript
62+
const firstNameDriver = TextFieldDriver({renderTree, testID: 'firstName'});
63+
const lastNameDriver = TextFieldDriver({renderTree, testID: 'lastName'});
64+
const addressDriver = TextFieldDriver({renderTree, testID: 'address'});
65+
const submitBtnDriver = ButtonDriver({renderTree, testID: 'submit'});
66+
```
67+
4. Change the text of the fields and submit the form.
68+
```javascript
69+
firstNameDriver.changeText('Musa');
70+
lastNameDriver.changeText('The Man');
71+
addressDriver.changeText('Yunitzman 5');
72+
submitBtnDriver.press();
73+
```
74+
5. Check that the correct values were passed to the submit handler
75+
```javascript
76+
expect(onSubmit).toHaveBeenCalledWith('Musa', 'The Man', 'Yunitzman 5');
77+
```
78+
<details>
79+
<summary>Full test</summary>
80+
81+
```javascript
82+
describe('My Form', () => {
83+
it('should submit MyForm with Musa The Man, Yunitzman 5', () => {
84+
const onSubmit = jest.fn();
85+
const renderTree = render(<MyForm onSubmit={onSubmit}/>);
86+
const firstNameDriver = TextFieldDriver({renderTree, testID: 'firstName'});
87+
const lastNameDriver = TextFieldDriver({renderTree, testID: 'lastName'});
88+
const addressDriver = TextFieldDriver({renderTree, testID: 'address'});
89+
const submitBtnDriver = ButtonDriver({renderTree, testID: 'submit'});
90+
firstNameDriver.changeText('Musa');
91+
lastNameDriver.changeText('The Man');
92+
addressDriver.changeText('Yunitzman 5');
93+
submitBtnDriver.press();
94+
expect(onSubmit).toHaveBeenCalledWith('Musa', 'The Man', 'Yunitzman 5');
95+
});
96+
});
97+
```
98+
</details>
99+
100+

docuilib/sidebars.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@ module.exports = {
6060
label: 'Components',
6161
collapsible: false,
6262
// items: ['Basic', 'Lists', 'Form', 'Overlays', 'Layout', 'Keyboard', 'Incubator'].map(category => {
63-
items: Object.keys(componentsCategories).map(category => {
63+
items: Object.keys(componentsCategories).sort().map(category => {
6464
return {
6565
type: 'category',
6666
label: componentsCategories[category],
67-
collapsed: false,
67+
collapsed: true,
6868
items: [
6969
{
7070
type: 'autogenerated',

scripts/prReleaseNotesCommon.js

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -80,31 +80,30 @@ function isSilent(pr) {
8080
return false;
8181
}
8282

83-
function getPRsByType(PRs) {
84-
const silentPRs = [],
85-
features = [],
86-
web = [],
87-
fixes = [],
88-
infra = [],
89-
others = [];
83+
function getPRsByType(PRs, categories) {
84+
const categorizedPRs = [];
85+
86+
categories.forEach(category => {
87+
categorizedPRs.push({name: category.name, PRs: [], title: category.title});
88+
});
9089

9190
PRs.forEach(pr => {
92-
if (isSilent(pr)) {
93-
silentPRs.push(pr);
94-
} else if (pr.branch.startsWith('feat/')) {
95-
features.push(pr);
96-
} else if (pr.branch.startsWith('web/')) {
97-
web.push(pr);
98-
} else if (pr.branch.startsWith('fix/')) {
99-
fixes.push(pr);
100-
} else if (pr.branch.startsWith('infra/')) {
101-
infra.push(pr);
91+
const category = categories.find(category => {
92+
return pr.branch.toLowerCase().startsWith(category.branch);
93+
});
94+
if (category) {
95+
const foundCategory = categorizedPRs.find(cat => cat.name === category.name);
96+
foundCategory.PRs.push(pr);
97+
} else if (isSilent(pr)) {
98+
const silentCategory = categorizedPRs.find(cat => cat.name === 'silent');
99+
silentCategory.PRs.push(pr);
102100
} else {
103-
others.push(pr);
101+
const otherCategory = categorizedPRs.find(cat => cat.name === 'others');
102+
otherCategory.PRs.push(pr);
104103
}
105104
});
106105

107-
return {silentPRs, features, web, fixes, infra, others};
106+
return categorizedPRs;
108107
}
109108

110109
function getLine(log, requester, prNumber) {
@@ -153,34 +152,47 @@ function getReleaseNotesForType(PRs, title) {
153152
return releaseNotes;
154153
}
155154

156-
async function _generateReleaseNotes(latestVersion, newVersion, githubToken, fileNamePrefix, repo, header, tagPrefix) {
155+
async function _generateReleaseNotes(latestVersion,
156+
newVersion,
157+
githubToken,
158+
fileNamePrefix,
159+
repo,
160+
header,
161+
tagPrefix,
162+
categories) {
157163
const latestReleaseDate = fetchLatestReleaseDate(tagPrefix, latestVersion);
158164
const PRs = await fetchMergedPRs(latestReleaseDate, repo, githubToken);
159165
if (!PRs) {
160166
return;
161167
}
162168

163-
const {silentPRs, features, web, fixes, infra, others} = getPRsByType(PRs);
169+
const prCategories = [
170+
{name: 'features', branch: 'feat/', title: ':gift: Features'},
171+
{name: 'web', branch: 'web/', title: ':spider_web: Web support'},
172+
{name: 'fixes', branch: 'fix/', title: ':wrench: Fixes'},
173+
{name: 'infra', branch: 'infra/', title: ':gear: Maintenance & Infra'},
174+
...categories,
175+
{name: 'others', branch: '', title: 'OTHERS'},
176+
{
177+
name: 'silent',
178+
branch: '',
179+
title: '// Silent - these PRs did not have a changelog or were left out for some other reason, is it on purpose?'
180+
}
181+
];
164182

183+
const categorizedPRs = getPRsByType(PRs, prCategories);
165184
let releaseNotes = header;
166185

167186
releaseNotes += getTitle(':rocket: What’s New?');
168187

169-
releaseNotes += getReleaseNotesForType(features, ':gift: Features');
170-
171-
releaseNotes += getReleaseNotesForType(web, ':spider_web: Web support');
172-
173-
releaseNotes += getReleaseNotesForType(fixes, ':wrench: Fixes');
174-
175-
releaseNotes += getReleaseNotesForType(infra, ':gear: Maintenance & Infra');
188+
categorizedPRs.forEach(({PRs, title}) => {
189+
if (PRs.length > 0) {
190+
releaseNotes += getReleaseNotesForType(PRs, title);
191+
}
192+
});
176193

177194
releaseNotes += getTitle(':bulb: Deprecations & Migrations');
178195

179-
releaseNotes += getReleaseNotesForType(others, 'OTHERS');
180-
181-
releaseNotes += getReleaseNotesForType(silentPRs,
182-
'// Silent - these PRs did not have a changelog or were left out for some other reason, is it on purpose?');
183-
184196
fs.writeFileSync(`${process.env.HOME}/Downloads/${fileNamePrefix}-release-notes_${newVersion}.txt`, releaseNotes, {
185197
encoding: 'utf8'
186198
});
@@ -194,7 +206,8 @@ async function generateReleaseNotes(latestVersion,
194206
fileNamePrefix,
195207
repo,
196208
header = '',
197-
tagPrefix = '') {
209+
tagPrefix = '',
210+
categories = []) {
198211
let latestVer, newVer;
199212
const rl = readline.createInterface({
200213
input: process.stdin,
@@ -212,7 +225,7 @@ async function generateReleaseNotes(latestVersion,
212225
rl.on('close', () => {
213226
console.info(`Current latest version is v${latestVer}`);
214227
console.info(`Generating release notes out or PRs for v${newVer}`);
215-
_generateReleaseNotes(latestVer, newVer, githubToken, fileNamePrefix, repo, header, tagPrefix);
228+
_generateReleaseNotes(latestVer, newVer, githubToken, fileNamePrefix, repo, header, tagPrefix, categories);
216229
});
217230
}
218231

0 commit comments

Comments
 (0)