Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
nitzanyiz committed Apr 10, 2024
2 parents f710846 + c4098b8 commit 238aa8d
Show file tree
Hide file tree
Showing 39 changed files with 3,781 additions and 3,568 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -65,7 +65,7 @@ yarn-error.log
dist
dist-ts
package-lock.json
docs/**/*.md
docs/components/**

# Ruby / CocoaPods
/ios/Pods/
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
@@ -1 +1 @@
14.17.0
16
1 change: 1 addition & 0 deletions .yarnrc
@@ -0,0 +1 @@
--registry "https://registry.npmjs.org/"
27 changes: 21 additions & 6 deletions demo/src/screens/componentScreens/GridListScreen.tsx
Expand Up @@ -12,27 +12,40 @@ import {
GridListItem
} from 'react-native-ui-lib';
import products from '../../data/products';
import {renderBooleanOption} from '../ExampleScreenPresenter';
import {renderBooleanOption, renderMultipleSegmentOptions} from '../ExampleScreenPresenter';

class GridListScreen extends Component {
state = {
orientation: Constants.orientation,
useGridListItem: false
useGridListItem: true,
horizontalAlignment: GridListItem.horizontalAlignment.left,
overlayText: false,
alignToStart: false
};

renderHeader = () => {
return (
<View>
<Text h1 marginB-s5>
<Text h1 marginV-s3>
GridList
</Text>
{renderBooleanOption.call(this, 'UseGridListItem', 'useGridListItem')}
<Text h3 marginV-s2>
GridListItem props
</Text>
{renderMultipleSegmentOptions.call(this, 'Horizontal Alignment:', 'horizontalAlignment', [
{label: 'left', value: GridListItem.horizontalAlignment.left},
{label: 'center', value: GridListItem.horizontalAlignment.center},
{label: 'right', value: GridListItem.horizontalAlignment.right}
])}
{renderBooleanOption.call(this, 'Align to start:', 'alignToStart')}
{renderBooleanOption.call(this, 'Use overlay text:', 'overlayText')}
</View>
);
};

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

if (useGridListItem) {
return (
Expand All @@ -41,8 +54,10 @@ class GridListScreen extends Component {
itemSize={{width: '100%', height: 200}}
imageProps={{source: {uri: item.mediaUrl}}}
title="Title"
subtitle="Subitle"
overlayText
subtitle="Subtile"
alignToStart={alignToStart}
overlayText={overlayText}
horizontalAlignment={horizontalAlignment}
/>
);
} else {
Expand Down
Expand Up @@ -74,7 +74,7 @@ const SectionsWheelPickerScreen = () => {
setSelectedMinutes(0);
}, []);

const sections: WheelPickerProps[] = useMemo(() => {
const sections: WheelPickerProps<string | number>[] = useMemo(() => {
return [
{
items: getItems(DAYS),
Expand Down
15 changes: 10 additions & 5 deletions demo/src/screens/incubatorScreens/IncubatorSliderScreen.tsx
Expand Up @@ -10,6 +10,7 @@ const MAX = 350;
const INITIAL_MIN = 30;
const INITIAL_MAX = 270;
const COLOR = Colors.blue30;
const GROUP_COLOR = Colors.yellow30;

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

const [color, setColor] = useState(COLOR);
const [groupColor, setGroupColor] = useState(Colors.yellow30);
const [groupColor, setGroupColor] = useState(GROUP_COLOR);
const [alpha, setAlpha] = useState(1);

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

const onGradientValueChange = useCallback((value: string, alpha: number) => {
const onGradientValueChange = useCallback((value: string, _: number) => {
setColor(value);
}, []);

const onGradientAlphaValueChange = useCallback((_: string, alpha: number) => {
setAlpha(alpha);
}, []);

Expand Down Expand Up @@ -201,7 +205,7 @@ const IncubatorSliderScreen = () => {
<GradientSlider
color={color}
containerStyle={styles.gradientSliderContainer}
onValueChange={onGradientValueChange}
onValueChange={onGradientAlphaValueChange}
// @ts-expect-error
ref={this.gradientSlider}
migrate
Expand All @@ -216,7 +220,7 @@ const IncubatorSliderScreen = () => {
</Text>
<GradientSlider
type={GradientSlider.types.HUE}
color={COLOR}
color={color}
containerStyle={styles.gradientSliderContainer}
onValueChange={onGradientValueChange}
migrate
Expand All @@ -236,7 +240,7 @@ const IncubatorSliderScreen = () => {
Color Slider Group
</Text>
<ColorSliderGroup
initialColor={groupColor}
initialColor={GROUP_COLOR}
sliderContainerStyle={styles.slider}
containerStyle={[styles.group, {borderWidth: 12, borderColor: groupColor}]}
showLabels
Expand Down Expand Up @@ -283,6 +287,7 @@ const styles = StyleSheet.create({
customThumb: {
width: 14,
height: 14,
borderWidth: 0.5,
borderRadius: 7,
backgroundColor: Colors.black,
borderColor: Colors.black
Expand Down
100 changes: 100 additions & 0 deletions docs/foundation/testing.md
@@ -0,0 +1,100 @@
---
index: 6
path: "/foundation/testing"
title: 'Testing'
---
#
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center"}}>
<h1>Testing</h1>
<label style={{
backgroundColor: "#5848ff",
color: "#ffffff",
borderRadius: "5px",
padding: "5px 10px",
margin: "10px",
display: "flex",
alignItems: "center"
}}>
<span>Experimental</span>
</label>
</div>

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:
* Changing the input of a `TextField` component can be done using the driver's `changeText`
* Pressing a button could be achieved using the Button driver's press function.
## How to use the testkits
### Initializing the driver
In order to initialize a test driver you pass it the renderTree and the component's testId as an object.

### Example
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:
```jsx
import {Button, TextField, View} from '@wix/wix-react-native-ui-lib';

type OnSubmitHandler = (firstName: string, lastName: string, address: string) => void;
const MyForm = (props: {onSubmit: OnSubmitHandler}) => {
const {onSubmit} = props;
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [address, setAddress] = useState('');
return (
<View>
<TextField label="First name" onChangeText={(value) => setFirstName(value)} value={firstName}/>
<TextField label="Last name" onChangeText={(value) => setLastName(value)} value={lastName}/>
<TextField label="Address" onChangeText={(value) => setAddress(value)} value={address}/>
<Button label="Submit" onPress={() => onSubmit(firstName, lastName, address)}/>
</View>
);
};
```
### Testing our flow
#### In order to test our flow we would do the following steps:
1. Import the TextField and Button driver from UI-LIB's testkit
```javascript
import {TextFieldDriver, ButtonDriver} from '@wix/react-native-ui-lib/testkit';
```
2. render our test case
```javascript
const renderTree = render(<MyForm onSubmit={onSubmit}/>);
```
3. Initialize our drivers for the TextFields and submit button
```javascript
const firstNameDriver = TextFieldDriver({renderTree, testID: 'firstName'});
const lastNameDriver = TextFieldDriver({renderTree, testID: 'lastName'});
const addressDriver = TextFieldDriver({renderTree, testID: 'address'});
const submitBtnDriver = ButtonDriver({renderTree, testID: 'submit'});
```
4. Change the text of the fields and submit the form.
```javascript
firstNameDriver.changeText('Musa');
lastNameDriver.changeText('The Man');
addressDriver.changeText('Yunitzman 5');
submitBtnDriver.press();
```
5. Check that the correct values were passed to the submit handler
```javascript
expect(onSubmit).toHaveBeenCalledWith('Musa', 'The Man', 'Yunitzman 5');
```
<details>
<summary>Full test</summary>

```javascript
describe('My Form', () => {
it('should submit MyForm with Musa The Man, Yunitzman 5', () => {
const onSubmit = jest.fn();
const renderTree = render(<MyForm onSubmit={onSubmit}/>);
const firstNameDriver = TextFieldDriver({renderTree, testID: 'firstName'});
const lastNameDriver = TextFieldDriver({renderTree, testID: 'lastName'});
const addressDriver = TextFieldDriver({renderTree, testID: 'address'});
const submitBtnDriver = ButtonDriver({renderTree, testID: 'submit'});
firstNameDriver.changeText('Musa');
lastNameDriver.changeText('The Man');
addressDriver.changeText('Yunitzman 5');
submitBtnDriver.press();
expect(onSubmit).toHaveBeenCalledWith('Musa', 'The Man', 'Yunitzman 5');
});
});
```
</details>


4 changes: 2 additions & 2 deletions docuilib/sidebars.js
Expand Up @@ -60,11 +60,11 @@ module.exports = {
label: 'Components',
collapsible: false,
// items: ['Basic', 'Lists', 'Form', 'Overlays', 'Layout', 'Keyboard', 'Incubator'].map(category => {
items: Object.keys(componentsCategories).map(category => {
items: Object.keys(componentsCategories).sort().map(category => {
return {
type: 'category',
label: componentsCategories[category],
collapsed: false,
collapsed: true,
items: [
{
type: 'autogenerated',
Expand Down
83 changes: 48 additions & 35 deletions scripts/prReleaseNotesCommon.js
Expand Up @@ -80,31 +80,30 @@ function isSilent(pr) {
return false;
}

function getPRsByType(PRs) {
const silentPRs = [],
features = [],
web = [],
fixes = [],
infra = [],
others = [];
function getPRsByType(PRs, categories) {
const categorizedPRs = [];

categories.forEach(category => {
categorizedPRs.push({name: category.name, PRs: [], title: category.title});
});

PRs.forEach(pr => {
if (isSilent(pr)) {
silentPRs.push(pr);
} else if (pr.branch.startsWith('feat/')) {
features.push(pr);
} else if (pr.branch.startsWith('web/')) {
web.push(pr);
} else if (pr.branch.startsWith('fix/')) {
fixes.push(pr);
} else if (pr.branch.startsWith('infra/')) {
infra.push(pr);
const category = categories.find(category => {
return pr.branch.toLowerCase().startsWith(category.branch);
});
if (category) {
const foundCategory = categorizedPRs.find(cat => cat.name === category.name);
foundCategory.PRs.push(pr);
} else if (isSilent(pr)) {
const silentCategory = categorizedPRs.find(cat => cat.name === 'silent');
silentCategory.PRs.push(pr);
} else {
others.push(pr);
const otherCategory = categorizedPRs.find(cat => cat.name === 'others');
otherCategory.PRs.push(pr);
}
});

return {silentPRs, features, web, fixes, infra, others};
return categorizedPRs;
}

function getLine(log, requester, prNumber) {
Expand Down Expand Up @@ -153,34 +152,47 @@ function getReleaseNotesForType(PRs, title) {
return releaseNotes;
}

async function _generateReleaseNotes(latestVersion, newVersion, githubToken, fileNamePrefix, repo, header, tagPrefix) {
async function _generateReleaseNotes(latestVersion,
newVersion,
githubToken,
fileNamePrefix,
repo,
header,
tagPrefix,
categories) {
const latestReleaseDate = fetchLatestReleaseDate(tagPrefix, latestVersion);
const PRs = await fetchMergedPRs(latestReleaseDate, repo, githubToken);
if (!PRs) {
return;
}

const {silentPRs, features, web, fixes, infra, others} = getPRsByType(PRs);
const prCategories = [
{name: 'features', branch: 'feat/', title: ':gift: Features'},
{name: 'web', branch: 'web/', title: ':spider_web: Web support'},
{name: 'fixes', branch: 'fix/', title: ':wrench: Fixes'},
{name: 'infra', branch: 'infra/', title: ':gear: Maintenance & Infra'},
...categories,
{name: 'others', branch: '', title: 'OTHERS'},
{
name: 'silent',
branch: '',
title: '// Silent - these PRs did not have a changelog or were left out for some other reason, is it on purpose?'
}
];

const categorizedPRs = getPRsByType(PRs, prCategories);
let releaseNotes = header;

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

releaseNotes += getReleaseNotesForType(features, ':gift: Features');

releaseNotes += getReleaseNotesForType(web, ':spider_web: Web support');

releaseNotes += getReleaseNotesForType(fixes, ':wrench: Fixes');

releaseNotes += getReleaseNotesForType(infra, ':gear: Maintenance & Infra');
categorizedPRs.forEach(({PRs, title}) => {
if (PRs.length > 0) {
releaseNotes += getReleaseNotesForType(PRs, title);
}
});

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

releaseNotes += getReleaseNotesForType(others, 'OTHERS');

releaseNotes += getReleaseNotesForType(silentPRs,
'// Silent - these PRs did not have a changelog or were left out for some other reason, is it on purpose?');

fs.writeFileSync(`${process.env.HOME}/Downloads/${fileNamePrefix}-release-notes_${newVersion}.txt`, releaseNotes, {
encoding: 'utf8'
});
Expand All @@ -194,7 +206,8 @@ async function generateReleaseNotes(latestVersion,
fileNamePrefix,
repo,
header = '',
tagPrefix = '') {
tagPrefix = '',
categories = []) {
let latestVer, newVer;
const rl = readline.createInterface({
input: process.stdin,
Expand All @@ -212,7 +225,7 @@ async function generateReleaseNotes(latestVersion,
rl.on('close', () => {
console.info(`Current latest version is v${latestVer}`);
console.info(`Generating release notes out or PRs for v${newVer}`);
_generateReleaseNotes(latestVer, newVer, githubToken, fileNamePrefix, repo, header, tagPrefix);
_generateReleaseNotes(latestVer, newVer, githubToken, fileNamePrefix, repo, header, tagPrefix, categories);
});
}

Expand Down

0 comments on commit 238aa8d

Please sign in to comment.