MM-18530 - Update Main Sidebar on iOS (#3622)
* initial working code * minimum changes for main sidebar working * add settings sidebar back in * fix visual bug with sidebar showing underneath * fix bug for android * cleanups * minor cleanups and update snapshot * add basic test * fix style errors * review changes
This commit is contained in:
parent
4c4e1907cb
commit
a0c5ad5c2b
4 changed files with 108 additions and 35 deletions
|
|
@ -196,14 +196,16 @@ export default class DrawerLayout extends Component {
|
|||
left: drawerPosition === 'left' ? 0 : null,
|
||||
right: drawerPosition === 'right' ? 0 : null,
|
||||
};
|
||||
|
||||
/* Drawer styles */
|
||||
let outputRange;
|
||||
// ios main sidebar sits mostly under the main screen, with slight move
|
||||
const translateDistance = Platform.OS === 'ios' && drawerPosition === 'left' ?
|
||||
Math.floor(drawerWidth * 0.2) : drawerWidth
|
||||
|
||||
if (this.getDrawerPosition() === 'left') {
|
||||
outputRange = [-drawerWidth, 0];
|
||||
outputRange = [-translateDistance, 0];
|
||||
} else {
|
||||
outputRange = [drawerWidth, 0];
|
||||
outputRange = [translateDistance, 0];
|
||||
}
|
||||
|
||||
const drawerTranslateX = this.openValue.interpolate({
|
||||
|
|
@ -215,30 +217,18 @@ export default class DrawerLayout extends Component {
|
|||
transform: [{ translateX: drawerTranslateX }],
|
||||
};
|
||||
|
||||
/* Overlay styles */
|
||||
const overlayOpacity = this.openValue.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 0.5],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
const animatedOverlayStyles = { opacity: overlayOpacity };
|
||||
const pointerEvents = drawerShown ? 'auto' : 'none';
|
||||
// 0 - ios main drawer
|
||||
// 1 - main | tablet
|
||||
// 2 - overlay
|
||||
// 3 - android main drawer | settings drawer
|
||||
const drawerZIndex = drawerPosition === 'left' && Platform.OS === 'ios' ? 0 : 3
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TouchableWithoutFeedback
|
||||
pointerEvents={pointerEvents}
|
||||
onPress={this._onOverlayClick}
|
||||
>
|
||||
<Animated.View
|
||||
pointerEvents={pointerEvents}
|
||||
style={[styles.overlay, animatedOverlayStyles]}
|
||||
/>
|
||||
</TouchableWithoutFeedback>
|
||||
<Animated.View
|
||||
accessibilityViewIsModal={accessibilityViewIsModal}
|
||||
style={[
|
||||
styles.drawer,
|
||||
StyleFactory.drawer(drawerZIndex),
|
||||
dynamicDrawerStyles,
|
||||
animatedDrawerStyles,
|
||||
]}
|
||||
|
|
@ -271,20 +261,61 @@ export default class DrawerLayout extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const {isTablet} = this.props;
|
||||
const {drawerPosition, drawerWidth, isTablet} = this.props;
|
||||
const panHandlers = isTablet ? emptyObject : this._panResponder.panHandlers;
|
||||
const containerStyles = [styles.container];
|
||||
if (isTablet) {
|
||||
containerStyles.push(styles.tabletContainer);
|
||||
}
|
||||
|
||||
const mainStyles = [styles.main]
|
||||
if (drawerPosition === 'left' && Platform.OS === 'ios') {
|
||||
/* Drawer styles */
|
||||
let outputRange;
|
||||
|
||||
if (this.getDrawerPosition() === 'left') {
|
||||
outputRange = [0, drawerWidth];
|
||||
} else {
|
||||
outputRange = [drawerWidth, 0];
|
||||
}
|
||||
|
||||
const drawerTranslateX = this.openValue.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange,
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
const animatedDrawerStyles = {
|
||||
transform: [{ translateX: drawerTranslateX }],
|
||||
};
|
||||
mainStyles.push(animatedDrawerStyles)
|
||||
}
|
||||
|
||||
/* Overlay styles */
|
||||
const overlayOpacity = this.openValue.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 0.5],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
const animatedOverlayStyles = { opacity: overlayOpacity };
|
||||
const pointerEvents = this.state.drawerShown ? 'auto' : 'none';
|
||||
|
||||
return (
|
||||
<View
|
||||
style={containerStyles}
|
||||
{...panHandlers}
|
||||
>
|
||||
{this.renderDrawerForTablet()}
|
||||
<Animated.View style={styles.main}>
|
||||
<Animated.View style={mainStyles}>
|
||||
<TouchableWithoutFeedback
|
||||
pointerEvents={pointerEvents}
|
||||
onPress={this._onOverlayClick}
|
||||
>
|
||||
<Animated.View
|
||||
pointerEvents={pointerEvents}
|
||||
style={[styles.overlay, animatedOverlayStyles]}
|
||||
/>
|
||||
</TouchableWithoutFeedback>
|
||||
{this.props.children}
|
||||
</Animated.View>
|
||||
{this.renderDrawer()}
|
||||
|
|
@ -508,6 +539,17 @@ export default class DrawerLayout extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
class StyleFactory {
|
||||
static drawer(zIndex) {
|
||||
return ({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
zIndex,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
|
|
@ -516,27 +558,22 @@ const styles = StyleSheet.create({
|
|||
tabletContainer: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
drawer: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
zIndex: 1001,
|
||||
},
|
||||
tablet: {
|
||||
height: '100%',
|
||||
zIndex: 0,
|
||||
zIndex: 1,
|
||||
},
|
||||
main: {
|
||||
flex: 1,
|
||||
zIndex: 0,
|
||||
zIndex: 1,
|
||||
position: 'relative', // so overlay pins to this View
|
||||
},
|
||||
overlay: {
|
||||
backgroundColor: '#000',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: -350,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
zIndex: 1000,
|
||||
zIndex: 2,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {Platform} from 'react-native';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import Preferences from 'mattermost-redux/constants/preferences';
|
||||
|
|
@ -82,4 +83,26 @@ describe('MainSidebar', () => {
|
|||
wrapper.setProps({theme: newTheme});
|
||||
expect(instance.render).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should render main sidebar below PostList for iOS', () => {
|
||||
Platform.OS = 'ios';
|
||||
|
||||
const wrapper = shallow(
|
||||
<MainSidebar {...baseProps}/>
|
||||
);
|
||||
const drawer = wrapper.dive().childAt(1);
|
||||
const drawerStyle = drawer.props().style.reduce((acc, obj) => ({...acc, ...obj}));
|
||||
expect(drawerStyle).toHaveProperty('zIndex', 0);
|
||||
});
|
||||
|
||||
test('should render main sidebar above PostList for android', () => {
|
||||
Platform.OS = 'android';
|
||||
|
||||
const wrapper = shallow(
|
||||
<MainSidebar {...baseProps}/>
|
||||
);
|
||||
const drawer = wrapper.dive().childAt(1);
|
||||
const drawerStyle = drawer.props().style.reduce((acc, obj) => ({...acc, ...obj}));
|
||||
expect(drawerStyle).toHaveProperty('zIndex', 3);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import SafeAreaView from 'app/components/safe_area_view';
|
|||
import SettingsSidebar from 'app/components/sidebars/settings';
|
||||
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {setNavigatorStyles} from 'app/utils/theme';
|
||||
import {makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
|
||||
import PushNotifications from 'app/push_notifications';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
import tracker from 'app/utils/time_tracker';
|
||||
|
|
@ -301,6 +301,7 @@ export default class ChannelBase extends PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
const baseStyle = getStyleFromTheme(theme);
|
||||
return (
|
||||
<MainSidebar
|
||||
ref={this.channelSidebarRef}
|
||||
|
|
@ -311,7 +312,9 @@ export default class ChannelBase extends PureComponent {
|
|||
ref={this.settingsSidebarRef}
|
||||
blurPostTextBox={this.blurPostTextBox}
|
||||
>
|
||||
{drawerContent}
|
||||
<View style={baseStyle.backdrop}>
|
||||
{drawerContent}
|
||||
</View>
|
||||
</SettingsSidebar>
|
||||
<InteractiveDialogController
|
||||
theme={theme}
|
||||
|
|
@ -327,6 +330,15 @@ export default class ChannelBase extends PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
backdrop: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const style = StyleSheet.create({
|
||||
flex: {
|
||||
flex: 1,
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ jest.doMock('react-native', () => {
|
|||
}, ReactNative);
|
||||
});
|
||||
|
||||
jest.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper');
|
||||
jest.mock('../node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter');
|
||||
|
||||
jest.mock('react-native-device-info', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue