Skip to content

Commit

Permalink
fix: fix elevation not animating in some components
Browse files Browse the repository at this point in the history
On Android P, if a view with elevation is inside another view which animates the opacity, the elevation doesn't animate when the opacity changes.
This PR refactors components to animate opacity on the same views those have elevation to workaround this behaviour
  • Loading branch information
satya164 committed Jan 22, 2019
1 parent 4572af2 commit 2618c06
Show file tree
Hide file tree
Showing 7 changed files with 307 additions and 318 deletions.
6 changes: 4 additions & 2 deletions src/components/Chip.js
Expand Up @@ -168,8 +168,10 @@ class Chip extends React.Component<Props, State> {
.alpha(0.54)
.rgb()
.string();
const selectedBackgroundColor = color(dark ? white : black)
.alpha(mode === 'outlined' ? 0.12 : 0.24)
const selectedBackgroundColor = (dark
? color(backgroundColor).lighten(mode === 'outlined' ? 0.2 : 0.4)
: color(backgroundColor).darken(mode === 'outlined' ? 0.08 : 0.2)
)
.rgb()
.string();

Expand Down
50 changes: 29 additions & 21 deletions src/components/Dialog/Dialog.js
Expand Up @@ -3,7 +3,6 @@
import * as React from 'react';
import { StyleSheet, Platform } from 'react-native';
import Modal from '../Modal';
import Surface from '../Surface';
import DialogContent from './DialogContent';
import DialogActions from './DialogActions';
import DialogTitle from './DialogTitle';
Expand Down Expand Up @@ -108,27 +107,35 @@ class Dialog extends React.Component<Props, void> {
} = this.props;

return (
<Modal dismissable={dismissable} onDismiss={onDismiss} visible={visible}>
<Surface
style={[styles.container, { borderRadius: theme.roundness }, style]}
>
{React.Children.toArray(children)
.filter(child => child != null && typeof child !== 'boolean')
.map((child, i) => {
if (
i === 0 &&
React.isValidElement(child) &&
child.type === DialogContent
) {
// Dialog content is the first item, so we add a top padding
return React.cloneElement(child, {
style: [{ paddingTop: 24 }, child.props.style],
});
}
<Modal
dismissable={dismissable}
onDismiss={onDismiss}
visible={visible}
contentContainerStyle={[
{
borderRadius: theme.roundness,
backgroundColor: theme.colors.surface,
},
styles.container,
style,
]}
>
{React.Children.toArray(children)
.filter(child => child != null && typeof child !== 'boolean')
.map((child, i) => {
if (
i === 0 &&
React.isValidElement(child) &&
child.type === DialogContent
) {
// Dialog content is the first item, so we add a top padding
return React.cloneElement(child, {
style: [{ paddingTop: 24 }, child.props.style],
});
}

return child;
})}
</Surface>
return child;
})}
</Modal>
);
}
Expand All @@ -146,6 +153,7 @@ const styles = StyleSheet.create({
marginVertical: Platform.OS === 'android' ? 44 : 0,
marginHorizontal: 26,
elevation: 24,
justifyContent: 'flex-start',
},
});

