diff --git a/example/src/Examples/ButtonExample.tsx b/example/src/Examples/ButtonExample.tsx
index 73d8819975..900069bb11 100644
--- a/example/src/Examples/ButtonExample.tsx
+++ b/example/src/Examples/ButtonExample.tsx
@@ -6,16 +6,16 @@ import ScreenWrapper from '../ScreenWrapper';
const ButtonExample = () => {
const theme = useTheme();
- const color = theme.isV3 ? theme.colors.secondary : theme.colors.accent;
+ const color = theme.isV3 ? theme.colors.inversePrimary : theme.colors.accent;
return (
-
+
-
-
+ {theme.isV3 && (
+
+
+ {}}
+ style={styles.button}
+ >
+ Default
+
+ {}}
+ style={styles.button}
+ >
+ Custom
+
+ {}}
+ style={styles.button}
+ >
+ Disabled
+
+ {}}
+ style={styles.button}
+ >
+ Icon
+
+ {}}
+ style={styles.button}
+ >
+ Loading
+
+ {}}
+ style={styles.button}
+ contentStyle={styles.flexReverse}
+ >
+ Icon right
+
+
+
+ )}
+
{}} style={styles.button}>
Default
{}}
style={styles.button}
>
@@ -75,24 +129,24 @@ const ButtonExample = () => {
Loading
{}}
style={styles.button}
- labelStyle={styles.fontStyles}
+ contentStyle={styles.flexReverse}
>
- Custom Font
+ Icon right
-
+
{}} style={styles.button}>
Default
{}}
style={styles.button}
>
@@ -122,9 +176,68 @@ const ButtonExample = () => {
>
Loading
+ {}}
+ style={styles.button}
+ contentStyle={styles.flexReverse}
+ >
+ Icon right
+
-
+ {theme.isV3 && (
+
+
+ {}} style={styles.button}>
+ Default
+
+ {}}
+ style={styles.button}
+ >
+ Custom
+
+ {}}
+ style={styles.button}
+ >
+ Disabled
+
+ {}}
+ style={styles.button}
+ >
+ Icon
+
+ {}}
+ style={styles.button}
+ >
+ Loading
+
+ {}}
+ style={styles.button}
+ contentStyle={styles.flexReverse}
+ >
+ Icon right
+
+
+
+ )}
+
{
>
Custom component
+ {}}
+ style={styles.button}
+ labelStyle={[styles.fontStyles, theme.isV3 && styles.md3FontStyles]}
+ >
+ Custom Font
+
@@ -177,6 +299,9 @@ const styles = StyleSheet.create({
flexReverse: {
flexDirection: 'row-reverse',
},
+ md3FontStyles: {
+ lineHeight: 32,
+ },
fontStyles: {
fontWeight: '800',
fontSize: 24,
diff --git a/src/babel/__fixtures__/rewrite-imports/output.js b/src/babel/__fixtures__/rewrite-imports/output.js
index 2aa67895de..e1d98641e7 100644
--- a/src/babel/__fixtures__/rewrite-imports/output.js
+++ b/src/babel/__fixtures__/rewrite-imports/output.js
@@ -2,7 +2,7 @@
import { Text } from 'react-native';
import PaperProvider from "react-native-paper/lib/module/core/Provider";
import BottomNavigation from "react-native-paper/lib/module/components/BottomNavigation/BottomNavigation";
-import Button from "react-native-paper/lib/module/components/Button";
+import Button from "react-native-paper/lib/module/components/Button/Button";
import FAB from "react-native-paper/lib/module/components/FAB";
import Appbar from "react-native-paper/lib/module/components/Appbar";
import * as MD2Colors from "react-native-paper/lib/module/styles/themes/v2/colors";
diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx
index e881783e20..0dbd815c30 100644
--- a/src/components/Banner.tsx
+++ b/src/components/Banner.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import { View, ViewStyle, StyleSheet, StyleProp, Animated } from 'react-native';
import Surface from './Surface';
import Text from './Typography/Text';
-import Button from './Button';
+import Button from './Button/Button';
import Icon, { IconSource } from './Icon';
import { withTheme } from '../core/theming';
import type { $RemoveChildren, Theme } from '../types';
@@ -238,7 +238,7 @@ const Banner = ({
compact
mode="text"
style={styles.button}
- color={theme.colors?.primary}
+ textColor={theme.colors?.primary}
{...others}
>
{label}
diff --git a/src/components/Button.tsx b/src/components/Button/Button.tsx
similarity index 59%
rename from src/components/Button.tsx
rename to src/components/Button/Button.tsx
index 27abb0c530..7c38e3074e 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button/Button.tsx
@@ -9,25 +9,29 @@ import {
} from 'react-native';
import color from 'color';
-import ActivityIndicator from './ActivityIndicator';
-import Icon, { IconSource } from './Icon';
-import Surface from './Surface';
-import Text from './Typography/Text';
-import TouchableRipple from './TouchableRipple/TouchableRipple';
-import { black, white } from '../styles/themes/v2/colors';
-import { withTheme } from '../core/theming';
-import type { Theme } from '../types';
+import ActivityIndicator from '../ActivityIndicator';
+import Icon, { IconSource } from '../Icon';
+import Surface from '../Surface';
+import Text from '../Typography/Text';
+import TouchableRipple from '../TouchableRipple/TouchableRipple';
+import { withTheme } from '../../core/theming';
+import type { Theme } from '../../types';
+import { ButtonMode, getButtonColors } from './utils';
type Props = React.ComponentProps & {
/**
* Mode of the button. You can change the mode to adjust the styling to give it desired emphasis.
- * - `text` - flat button without background or outline (low emphasis)
- * - `outlined` - button with an outline (medium emphasis)
- * - `contained` - button with a background color and elevation shadow (high emphasis)
+ * - `text` - flat button without background or outline, used for the lowest priority actions, especially when presenting multiple options.
+ * - `outlined` - button with an outline without background, typically used for important, but not primary action – represents medium emphasis.
+ * - `contained` - button with a background color, used for important action, have the most visual impact and high emphasis.
+ * - `elevated` - button with a background color and elevation, used when absolutely necessary e.g. button requires visual separation from a patterned background. @supported Available in v3.x with theme version 3
+ * - `container-tonal` - button with a secondary background color, an alternative middle ground between contained and outlined buttons. @supported Available in v3.x with theme version 3
*/
- mode?: 'text' | 'outlined' | 'contained';
+ mode?: 'text' | 'outlined' | 'contained' | 'elevated' | 'contained-tonal';
/**
- * Whether the color is a dark color. A dark button will render light text and vice-versa. Only applicable for `contained` mode.
+ * Whether the color is a dark color. A dark button will render light text and vice-versa. Only applicable for:
+ * * `contained` mode for theme version 2
+ * * `contained`, `contained-tonal` and `elevated` modes for theme version 3.
*/
dark?: boolean;
/**
@@ -35,9 +39,20 @@ type Props = React.ComponentProps & {
*/
compact?: boolean;
/**
+ * @deprecated Deprecated in v3.x - use `buttonColor` or `textColor` instead.
* Custom text color for flat button, or background color for contained button.
*/
color?: string;
+ /**
+ * @supported Available in v3.x
+ * Custom button's background color.
+ */
+ buttonColor?: string;
+ /**
+ * @supported Available in v3.x
+ * Custom button's text color.
+ */
+ textColor?: string;
/**
* Whether to show a loading indicator.
*/
@@ -133,134 +148,109 @@ const Button = ({
dark,
loading,
icon,
- color: buttonColor,
+ buttonColor: customButtonColor,
+ textColor: customTextColor,
children,
- uppercase = true,
accessibilityLabel,
accessibilityHint,
onPress,
onLongPress,
style,
theme,
+ uppercase = !theme.isV3,
contentStyle,
labelStyle,
testID,
accessible,
...rest
}: Props) => {
- const containedInitialElevation = theme.isV3 ? 1 : 2;
- const containedActiveElevation = theme.isV3 ? 2 : 8;
+ const isMode = React.useCallback(
+ (modeToCompare: ButtonMode) => {
+ return mode === modeToCompare;
+ },
+ [mode]
+ );
+ const { roundness, isV3, animation, fonts } = theme;
+
+ const isElevationEntitled = isV3 ? isMode('elevated') : isMode('contained');
+ const initialElevation = isV3 ? 1 : 2;
+ const activeElevation = isV3 ? 2 : 8;
const { current: elevation } = React.useRef(
- new Animated.Value(mode === 'contained' ? containedInitialElevation : 0)
+ new Animated.Value(isElevationEntitled ? initialElevation : 0)
);
+
React.useEffect(() => {
- elevation.setValue(mode === 'contained' ? containedInitialElevation : 0);
- }, [mode, elevation, containedInitialElevation]);
+ elevation.setValue(isElevationEntitled ? initialElevation : 0);
+ }, [isElevationEntitled, elevation, initialElevation]);
const handlePressIn = () => {
- if (mode === 'contained') {
- const { scale } = theme.animation;
+ if (isMode('contained')) {
+ const { scale } = animation;
Animated.timing(elevation, {
- toValue: containedActiveElevation,
+ toValue: activeElevation,
duration: 200 * scale,
- useNativeDriver: false,
+ useNativeDriver: true,
}).start();
}
};
const handlePressOut = () => {
- if (mode === 'contained') {
- const { scale } = theme.animation;
+ if (isMode('contained')) {
+ const { scale } = animation;
Animated.timing(elevation, {
- toValue: containedInitialElevation,
+ toValue: initialElevation,
duration: 150 * scale,
- useNativeDriver: false,
+ useNativeDriver: true,
}).start();
}
};
- const { colors, roundness } = theme;
- const font = theme.fonts.medium;
+ const borderRadius = (isV3 ? 5 : 1) * roundness;
+ const iconSize = isV3 ? 18 : 16;
- let backgroundColor: string,
- borderColor: string,
- textColor: string,
- borderWidth: number;
-
- if (mode === 'contained') {
- if (disabled) {
- backgroundColor = color(theme.dark ? white : black)
- .alpha(0.12)
- .rgb()
- .string();
- } else if (buttonColor) {
- backgroundColor = buttonColor;
- } else {
- backgroundColor = colors?.primary || white;
- }
- } else {
- backgroundColor = 'transparent';
- }
-
- if (mode === 'outlined') {
- borderColor = color(theme.dark ? white : black)
- .alpha(0.29)
- .rgb()
- .string();
- borderWidth = StyleSheet.hairlineWidth;
- } else {
- borderColor = 'transparent';
- borderWidth = 0;
- }
-
- if (disabled) {
- textColor = color(theme.dark ? white : black)
- .alpha(0.32)
- .rgb()
- .string();
- } else if (mode === 'contained') {
- let isDark;
-
- if (typeof dark === 'boolean') {
- isDark = dark;
- } else {
- isDark =
- backgroundColor === 'transparent'
- ? false
- : !color(backgroundColor).isLight();
- }
+ const { backgroundColor, borderColor, textColor, borderWidth } =
+ getButtonColors({
+ customButtonColor,
+ customTextColor,
+ theme,
+ mode,
+ disabled,
+ dark,
+ });
- textColor = isDark ? white : black;
- } else if (buttonColor) {
- textColor = buttonColor;
- } else {
- textColor = colors?.primary || black;
- }
+ const rippleColor = color(textColor).alpha(0.12).rgb().string();
- const rippleColor = color(textColor).alpha(0.32).rgb().string();
const buttonStyle = {
backgroundColor,
borderColor,
borderWidth,
- borderRadius: roundness,
+ borderRadius,
};
const touchableStyle = {
borderRadius: style
? ((StyleSheet.flatten(style) || {}) as ViewStyle).borderRadius ||
- roundness
- : roundness,
+ borderRadius
+ : borderRadius,
};
const { color: customLabelColor, fontSize: customLabelSize } =
StyleSheet.flatten(labelStyle) || {};
- const textStyle = { color: textColor, ...font };
- const elevationRes = disabled || mode !== 'contained' ? 0 : elevation;
+ const textStyle = { color: textColor, ...(!isV3 && fonts.medium) };
+ const elevationRes = disabled || !isElevationEntitled ? 0 : elevation;
const iconStyle =
StyleSheet.flatten(contentStyle)?.flexDirection === 'row-reverse'
- ? styles.iconReverse
- : styles.icon;
+ ? [
+ styles.iconReverse,
+ isV3 && styles.md3IconReverse,
+ isV3 && isMode('text') && styles.md3IconReverseTextMode,
+ ]
+ : [
+ styles.icon,
+ isV3 && styles.md3Icon,
+ isV3 && isMode('text') && styles.md3IconTextMode,
+ ];
return (
) : null}
@@ -360,18 +357,46 @@ const styles = StyleSheet.create({
marginRight: 12,
marginLeft: -4,
},
+ md3Icon: {
+ marginLeft: 16,
+ marginRight: -16,
+ },
+ md3IconReverse: {
+ marginLeft: -16,
+ marginRight: 16,
+ },
+ md3IconTextMode: {
+ marginLeft: 12,
+ marginRight: -8,
+ },
+ md3IconReverseTextMode: {
+ marginLeft: -8,
+ marginRight: 12,
+ },
label: {
textAlign: 'center',
- letterSpacing: 1,
marginVertical: 9,
marginHorizontal: 16,
},
+ md2Label: {
+ letterSpacing: 1,
+ },
compactLabel: {
marginHorizontal: 8,
},
uppercaseLabel: {
textTransform: 'uppercase',
},
+ md3Label: {
+ marginVertical: 10,
+ marginHorizontal: 24,
+ },
+ md3LabelText: {
+ marginHorizontal: 12,
+ },
+ md3LabelTextAddons: {
+ marginHorizontal: 16,
+ },
});
export default withTheme(Button);
diff --git a/src/components/Button/utils.tsx b/src/components/Button/utils.tsx
new file mode 100644
index 0000000000..3e7ae978d0
--- /dev/null
+++ b/src/components/Button/utils.tsx
@@ -0,0 +1,230 @@
+import { StyleSheet } from 'react-native';
+import color from 'color';
+import { black, white } from '../../styles/themes/v2/colors';
+import type { Theme } from '../../types';
+
+export type ButtonMode =
+ | 'text'
+ | 'outlined'
+ | 'contained'
+ | 'elevated'
+ | 'contained-tonal';
+
+type BaseProps = {
+ isMode: (mode: ButtonMode) => boolean;
+ theme: Theme;
+ disabled?: boolean;
+};
+
+const isDark = ({
+ dark,
+ backgroundColor,
+}: {
+ dark?: boolean;
+ backgroundColor?: string;
+}) => {
+ if (typeof dark === 'boolean') {
+ return dark;
+ }
+
+ if (backgroundColor === 'transparent') {
+ return false;
+ }
+
+ if (backgroundColor !== 'transparent') {
+ return !color(backgroundColor).isLight();
+ }
+
+ return false;
+};
+
+const getButtonBackgroundColor = ({
+ isMode,
+ theme,
+ disabled,
+ customButtonColor,
+}: BaseProps & {
+ customButtonColor?: string;
+}) => {
+ if (customButtonColor && !disabled) {
+ return customButtonColor;
+ }
+
+ if (theme.isV3) {
+ if (disabled) {
+ if (isMode('outlined') || isMode('text')) {
+ return 'transparent';
+ }
+
+ return theme.colors.surfaceDisabled;
+ }
+
+ if (isMode('elevated')) {
+ return theme.colors.elevation.level1;
+ }
+
+ if (isMode('contained')) {
+ return theme.colors.primary;
+ }
+
+ if (isMode('contained-tonal')) {
+ return theme.colors.secondaryContainer;
+ }
+ }
+
+ if (isMode('contained')) {
+ if (disabled) {
+ return color(theme.dark ? white : black)
+ .alpha(0.12)
+ .rgb()
+ .string();
+ }
+
+ return theme.colors.primary;
+ }
+
+ return 'transparent';
+};
+
+const getButtonTextColor = ({
+ isMode,
+ theme,
+ disabled,
+ customTextColor,
+ backgroundColor,
+ dark,
+}: BaseProps & {
+ customTextColor?: string;
+ backgroundColor: string;
+ dark?: boolean;
+}) => {
+ if (customTextColor && !disabled) {
+ return customTextColor;
+ }
+
+ if (theme.isV3) {
+ if (disabled) {
+ return theme.colors.onSurfaceDisabled;
+ }
+
+ if (typeof dark === 'boolean') {
+ if (
+ isMode('contained') ||
+ isMode('contained-tonal') ||
+ isMode('elevated')
+ ) {
+ return isDark({ dark, backgroundColor }) ? white : black;
+ }
+ }
+
+ if (isMode('outlined') || isMode('text') || isMode('elevated')) {
+ return theme.colors.primary;
+ }
+
+ if (isMode('contained')) {
+ return theme.colors.onPrimary;
+ }
+
+ if (isMode('contained-tonal')) {
+ return theme.colors.onSecondaryContainer;
+ }
+ }
+
+ if (disabled) {
+ return color(theme.dark ? white : black)
+ .alpha(0.32)
+ .rgb()
+ .string();
+ }
+
+ if (isMode('contained')) {
+ return isDark({ dark, backgroundColor }) ? white : black;
+ }
+
+ return theme.colors.primary;
+};
+
+const getButtonBorderColor = ({ isMode, disabled, theme }: BaseProps) => {
+ if (theme.isV3) {
+ if (disabled && isMode('outlined')) {
+ return theme.colors.surfaceDisabled;
+ }
+
+ if (isMode('outlined')) {
+ return theme.colors.outline;
+ }
+ }
+
+ if (isMode('outlined')) {
+ return color(theme.dark ? white : black)
+ .alpha(0.29)
+ .rgb()
+ .string();
+ }
+
+ return 'transparent';
+};
+
+const getButtonBorderWidth = ({
+ isMode,
+ theme,
+}: Omit) => {
+ if (theme.isV3) {
+ if (isMode('outlined')) {
+ return 1;
+ }
+ }
+
+ if (isMode('outlined')) {
+ return StyleSheet.hairlineWidth;
+ }
+
+ return 0;
+};
+
+export const getButtonColors = ({
+ theme,
+ mode,
+ customButtonColor,
+ customTextColor,
+ disabled,
+ dark,
+}: {
+ theme: Theme;
+ mode: ButtonMode;
+ customButtonColor?: string;
+ customTextColor?: string;
+ disabled?: boolean;
+ dark?: boolean;
+}) => {
+ const isMode = (modeToCompare: ButtonMode) => {
+ return mode === modeToCompare;
+ };
+
+ const backgroundColor = getButtonBackgroundColor({
+ isMode,
+ theme,
+ disabled,
+ customButtonColor,
+ });
+
+ const textColor = getButtonTextColor({
+ isMode,
+ theme,
+ disabled,
+ customTextColor,
+ backgroundColor,
+ dark,
+ });
+
+ const borderColor = getButtonBorderColor({ isMode, theme, disabled });
+
+ const borderWidth = getButtonBorderWidth({ isMode, theme });
+
+ return {
+ backgroundColor,
+ borderColor,
+ textColor,
+ borderWidth,
+ };
+};
diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx
index 2ea2f5603f..ee849854c5 100644
--- a/src/components/DataTable/DataTablePagination.tsx
+++ b/src/components/DataTable/DataTablePagination.tsx
@@ -12,7 +12,7 @@ import Text from '../Typography/Text';
import { withTheme, useTheme } from '../../core/theming';
import MaterialCommunityIcon from '../MaterialCommunityIcon';
import Menu from '../Menu/Menu';
-import Button from '../Button';
+import Button from '../Button/Button';
import type { Theme } from '../../types';
type Props = React.ComponentPropsWithRef &
diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx
index 8bda42c595..ff6c464618 100644
--- a/src/components/Snackbar.tsx
+++ b/src/components/Snackbar.tsx
@@ -9,7 +9,7 @@ import {
Easing,
} from 'react-native';
-import Button from './Button';
+import Button from './Button/Button';
import Surface from './Surface';
import Text from './Typography/Text';
import { withTheme } from '../core/theming';
@@ -188,7 +188,7 @@ const Snackbar = ({
const marginRight = action ? 0 : 16;
const textColor = theme.isV3
? theme.colors.inversePrimary
- : theme.colors?.accent;
+ : theme.colors.accent;
return (
{
+ const theme = isDark
+ ? isV3
+ ? MD3DarkTheme
+ : MD2DarkTheme
+ : isV3
+ ? MD3LightTheme
+ : MD2LightTheme;
+ return {
+ ...theme,
+ isV3,
+ md: (tokenKey) => get(theme.tokens, tokenKey),
+ };
+};
+
it('renders text button by default', () => {
const tree = renderer.create(Text Button).toJSON();
@@ -78,7 +100,15 @@ it('renders disabled button', () => {
it('renders button with color', () => {
const tree = renderer
- .create(Custom Button)
+ .create(Custom Button)
+ .toJSON();
+
+ expect(tree).toMatchSnapshot();
+});
+
+it('renders button with button color', () => {
+ const tree = renderer
+ .create(Custom Button)
.toJSON();
expect(tree).toMatchSnapshot();
@@ -113,3 +143,595 @@ it('renders button with an accessibility hint', () => {
expect(tree).toMatchSnapshot();
});
+
+describe('getButtonColors - background color', () => {
+ const customButtonColor = '#111111';
+
+ it('should return custom color no matter what is the theme version, when not disabled', () => {
+ expect(
+ getButtonColors({
+ customButtonColor,
+ theme: getTheme(),
+ disabled: false,
+ })
+ ).toMatchObject({ backgroundColor: customButtonColor });
+ });
+
+ ['outlined', 'text'].forEach((mode) =>
+ it(`should return correct disabled color, for theme version 3, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ customButtonColor,
+ theme: getTheme(),
+ mode,
+ disabled: true,
+ })
+ ).toMatchObject({ backgroundColor: 'transparent' });
+ })
+ );
+
+ ['outlined', 'text'].forEach((mode) =>
+ it(`should return correct disabled color, for theme version 3, dark theme, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ customButtonColor,
+ theme: getTheme(),
+ mode,
+ disabled: true,
+ })
+ ).toMatchObject({ backgroundColor: 'transparent' });
+ })
+ );
+
+ ['contained', 'contained-tonal', 'elevated'].forEach((mode) =>
+ it(`should return correct disabled color, for theme version 3, ${mode} mode`, () => {
+ return expect(
+ getButtonColors({
+ customButtonColor,
+ theme: getTheme(),
+ mode,
+ disabled: true,
+ })
+ ).toMatchObject({
+ backgroundColor: getTheme().colors.surfaceDisabled,
+ });
+ })
+ );
+
+ ['contained', 'contained-tonal', 'elevated'].forEach((mode) =>
+ it(`should return correct disabled color, for theme version 3, dark theme, ${mode} mode`, () => {
+ return expect(
+ getButtonColors({
+ customButtonColor,
+ theme: getTheme(true),
+ mode,
+ disabled: true,
+ })
+ ).toMatchObject({
+ backgroundColor: getTheme(true).colors.surfaceDisabled,
+ });
+ })
+ );
+
+ it('should return correct theme color, for theme version 3, elevated mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode: 'elevated',
+ })
+ ).toMatchObject({
+ backgroundColor: getTheme().colors.elevation.level1,
+ });
+ });
+
+ it('should return correct theme color, for theme version 3, dark theme, elevated mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true),
+ mode: 'elevated',
+ })
+ ).toMatchObject({
+ backgroundColor: getTheme(true).colors.elevation.level1,
+ });
+ });
+
+ it('should return correct theme color, for theme version 3, contained mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode: 'contained',
+ })
+ ).toMatchObject({
+ backgroundColor: getTheme().colors.primary,
+ });
+ });
+
+ it('should return correct theme color, for theme version 3, dark theme, contained mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true),
+ mode: 'contained',
+ })
+ ).toMatchObject({
+ backgroundColor: getTheme(true).colors.primary,
+ });
+ });
+
+ it('should return correct theme color, for theme version 3, contained-tonal mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode: 'contained-tonal',
+ })
+ ).toMatchObject({
+ backgroundColor: getTheme().colors.secondaryContainer,
+ });
+ });
+
+ it('should return correct theme color, for theme version 3, dark theme, contained-tonal mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true),
+ mode: 'contained-tonal',
+ })
+ ).toMatchObject({
+ backgroundColor: getTheme(true).colors.secondaryContainer,
+ });
+ });
+
+ ['text', 'outlined'].forEach((mode) =>
+ it(`should return transparent color, for theme version 3, ${mode} mode`, () => {
+ return expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode,
+ })
+ ).toMatchObject({
+ backgroundColor: 'transparent',
+ });
+ })
+ );
+
+ ['text', 'outlined'].forEach((mode) =>
+ it(`should return transparent color, for theme version 3, dark theme, ${mode} mode`, () => {
+ return expect(
+ getButtonColors({
+ theme: getTheme(true),
+ mode,
+ })
+ ).toMatchObject({
+ backgroundColor: 'transparent',
+ });
+ })
+ );
+
+ it('should return correct theme color, for theme version 2, contained mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode: 'contained',
+ })
+ ).toMatchObject({
+ backgroundColor: getTheme(false, false).colors.primary,
+ });
+ });
+
+ it('should return correct theme color, for theme version 2, when disabled, contained mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode: 'contained',
+ disabled: true,
+ })
+ ).toMatchObject({
+ backgroundColor: color(black).alpha(0.12).rgb().string(),
+ });
+ });
+
+ it('should return correct theme color, for theme version 2, when disabled, dark theme, contained mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true, false),
+ mode: 'contained',
+ disabled: true,
+ })
+ ).toMatchObject({
+ backgroundColor: color(white).alpha(0.12).rgb().string(),
+ });
+ });
+
+ ['text', 'outlined'].forEach((mode) =>
+ it(`should return correct theme color, for theme version 2, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode,
+ })
+ ).toMatchObject({
+ backgroundColor: 'transparent',
+ });
+ })
+ );
+
+ ['text', 'outlined'].forEach((mode) =>
+ it(`should return correct theme color, for theme version 2, dark theme, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true, false),
+ mode,
+ })
+ ).toMatchObject({
+ backgroundColor: 'transparent',
+ });
+ })
+ );
+
+ ['text', 'outlined'].forEach((mode) =>
+ it(`should return correct theme color, for theme version 2, when disabled, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode,
+ disabled: true,
+ })
+ ).toMatchObject({
+ backgroundColor: 'transparent',
+ });
+ })
+ );
+
+ ['text', 'outlined'].forEach((mode) =>
+ it(`should return correct theme color, for theme version 2, when disabled, dark theme, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true, false),
+ mode,
+ disabled: true,
+ })
+ ).toMatchObject({
+ backgroundColor: 'transparent',
+ });
+ })
+ );
+});
+
+describe('getButtonColors - text color', () => {
+ const customTextColor = '#313131';
+
+ it('should return custom text color no matter what is the theme version, when not disabled', () => {
+ expect(
+ getButtonColors({
+ customTextColor,
+ theme: getTheme(),
+ disabled: false,
+ })
+ ).toMatchObject({ textColor: customTextColor });
+ });
+
+ it('should return correct disabled text color, for theme version 3, no matter what the mode is', () => {
+ expect(
+ getButtonColors({
+ customTextColor,
+ theme: getTheme(),
+ disabled: true,
+ })
+ ).toMatchObject({
+ textColor: getTheme().colors.onSurfaceDisabled,
+ });
+ });
+
+ it('should return correct disabled text color, for theme version 3, dark theme, no matter what the mode is', () => {
+ expect(
+ getButtonColors({
+ customTextColor,
+ theme: getTheme(true),
+ disabled: true,
+ })
+ ).toMatchObject({
+ textColor: getTheme(true).colors.onSurfaceDisabled,
+ });
+ });
+
+ ['contained', 'contained-tonal', 'elevated'].forEach((mode) =>
+ it(`should return correct text color for dark prop, for theme version 3, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode,
+ dark: true,
+ })
+ ).toMatchObject({
+ textColor: white,
+ });
+ })
+ );
+
+ ['outlined', 'text', 'elevated'].forEach((mode) =>
+ it(`should return correct theme text color, for theme version 3, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode,
+ })
+ ).toMatchObject({
+ textColor: getTheme().colors.primary,
+ });
+ })
+ );
+
+ ['outlined', 'text', 'elevated'].forEach((mode) =>
+ it(`should return correct theme text color, for theme version 3, dark theme, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true),
+ mode,
+ })
+ ).toMatchObject({
+ textColor: getTheme(true).colors.primary,
+ });
+ })
+ );
+
+ it('should return correct theme text color, for theme version 3, contained mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode: 'contained',
+ })
+ ).toMatchObject({
+ textColor: getTheme().colors.onPrimary,
+ });
+ });
+
+ it('should return correct theme text color, for theme version 3, dark theme, contained mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true),
+ mode: 'contained',
+ })
+ ).toMatchObject({
+ textColor: getTheme(true).colors.onPrimary,
+ });
+ });
+
+ it('should return correct theme text color, for theme version 3, contained-tonal mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode: 'contained-tonal',
+ })
+ ).toMatchObject({
+ textColor: getTheme().colors.onSecondaryContainer,
+ });
+ });
+
+ it('should return correct theme text color, for theme version 3, dark theme contained-tonal mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true),
+ mode: 'contained-tonal',
+ })
+ ).toMatchObject({
+ textColor: getTheme(true).colors.onSecondaryContainer,
+ });
+ });
+
+ it('should return correct theme text color, for theme version 2, when disabled, no matter what the mode is', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ disabled: true,
+ })
+ ).toMatchObject({
+ textColor: color(black).alpha(0.32).rgb().string(),
+ });
+ });
+
+ it('should return correct theme text color, for theme version 2, when disabled, dark theme, no matter what the mode is', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true, false),
+ disabled: true,
+ })
+ ).toMatchObject({
+ textColor: color(white).alpha(0.32).rgb().string(),
+ });
+ });
+
+ it('should return correct theme text color, for theme version 2, contained mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode: 'contained',
+ dark: true,
+ })
+ ).toMatchObject({
+ textColor: '#ffffff',
+ });
+ });
+
+ ['text', 'outlined'].forEach((mode) =>
+ it(`should return correct theme text color, for theme version 2, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode,
+ })
+ ).toMatchObject({
+ textColor: getTheme(false, false).colors.primary,
+ });
+ })
+ );
+
+ ['text', 'outlined'].forEach((mode) =>
+ it(`should return correct theme text color, for theme version 2, dark theme, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true, false),
+ mode,
+ })
+ ).toMatchObject({
+ textColor: getTheme(true, false).colors.primary,
+ });
+ })
+ );
+});
+
+describe('getButtonColors - border color', () => {
+ it('should return correct border color, for theme version 3, when disabled, outlined mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ disabled: true,
+ mode: 'outlined',
+ })
+ ).toMatchObject({
+ borderColor: getTheme().colors.surfaceDisabled,
+ });
+ });
+
+ it('should return correct border color, for theme version 3, when disabled, dark theme, outlined mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true),
+ disabled: true,
+ mode: 'outlined',
+ })
+ ).toMatchObject({
+ borderColor: getTheme(true).colors.surfaceDisabled,
+ });
+ });
+
+ it('should return correct border color, for theme version 3, outlined mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode: 'outlined',
+ })
+ ).toMatchObject({
+ borderColor: getTheme().colors.outline,
+ });
+ });
+
+ it('should return correct border color, for theme version 3, dark theme, outlined mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true),
+ mode: 'outlined',
+ })
+ ).toMatchObject({
+ borderColor: getTheme(true).colors.outline,
+ });
+ });
+
+ ['text', 'contained', 'contained-tonal', 'elevated'].forEach((mode) =>
+ it(`should return transparent border, for theme version 3, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode,
+ })
+ ).toMatchObject({
+ borderColor: 'transparent',
+ });
+ })
+ );
+
+ ['text', 'contained', 'contained-tonal', 'elevated'].forEach((mode) =>
+ it(`should return transparent border, for theme version 3, dark theme, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true),
+ mode,
+ })
+ ).toMatchObject({
+ borderColor: 'transparent',
+ });
+ })
+ );
+
+ it('should return correct border color, for theme version 2, outlined mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode: 'outlined',
+ })
+ ).toMatchObject({
+ borderColor: color(black).alpha(0.29).rgb().string(),
+ });
+ });
+
+ it('should return correct border color, for theme version 2, dark theme, outlined mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(true, false),
+ mode: 'outlined',
+ })
+ ).toMatchObject({
+ borderColor: color(white).alpha(0.29).rgb().string(),
+ });
+ });
+
+ ['text', 'contained', 'contained-tonal', 'elevated'].forEach((mode) =>
+ it(`should return transparent border, for theme version 2, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode,
+ })
+ ).toMatchObject({
+ borderColor: 'transparent',
+ });
+ })
+ );
+
+ ['text', 'contained', 'contained-tonal', 'elevated'].forEach((mode) =>
+ it(`should return transparent border, for theme version 2, dark theme, ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode,
+ })
+ ).toMatchObject({
+ borderColor: 'transparent',
+ });
+ })
+ );
+});
+
+describe('getButtonColors - border width', () => {
+ it('should return correct border width, for theme version 3, outlined mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode: 'outlined',
+ })
+ ).toMatchObject({
+ borderWidth: 1,
+ });
+ });
+
+ it('should return correct border width, for theme version 2, outlined mode', () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(false, false),
+ mode: 'outlined',
+ })
+ ).toMatchObject({
+ borderWidth: StyleSheet.hairlineWidth,
+ });
+ });
+
+ ['text', 'contained', 'contained-tonal', 'elevated'].forEach((mode) =>
+ it(`should return correct border width, for ${mode} mode`, () => {
+ expect(
+ getButtonColors({
+ theme: getTheme(),
+ mode,
+ })
+ ).toMatchObject({
+ borderWidth: 0,
+ });
+ })
+ );
+});
diff --git a/src/components/__tests__/Menu.test.js b/src/components/__tests__/Menu.test.js
index f264ae0e5e..492e8f5fc5 100644
--- a/src/components/__tests__/Menu.test.js
+++ b/src/components/__tests__/Menu.test.js
@@ -2,7 +2,7 @@ import * as React from 'react';
import { StyleSheet } from 'react-native';
import renderer from 'react-test-renderer';
import Menu from '../Menu/Menu.tsx';
-import Button from '../Button.tsx';
+import Button from '../Button/Button.tsx';
const styles = StyleSheet.create({
contentStyle: {
diff --git a/src/components/__tests__/__snapshots__/Banner.test.js.snap b/src/components/__tests__/__snapshots__/Banner.test.js.snap
index 1bfe49d1d1..af0cffa26b 100644
--- a/src/components/__tests__/__snapshots__/Banner.test.js.snap
+++ b/src/components/__tests__/__snapshots__/Banner.test.js.snap
@@ -156,11 +156,14 @@ exports[`render visible banner, with custom theme 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
Object {
"marginHorizontal": 8,
},
@@ -172,10 +175,6 @@ exports[`render visible banner, with custom theme 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
@@ -454,11 +453,14 @@ exports[`renders visible banner, with action buttons and with image 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
Object {
"marginHorizontal": 8,
},
@@ -470,10 +472,6 @@ exports[`renders visible banner, with action buttons and with image 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
@@ -646,11 +644,14 @@ exports[`renders visible banner, with action buttons and without image 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
Object {
"marginHorizontal": 8,
},
@@ -662,10 +663,6 @@ exports[`renders visible banner, with action buttons and without image 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
@@ -744,11 +741,14 @@ exports[`renders visible banner, with action buttons and without image 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
Object {
"marginHorizontal": 8,
},
@@ -760,10 +760,6 @@ exports[`renders visible banner, with action buttons and without image 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
diff --git a/src/components/__tests__/__snapshots__/Button.test.js.snap b/src/components/__tests__/__snapshots__/Button.test.js.snap
index a0d04b8b4d..4fb393b51b 100644
--- a/src/components/__tests__/__snapshots__/Button.test.js.snap
+++ b/src/components/__tests__/__snapshots__/Button.test.js.snap
@@ -69,11 +69,14 @@ exports[`renders button with an accessibility hint 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
undefined,
Object {
"textTransform": "uppercase",
@@ -83,10 +86,6 @@ exports[`renders button with an accessibility hint 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
@@ -168,11 +167,14 @@ exports[`renders button with an accessibility label 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
undefined,
Object {
"textTransform": "uppercase",
@@ -182,7 +184,100 @@ exports[`renders button with an accessibility label 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
+ undefined,
+ ],
+ ]
+ }
+ >
+ Button with accessibility label
+
+
+
+
+`;
+
+exports[`renders button with button color 1`] = `
+
+
+
+
- Button with accessibility label
+ Custom Button
@@ -266,11 +361,14 @@ exports[`renders button with color 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
undefined,
Object {
"textTransform": "uppercase",
@@ -280,10 +378,6 @@ exports[`renders button with color 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
@@ -365,11 +459,14 @@ exports[`renders button with custom testID 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
undefined,
Object {
"textTransform": "uppercase",
@@ -379,10 +476,6 @@ exports[`renders button with custom testID 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
@@ -450,10 +543,14 @@ exports[`renders button with icon 1`] = `
>
@@ -1110,11 +1211,14 @@ exports[`renders loading button 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
undefined,
Object {
"textTransform": "uppercase",
@@ -1124,10 +1228,6 @@ exports[`renders loading button 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
@@ -1208,11 +1308,14 @@ exports[`renders outlined button with mode 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
undefined,
Object {
"textTransform": "uppercase",
@@ -1222,10 +1325,6 @@ exports[`renders outlined button with mode 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
@@ -1306,11 +1405,14 @@ exports[`renders text button by default 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
undefined,
Object {
"textTransform": "uppercase",
@@ -1320,10 +1422,6 @@ exports[`renders text button by default 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
@@ -1404,11 +1502,14 @@ exports[`renders text button with mode 1`] = `
},
Array [
Object {
- "letterSpacing": 1,
"marginHorizontal": 16,
"marginVertical": 9,
"textAlign": "center",
},
+ Object {
+ "letterSpacing": 1,
+ },
+ false,
undefined,
Object {
"textTransform": "uppercase",
@@ -1418,10 +1519,6 @@ exports[`renders text button with mode 1`] = `
"fontFamily": "System",
"fontWeight": "500",
},
- Object {
- "fontFamily": "System",
- "fontWeight": "500",
- },
undefined,
],
]
diff --git a/src/components/__tests__/__snapshots__/DataTable.test.js.snap b/src/components/__tests__/__snapshots__/DataTable.test.js.snap
index 6423882c25..9ea4bc91a0 100644
--- a/src/components/__tests__/__snapshots__/DataTable.test.js.snap
+++ b/src/components/__tests__/__snapshots__/DataTable.test.js.snap
@@ -1007,10 +1007,14 @@ exports[`renders data table pagination with options select 1`] = `
>