Skip to content

Commit 2c74549

Browse files
committed
Merge branch 'master' into release
2 parents fc0abd1 + 9415434 commit 2c74549

File tree

9 files changed

+193
-33
lines changed

9 files changed

+193
-33
lines changed

demo/src/screens/componentScreens/FloatingButtonScreen.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export default class FloatingButtonScreen extends Component<{}, State> {
1313
state = {
1414
showButton: true,
1515
showSecondary: true,
16-
showVertical: true
16+
showVertical: true,
17+
fullWidth: false
1718
};
1819

1920
notNow = () => {
@@ -34,6 +35,7 @@ export default class FloatingButtonScreen extends Component<{}, State> {
3435
Trigger Floating Button
3536
</Text>
3637
{renderBooleanOption.call(this, 'Show Floating Button', 'showButton')}
38+
{renderBooleanOption.call(this, 'Full Width Button', 'fullWidth')}
3739
{renderBooleanOption.call(this, 'Show Secondary Button', 'showSecondary')}
3840
{renderBooleanOption.call(this, 'Button Layout Vertical', 'showVertical')}
3941

@@ -64,6 +66,7 @@ export default class FloatingButtonScreen extends Component<{}, State> {
6466

6567
<FloatingButton
6668
visible={this.state.showButton}
69+
fullWidth={this.state.fullWidth}
6770
button={{
6871
label: 'Approve',
6972
onPress: this.close

docuilib/docusaurus.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula');
1717
trailingSlash: false,
1818
customFields: {
1919
docsMainEntry: 'getting-started/setup',
20-
expoSnackLink: 'https://snack.expo.io/@ethanshar/rnuilib_snack?platform=ios&supportedPlatforms=ios,android',
20+
expoSnackLink: 'https://snack.expo.io/@ethanshar/rnuilib_snack',
2121
stars: '4.7'
2222
},
2323
plugins: ['docusaurus-plugin-sass'],

lib/ios/reactnativeuilib/keyboardtrackingview/ObservingInputAccessoryViewTemp.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ - (void)_keyboardDidShowNotification:(NSNotification*)notification
129129
{
130130
_keyboardState = KeyboardStateShown;
131131

132-
[self invalidateIntrinsicContentSize];
132+
if (_keyboardHeight > 0) { //prevent triggering observeValueForKeyPath if an external keyboard is in use
133+
[self invalidateIntrinsicContentSize];
134+
}
133135
}
134136

135137
- (void)_keyboardWillHideNotification:(NSNotification*)notification

lib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "uilib-native",
3-
"version": "4.1.2",
3+
"version": "4.1.3",
44
"homepage": "https://github.com/wix/react-native-ui-lib",
55
"description": "uilib native components (separated from js components)",
66
"main": "components/index.js",
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import React from 'react';
2+
import {ViewStyle} from 'react-native';
3+
import {render} from '@testing-library/react-native';
4+
import {Spacings} from '../../../style';
5+
import FloatingButton, {FloatingButtonLayouts} from '../index';
6+
import {ButtonDriver} from '../../button/Button.driver.new';
7+
import {useComponentDriver, ComponentProps} from '../../../testkit/new/Component.driver';
8+
9+
const TEST_ID = 'floating_button';
10+
const button = {
11+
label: 'First'
12+
};
13+
const secondaryButton = {
14+
label: 'Second'
15+
};
16+
17+
const TestCase = (props) => {
18+
return <FloatingButton {...props} testID={TEST_ID}/>;
19+
};
20+
export const FloatingButtonDriver = (props: ComponentProps) => {
21+
const driver = useComponentDriver(props);
22+
const getStyle = () => driver.getProps().style as ViewStyle;
23+
return {...driver, getStyle};
24+
};
25+
26+
describe('FloatingButton', () => {
27+
describe('visible', () => {
28+
it('should render a button according to visibility', async () => {
29+
const props = {};
30+
const renderTree = render(<TestCase {...props}/>);
31+
const buttonDriver = ButtonDriver({renderTree, testID: `${TEST_ID}.button`});
32+
expect(await buttonDriver.exists()).not.toBeTruthy();
33+
34+
renderTree.rerender(<TestCase visible/>);
35+
expect(await buttonDriver.exists()).toBeTruthy();
36+
});
37+
});
38+
39+
describe('buttons', () => {
40+
it('should render a button', async () => {
41+
const props = {visible: true};
42+
const renderTree = render(<TestCase {...props}/>);
43+
const buttonDriver = ButtonDriver({renderTree, testID: `${TEST_ID}.button`});
44+
expect(await buttonDriver.exists()).toBeTruthy();
45+
});
46+
47+
it('should not render a secondary button', async () => {
48+
const props = {visible: true};
49+
const renderTree = render(<TestCase {...props}/>);
50+
const buttonDriver = ButtonDriver({renderTree, testID: `${TEST_ID}.secondaryButton`});
51+
expect(await buttonDriver.exists()).not.toBeTruthy();
52+
});
53+
54+
it('should render a button with a label', async () => {
55+
const props = {visible: true, button};
56+
const renderTree = render(<TestCase {...props}/>);
57+
const buttonDriver = ButtonDriver({renderTree, testID: `${TEST_ID}.button`});
58+
expect(await buttonDriver.getLabel().getText()).toEqual(button.label);
59+
});
60+
61+
it('should render secondary button with label', async () => {
62+
const props = {visible: true, secondaryButton};
63+
const renderTree = render(<TestCase {...props}/>);
64+
const buttonDriver = ButtonDriver({renderTree, testID: `${TEST_ID}.secondaryButton`});
65+
expect(await buttonDriver.getLabel().getText()).toEqual(secondaryButton.label);
66+
});
67+
});
68+
69+
describe('bottomMargin', () => {
70+
it('should have default bottom margin', () => {
71+
const props = {visible: true};
72+
const renderTree = render(<TestCase {...props}/>);
73+
const buttonDriver = ButtonDriver({renderTree, testID: `${TEST_ID}.button`});
74+
75+
expect(buttonDriver.getProps()?.style?.marginBottom).toBe(Spacings.s8);
76+
});
77+
78+
it('should have default bottom margin for both buttons', () => {
79+
const props = {visible: true, secondaryButton};
80+
const renderTree = render(<TestCase {...props}/>);
81+
const buttonDriver = ButtonDriver({renderTree, testID: `${TEST_ID}.button`});
82+
const buttonDriver2 = ButtonDriver({renderTree, testID: `${TEST_ID}.secondaryButton`});
83+
84+
expect(buttonDriver.getProps()?.style?.marginBottom).toBe(Spacings.s4);
85+
expect(buttonDriver2.getProps()?.style?.marginBottom).toBe(Spacings.s7);
86+
});
87+
88+
it('should have bottom margin that match bottomMargin prop', () => {
89+
const props = {visible: true, bottomMargin: 10};
90+
const renderTree = render(<TestCase {...props}/>);
91+
const buttonDriver = ButtonDriver({renderTree, testID: `${TEST_ID}.button`});
92+
93+
expect(buttonDriver.getProps()?.style?.marginBottom).toBe(10);
94+
});
95+
96+
it('should have bottom margin for secondary button that match bottomMarginProp', () => {
97+
const props = {visible: true, secondaryButton, bottomMargin: 10};
98+
const renderTree = render(<TestCase {...props}/>);
99+
const buttonDriver = ButtonDriver({renderTree, testID: `${TEST_ID}.button`});
100+
const buttonDriver2 = ButtonDriver({renderTree, testID: `${TEST_ID}.secondaryButton`});
101+
102+
expect(buttonDriver.getProps()?.style?.marginBottom).toBe(Spacings.s4);
103+
expect(buttonDriver2.getProps()?.style?.marginBottom).toBe(10);
104+
});
105+
});
106+
107+
describe('buttonLayout', () => {
108+
it('should style vertical layout (default)', () => {
109+
const props = {visible: true, secondaryButton};
110+
const renderTree = render(<TestCase {...props}/>);
111+
const driver = FloatingButtonDriver({renderTree, testID: TEST_ID});
112+
113+
expect(driver.getStyle()?.flexDirection).toBe(undefined);
114+
expect(driver.getStyle()?.alignItems).toBe('center');
115+
expect(driver.getStyle()?.justifyContent).toBe('center');
116+
expect(driver.getStyle()?.paddingHorizontal).toBe(undefined);
117+
});
118+
119+
it('should style horizontal layout', () => {
120+
const props = {visible: true, secondaryButton, buttonLayout: FloatingButtonLayouts.HORIZONTAL};
121+
const renderTree = render(<TestCase {...props}/>);
122+
const driver = FloatingButtonDriver({renderTree, testID: TEST_ID});
123+
124+
expect(driver.getStyle()?.flexDirection).toBe('row');
125+
expect(driver.getStyle()?.alignItems).toBe('center');
126+
expect(driver.getStyle()?.justifyContent).toBe('center');
127+
expect(driver.getStyle()?.paddingHorizontal).toBe(undefined);
128+
});
129+
});
130+
131+
describe('fullWidth', () => {
132+
it('should style vertical layout (default) when fullWidth is true', () => {
133+
const props = {visible: true, secondaryButton, fullWidth: true};
134+
const renderTree = render(<TestCase {...props}/>);
135+
const driver = FloatingButtonDriver({renderTree, testID: TEST_ID});
136+
137+
expect(driver.getStyle()?.flexDirection).toBe(undefined);
138+
expect(driver.getStyle()?.alignItems).toBe(undefined);
139+
expect(driver.getStyle()?.justifyContent).toBe(undefined);
140+
expect(driver.getStyle()?.paddingHorizontal).toBe(16);
141+
});
142+
143+
it('should style horizontal layout when fullWidth is true', () => {
144+
const props = {visible: true, secondaryButton, buttonLayout: FloatingButtonLayouts.HORIZONTAL, fullWidth: true};
145+
const renderTree = render(<TestCase {...props}/>);
146+
const driver = FloatingButtonDriver({renderTree, testID: TEST_ID});
147+
148+
expect(driver.getStyle()?.flexDirection).toBe('row');
149+
expect(driver.getStyle()?.alignItems).toBe('center');
150+
expect(driver.getStyle()?.justifyContent).toBe('center');
151+
expect(driver.getStyle()?.paddingHorizontal).toBe(undefined);
152+
});
153+
});
154+
});

src/components/floatingButton/floatingButton.api.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
"type": "number",
1717
"description": "The bottom margin of the button, or secondary button if passed"
1818
},
19+
{"name": "fullWidth", "type": "boolean", "description": "Whether the buttons get the container's full with", "note": "Relevant to vertical layout only"},
20+
{"name": "buttonLayout", "type": "FloatingButtonLayouts", "description": "Button layout direction: vertical or horizontal"},
1921
{"name": "duration", "type": "number", "description": "The duration of the button's animations (show/hide)"},
2022
{"name": "withoutAnimation", "type": "boolean", "description": "Whether to show/hide the button without animation"},
2123
{"name": "hideBackgroundOverlay", "type": "boolean", "description": "Whether to show background overlay"},

src/components/floatingButton/index.tsx

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {StyleSheet, Animated} from 'react-native';
33
import {Constants, asBaseComponent} from '../../commons/new';
44
import {Colors, Spacings} from '../../style';
55
import View from '../view';
6-
import Button, {ButtonProps} from '../button';
76
import Image from '../image';
7+
import Button, {ButtonProps} from '../button';
88

99
export enum FloatingButtonLayouts {
1010
VERTICAL = 'Vertical',
@@ -27,6 +27,14 @@ export interface FloatingButtonProps {
2727
* The bottom margin of the button, or secondary button if passed
2828
*/
2929
bottomMargin?: number;
30+
/**
31+
* Whether the buttons get the container's full with (vertical layout only)
32+
*/
33+
fullWidth?: boolean;
34+
/**
35+
* Button layout direction: vertical or horizontal
36+
*/
37+
buttonLayout?: FloatingButtonLayouts | `${FloatingButtonLayouts}`;
3038
/**
3139
* The duration of the button's animations (show/hide)
3240
*/
@@ -46,10 +54,6 @@ export interface FloatingButtonProps {
4654
* <TestID>.secondaryButton - the floatingButton secondaryButton
4755
*/
4856
testID?: string;
49-
/**
50-
* Button layout direction: vertical or horizontal
51-
*/
52-
buttonLayout?: FloatingButtonLayouts | `${FloatingButtonLayouts}`;
5357
}
5458

5559
const gradientImage = () => require('./gradient.png');
@@ -156,35 +160,26 @@ class FloatingButton extends PureComponent<FloatingButtonProps> {
156160
const {secondaryButton, bottomMargin, testID, buttonLayout} = this.props;
157161

158162
const bgColor = secondaryButton?.backgroundColor || Colors.$backgroundDefault;
159-
160-
if (buttonLayout === FloatingButtonLayouts.HORIZONTAL) {
161-
return (
162-
<Button
163-
outline
164-
flex
165-
size={Button.sizes.large}
166-
testID={`${testID}.secondaryButton`}
167-
{...secondaryButton}
168-
style={[styles.shadow, styles.secondaryMargin, {backgroundColor: bgColor}]}
169-
enableShadow={false}
170-
/>
171-
);
172-
}
173-
163+
const isHorizontal = buttonLayout === FloatingButtonLayouts.HORIZONTAL;
164+
const buttonStyle = isHorizontal ?
165+
[styles.shadow, styles.secondaryMargin, {backgroundColor: bgColor}] : {marginBottom: bottomMargin || Spacings.s7};
166+
174167
return (
175168
<Button
176-
link
169+
outline={isHorizontal}
170+
flex={isHorizontal}
171+
link={!isHorizontal}
177172
size={Button.sizes.large}
178173
testID={`${testID}.secondaryButton`}
179174
{...secondaryButton}
180-
style={{marginBottom: bottomMargin || Spacings.s7}}
175+
style={buttonStyle}
181176
enableShadow={false}
182177
/>
183178
);
184179
}
185180

186181
render() {
187-
const {withoutAnimation, visible, testID} = this.props;
182+
const {withoutAnimation, visible, fullWidth, testID} = this.props;
188183
// NOTE: keep this.firstLoad as true as long as the visibility changed to true
189184
this.firstLoad && !visible ? (this.firstLoad = true) : (this.firstLoad = false);
190185

@@ -198,8 +193,9 @@ class FloatingButton extends PureComponent<FloatingButtonProps> {
198193

199194
return (
200195
<View
201-
row={!!this.isSecondaryHorizontal}
202-
center={!!this.isSecondaryHorizontal}
196+
row={this.isSecondaryHorizontal}
197+
center={this.isSecondaryHorizontal || !fullWidth}
198+
paddingH-16={!this.isSecondaryHorizontal && fullWidth}
203199
pointerEvents="box-none"
204200
animated
205201
style={[styles.container, this.getAnimatedStyle()]}
@@ -218,7 +214,6 @@ const styles = StyleSheet.create({
218214
container: {
219215
...StyleSheet.absoluteFillObject,
220216
top: undefined,
221-
alignItems: 'center',
222217
zIndex: Constants.isAndroid ? 99 : undefined
223218
},
224219
image: {

src/components/picker/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ const Picker = React.forwardRef((props: PickerProps, ref) => {
273273
);
274274
} else if (fieldType === PickerFieldTypes.settings) {
275275
return (
276-
<View flex row spread>
276+
<View flexG row spread>
277277
<Text text70 style={labelStyle}>
278278
{others.label}
279279
</Text>

src/uilib-test-renderer/helper.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import {StyleSheet} from 'react-native';
22

33
interface Props {
4-
style: any;
4+
style?: any;
55
}
66

77
interface Component {
88
props: Props;
99
}
1010

1111
const findStyle = <T>(key: string, component: Component): T => {
12-
return StyleSheet.flatten(component.props.style)[key];
12+
const style = component?.props?.style;
13+
if (style) {
14+
return StyleSheet.flatten(style)[key];
15+
}
16+
return style;
1317
};
1418

1519
export {findStyle};

0 commit comments

Comments
 (0)