Expand Down
76 changes: 37 additions & 39 deletions src/components/FAB/FABGroup.js
Expand Up @@ -243,64 +243,62 @@ class FABGroup extends React.Component<Props, State> {
</TouchableWithoutFeedback>
<View pointerEvents={open ? 'box-none' : 'none'}>
{actions.map((it, i) => (
<Animated.View
<View
key={i} // eslint-disable-line react/no-array-index-key
style={[
{
opacity: opacities[i],
},
]}
style={styles.item}
pointerEvents="box-none"
>
<View style={styles.item} pointerEvents="box-none">
{it.label && (
<Card
style={[
styles.label,
{ transform: [{ scale: scales[i] }] },
]}
onPress={() => {
it.onPress();
this._close();
}}
accessibilityLabel={
it.accessibilityLabel !== 'undefined'
? it.accessibilityLabel
: it.label
}
accessibilityTraits="button"
accessibilityComponentType="button"
accessibilityRole="button"
>
<Text style={{ color: labelColor }}>{it.label}</Text>
</Card>
)}
<FAB
small
icon={it.icon}
color={it.color}
{it.label && (
<Card
style={[
styles.label,
{
transform: [{ scale: scales[i] }],
backgroundColor: theme.colors.surface,
opacity: opacities[i],
},
it.style,
]}
onPress={() => {
it.onPress();
this._close();
}}
accessibilityLabel={
typeof it.accessibilityLabel !== 'undefined'
it.accessibilityLabel !== 'undefined'
? it.accessibilityLabel
: it.label
}
accessibilityTraits="button"
accessibilityComponentType="button"
accessibilityRole="button"
/>
</View>
</Animated.View>
>
<Text style={{ color: labelColor }}>{it.label}</Text>
</Card>
)}
<FAB
small
icon={it.icon}
color={it.color}
style={[
{
transform: [{ scale: scales[i] }],
opacity: opacities[i],
backgroundColor: theme.colors.surface,
},
it.style,
]}
onPress={() => {
it.onPress();
this._close();
}}
accessibilityLabel={
typeof it.accessibilityLabel !== 'undefined'
? it.accessibilityLabel
: it.label
}
accessibilityTraits="button"
accessibilityComponentType="button"
accessibilityRole="button"
/>
</View>
))}
</View>
<FAB
Expand Down
35 changes: 28 additions & 7 deletions src/components/Modal.js
Expand Up @@ -10,6 +10,7 @@ import {
BackHandler,
} from 'react-native';
import { polyfill } from 'react-lifecycles-compat';
import Surface from './Surface';
import { withTheme } from '../core/theming';
import type { Theme } from '../types';

Expand All @@ -30,6 +31,10 @@ type Props = {|
* Content of the `Modal`.
*/
children: React.Node,
/**
* Style for the content of the modal
*/
contentContainerStyle?: any,
/**
* @optional
*/
Expand Down Expand Up @@ -142,23 +147,35 @@ class Modal extends React.Component<Props, State> {
render() {
if (!this.state.rendered) return null;

const { children, dismissable, theme } = this.props;
const { children, dismissable, theme, contentContainerStyle } = this.props;
const { colors } = theme;
return (
<Animated.View
accessibilityViewIsModal
accessibilityLiveRegion="polite"
style={[{ opacity: this.state.opacity }, StyleSheet.absoluteFill]}
style={StyleSheet.absoluteFill}
>
<TouchableWithoutFeedback
onPress={dismissable ? this._hideModal : undefined}
>
<View
style={[styles.backdrop, { backgroundColor: colors.backdrop }]}
<Animated.View
style={[
styles.backdrop,
{ backgroundColor: colors.backdrop, opacity: this.state.opacity },
]}
/>
</TouchableWithoutFeedback>
<View pointerEvents="box-none" style={styles.content}>
{children}
<View style={styles.wrapper}>
<Surface
pointerEvents="box-none"
style={[
{ opacity: this.state.opacity },
styles.content,
contentContainerStyle,
]}
>
{children}
</Surface>
</View>
</Animated.View>
);
Expand All @@ -173,8 +190,12 @@ const styles = StyleSheet.create({
backdrop: {
flex: 1,
},
content: {
wrapper: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
},
content: {
backgroundColor: 'transparent',
justifyContent: 'center',
},
});
80 changes: 42 additions & 38 deletions src/components/Snackbar.js
@@ -1,9 +1,10 @@
/* @flow */

import * as React from 'react';
import { StyleSheet, Animated, View, SafeAreaView } from 'react-native';
import { StyleSheet, Animated, SafeAreaView } from 'react-native';

import Button from './Button';
import Surface from './Surface';
import Text from './Typography/Text';
import { withTheme } from '../core/theming';
import { white } from '../styles/colors';
Expand Down Expand Up @@ -182,50 +183,53 @@ class Snackbar extends React.Component<Props, State> {
render() {
const { children, visible, action, onDismiss, theme, style } = this.props;
const { colors, roundness } = theme;

if (this.state.hidden) {
return null;
}

return (
<SafeAreaView pointerEvents="box-none" style={styles.wrapper}>
<Animated.View
<Surface
pointerEvents="box-none"
accessibilityLiveRegion="polite"
style={{
opacity: this.state.opacity,
transform: [
{
scale: visible
? this.state.opacity.interpolate({
inputRange: [0, 1],
outputRange: [0.9, 1],
})
: 1,
},
],
}}
style={[
styles.container,
{
orderRadius: roundness,
opacity: this.state.opacity,
transform: [
{
scale: visible
? this.state.opacity.interpolate({
inputRange: [0, 1],
outputRange: [0.9, 1],
})
: 1,
},
],
},
style,
]}
>
{!this.state.hidden ? (
<View
pointerEvents="box-none"
style={[styles.container, { borderRadius: roundness }, style]}
<Text style={[styles.content, { marginRight: action ? 0 : 16 }]}>
{children}
</Text>
{action ? (
<Button
onPress={() => {
action.onPress();
onDismiss();
}}
style={styles.button}
color={colors.accent}
compact
mode="text"
>
<Text style={[styles.content, { marginRight: action ? 0 : 16 }]}>
{children}
</Text>
{action ? (
<Button
onPress={() => {
action.onPress();
onDismiss();
}}
style={styles.button}
color={colors.accent}
compact
mode="text"
>
{action.label.toUpperCase()}
</Button>
) : null}
</View>
{action.label.toUpperCase()}
</Button>
) : null}
</Animated.View>
</Surface>
</SafeAreaView>
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/__tests__/__snapshots__/Chip.test.js.snap
Expand Up @@ -437,7 +437,7 @@ exports[`renders selected chip 1`] = `
<View
style={
Object {
"backgroundColor": "rgba(0, 0, 0, 0.24)",
"backgroundColor": "rgb(188, 188, 188)",
"borderColor": "#ebebeb",
"borderRadius": 16,
"borderStyle": "solid",
Expand Down

0 comments on commit 2618c06

Please sign in to comment.