From bd284d530591cd2745cb497e4c989561c573d3ce Mon Sep 17 00:00:00 2001 From: Josh Justice Date: Mon, 11 Feb 2019 14:00:06 -0800 Subject: [PATCH] Separate RTL examples in RNTester (#23354) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Splits RTLExample into separate exported examples, so they can be filtered. This will help with Detox tests. Previously the single forceRTL toggle affected multiple examples because they all share state—although the box model examples at the end had their own toggles. Now each example has its own RTL toggle so it is always available even when examples are filtered, and so the examples don't have to share state. There is still the separate forceRTL toggle that changes the setting in `I18nManager`, which affects the default setting when the page appears, as well as the direction of the "with directional meaning" pointer icon. [General] [Changed] - Split RTLExample into separate exported examples Pull Request resolved: https://github.com/facebook/react-native/pull/23354 Differential Revision: D14030498 Pulled By: cpojer fbshipit-source-id: 44eb493297f6a4832b55ef2b02a93dc5c213f337 --- js/RTLExample.js | 496 ++++++++++++++++++++++++++--------------------- 1 file changed, 279 insertions(+), 217 deletions(-) diff --git a/js/RTLExample.js b/js/RTLExample.js index 96fe2fc8a2a..a58b744709a 100644 --- a/js/RTLExample.js +++ b/js/RTLExample.js @@ -16,9 +16,7 @@ const { Animated, I18nManager, Image, - PanResponder, PixelRatio, - ScrollView, StyleSheet, Text, TouchableWithoutFeedback, @@ -28,14 +26,20 @@ const { } = ReactNative; const Platform = require('Platform'); -const RNTesterPage = require('./RNTesterPage'); -const RNTesterBlock = require('./RNTesterBlock'); - type State = { toggleStatus: any, pan: Object, linear: Object, isRTL: boolean, +}; + +type RTLToggleState = { + isRTL: boolean, +}; + +type AnimationState = { + toggleStatus: any, + linear: Object, windowWidth: number, }; @@ -65,10 +69,11 @@ function ListItem(props) { ); } -function TextAlignmentExample(props) { +const TextAlignmentExample = withRTLState(({isRTL, setRTL, ...props}) => { return ( - - + + + Left-to-Right language without text alignment. @@ -84,9 +89,37 @@ function TextAlignmentExample(props) { '\u05D9\u05D9\u05E9\u05D5\u05E8 \u05D8\u05E7\u05E1\u05D8'} - + ); -} +}); + +const IconsExample = withRTLState(({isRTL, setRTL}) => { + return ( + + + + + + + Without directional meaning + + + + + + With directional meaning + + + + + ); +}); function AnimationBlock(props) { return ( @@ -116,7 +149,9 @@ function withRTLState(Component) { render() { const setRTL = isRTL => this.setState({isRTL: isRTL}); - return ; + return ( + + ); } }; } @@ -137,11 +172,124 @@ const RTLToggler = ({isRTL, setRTL}) => { ); }; +class RTLToggleExample extends React.Component { + constructor(props: Object) { + super(props); + + this.state = { + isRTL: IS_RTL, + }; + } + + render() { + return ( + + + + {this.state.isRTL ? 'Right-to-Left' : 'Left-to-Right'} + + + + forceRTL + + + + + + ); + } + + _onDirectionChange = () => { + I18nManager.forceRTL(!this.state.isRTL); + this.setState({isRTL: !this.state.isRTL}); + Alert.alert( + 'Reload this page', + 'Please reload this page to change the UI direction! ' + + 'All examples in this app will be affected. ' + + 'Check them out to see what they look like in RTL layout.', + ); + }; +} + +const SimpleListItemExample = withRTLState(({isRTL, setRTL}) => { + return ( + + + + + + + + ); +}); + +const AnimationContainer = withRTLState(({isRTL, setRTL}) => { + return ; +}); + +class AnimationExample extends React.Component { + constructor(props: Object) { + super(props); + + this.state = { + toggleStatus: {}, + linear: new Animated.Value(0), + windowWidth: 0, + }; + } + + render() { + return ( + + + + + + + ); + } + + _onLayout = (e: Object) => { + this.setState({ + windowWidth: e.nativeEvent.layout.width, + }); + }; + + _linearTap = (e: Object) => { + this.setState({ + toggleStatus: { + ...this.state.toggleStatus, + [e]: !this.state.toggleStatus[e], + }, + }); + const offset = IMAGE_SIZE[0] / SCALE / 2 + 10; + const toMaxDistance = + (this.props.isRTL ? -1 : 1) * (this.state.windowWidth / 2 - offset); + Animated.timing(this.state.linear, { + toValue: this.state.toggleStatus[e] ? toMaxDistance : 0, + duration: 2000, + useNativeDriver: true, + }).start(); + }; +} + const PaddingExample = withRTLState(({isRTL, setRTL}) => { const color = 'teal'; return ( - + Styles paddingStart: 50, paddingEnd: 10 @@ -169,13 +317,13 @@ const PaddingExample = withRTLState(({isRTL, setRTL}) => { - + ); }); const MarginExample = withRTLState(({isRTL, setRTL}) => { return ( - + Styles marginStart: 50, marginEnd: 10 @@ -203,13 +351,13 @@ const MarginExample = withRTLState(({isRTL, setRTL}) => { - + ); }); const PositionExample = withRTLState(({isRTL, setRTL}) => { return ( - + Styles start: 50 @@ -253,19 +401,19 @@ const PositionExample = withRTLState(({isRTL, setRTL}) => { - + ); }); const BorderWidthExample = withRTLState(({isRTL, setRTL}) => { return ( - + Styles borderStartWidth: 10, borderEndWidth: 50 Demo: - + { - + ); }); const BorderColorExample = withRTLState(({isRTL, setRTL}) => { return ( - + Styles borderStartColor: 'red', borderEndColor: 'green', Demo: - + { - + ); }); const BorderRadiiExample = withRTLState(({isRTL, setRTL}) => { return ( - + Styles borderTopStartRadius: 10, borderTopEndRadius: 20, @@ -316,7 +464,7 @@ const BorderRadiiExample = withRTLState(({isRTL, setRTL}) => { borderBottomEndRadius: 40 Demo: - + { - + ); }); const BorderExample = withRTLState(({isRTL, setRTL}) => { return ( - + Styles borderStartColor: 'red', borderEndColor: 'green', @@ -349,7 +497,7 @@ const BorderExample = withRTLState(({isRTL, setRTL}) => { borderBottomEndRadius: 40 Demo: - + { - + ); }); -class RTLExample extends React.Component { - _panResponder: Object; - - constructor(props: Object) { - super(props); - const pan = new Animated.ValueXY(); - - this._panResponder = PanResponder.create({ - onStartShouldSetPanResponder: () => true, - onPanResponderGrant: this._onPanResponderGrant, - onPanResponderMove: Animated.event([null, {dx: pan.x, dy: pan.y}]), - onPanResponderRelease: this._onPanResponderEnd, - onPanResponderTerminate: this._onPanResponderEnd, - }); - - this.state = { - toggleStatus: {}, - pan, - linear: new Animated.Value(0), - isRTL: IS_RTL, - windowWidth: 0, - }; - } - - render() { - return ( - - - - - - {this.state.isRTL ? 'Right-to-Left' : 'Left-to-Right'} - - - - - - forceRTL - - - - - - - - - - - - - - - - - - - - Without directional meaning - - - - - - With directional meaning - - - - - - - - - - - - - - - - - - - ); - } - - _onLayout = (e: Object) => { - this.setState({ - windowWidth: e.nativeEvent.layout.width, - }); - }; - - _onDirectionChange = () => { - I18nManager.forceRTL(!this.state.isRTL); - this.setState({isRTL: !this.state.isRTL}); - Alert.alert( - 'Reload this page', - 'Please reload this page to change the UI direction! ' + - 'All examples in this app will be affected. ' + - 'Check them out to see what they look like in RTL layout.', - ); - }; - - _linearTap = (e: Object) => { - this.setState({ - toggleStatus: { - ...this.state.toggleStatus, - [e]: !this.state.toggleStatus[e], - }, - }); - const offset = IMAGE_SIZE[0] / SCALE / 2 + 10; - const toMaxDistance = - (IS_RTL ? -1 : 1) * (this.state.windowWidth / 2 - offset); - Animated.timing(this.state.linear, { - toValue: this.state.toggleStatus[e] ? toMaxDistance : 0, - duration: 2000, - useNativeDriver: true, - }).start(); - }; - - _onPanResponderGrant = (e: Object, gestureState: Object) => { - this.state.pan.stopAnimation(value => { - this.state.pan.setOffset(value); - }); - }; - - _onPanResponderEnd = (e: Object, gestureState: Object) => { - this.state.pan.flattenOffset(); - Animated.sequence([ - Animated.decay(this.state.pan, { - velocity: {x: gestureState.vx, y: gestureState.vy}, - deceleration: 0.995, - }), - Animated.spring(this.state.pan, {toValue: {x: 0, y: 0}}), - ]).start(); - }; -} +const directionStyle = isRTL => + Platform.OS === 'ios' ? {direction: isRTL ? 'rtl' : 'ltr'} : null; const styles = StyleSheet.create({ container: { @@ -567,6 +532,7 @@ const styles = StyleSheet.create({ backgroundColor: '#f8f8f8', borderWidth: 0.5, borderColor: 'black', + marginBottom: 15, }, directionText: { padding: 10, @@ -679,9 +645,105 @@ exports.title = 'RTLExample'; exports.description = 'Examples to show how to apply components to RTL layout.'; exports.examples = [ { - title: 'Simple RTL', - render: function(): React.Element { - return ; + title: 'Current Layout Direction', + render: function(): React.Element { + return ; + }, + }, + { + title: 'A Simple List Item Layout', + render: function(): React.Element { + return ; + }, + }, + { + title: 'Default Text Alignment', + description: + 'In iOS, it depends on active language. ' + + 'In Android, it depends on the text content.', + render: function(): React.Element { + return ; + }, + }, + { + title: "Using textAlign: 'left'", + description: + 'In iOS/Android, text alignment flips regardless of ' + + 'languages or text content.', + render: function(): React.Element { + return ( + + ); + }, + }, + { + title: "Using textAlign: 'right'", + description: + 'In iOS/Android, text alignment flips regardless of ' + + 'languages or text content.', + render: function(): React.Element { + return ( + + ); + }, + }, + { + title: 'Working With Icons', + render: function(): React.Element { + return ; + }, + }, + { + title: 'Controlling Animation', + description: 'Animation direction according to layout', + render: function(): React.Element { + return ; + }, + }, + { + title: 'Padding Start/End', + render: function(): React.Element { + return ; + }, + }, + { + title: 'Margin Start/End', + render: function(): React.Element { + return ; + }, + }, + { + title: 'Position Start/End', + render: function(): React.Element { + return ; + }, + }, + { + title: 'Border Width Start/End', + render: function(): React.Element { + return ; + }, + }, + { + title: 'Border Color Start/End', + render: function(): React.Element { + return ; + }, + }, + { + title: 'Border Radii Start/End', + render: function(): React.Element { + return ; + }, + }, + { + title: 'Border', + render: function(): React.Element { + return ; }, }, ];