diff --git a/src/components/Avatar/AvatarIcon.tsx b/src/components/Avatar/AvatarIcon.tsx
index 9c3b813e59..5e0c5d0c00 100644
--- a/src/components/Avatar/AvatarIcon.tsx
+++ b/src/components/Avatar/AvatarIcon.tsx
@@ -2,13 +2,11 @@ import { StyleSheet, View } from 'react-native';
import type { StyleProp, ViewProps, ViewStyle } from 'react-native';
import { useInternalTheme } from '../../core/theming';
-import { white } from '../../theme/colors';
+import { cornerFull } from '../../theme/tokens/sys/shape';
import type { ThemeProp } from '../../types';
-import getContrastingColor from '../../utils/getContrastingColor';
import Icon from '../Icon';
import type { IconSource } from '../Icon';
-
-const defaultSize = 64;
+import { DEFAULT_SIZE, ICON_SIZE_RATIO, resolveAvatarColors } from './utils';
export type Props = ViewProps & {
/**
@@ -45,17 +43,18 @@ export type Props = ViewProps & {
*/
const Avatar = ({
icon,
- size = defaultSize,
+ size = DEFAULT_SIZE,
style,
theme: themeOverrides,
...rest
}: Props) => {
const theme = useInternalTheme(themeOverrides);
- const { backgroundColor = theme.colors?.primary, ...restStyle } =
- StyleSheet.flatten(style) || {};
- const textColor =
- rest.color ??
- getContrastingColor(backgroundColor, white, 'rgba(0, 0, 0, .54)');
+ const { backgroundColor, ...restStyle } = StyleSheet.flatten(style) || {};
+ const { background, textColor } = resolveAvatarColors({
+ theme,
+ backgroundColor,
+ color: rest.color,
+ });
return (
-
+
);
};
diff --git a/src/components/Avatar/AvatarImage.tsx b/src/components/Avatar/AvatarImage.tsx
index 51330dd70c..17d61354ff 100644
--- a/src/components/Avatar/AvatarImage.tsx
+++ b/src/components/Avatar/AvatarImage.tsx
@@ -8,11 +8,11 @@ import type {
ViewStyle,
} from 'react-native';
+import { DEFAULT_SIZE, resolveAvatarColors } from './utils';
import { useInternalTheme } from '../../core/theming';
+import { cornerFull } from '../../theme/tokens/sys/shape';
import type { ThemeProp } from '../../types';
-const defaultSize = 64;
-
export type AvatarImageSource =
| ImageSourcePropType
| ((props: { size: number }) => React.ReactNode);
@@ -74,7 +74,7 @@ export type Props = ViewProps & {
* ```
*/
const AvatarImage = ({
- size = defaultSize,
+ size = DEFAULT_SIZE,
source,
style,
onError,
@@ -87,8 +87,9 @@ const AvatarImage = ({
testID,
...rest
}: Props) => {
- const { colors } = useInternalTheme(themeOverrides);
- const { backgroundColor = colors?.primary } = StyleSheet.flatten(style) || {};
+ const theme = useInternalTheme(themeOverrides);
+ const { backgroundColor } = StyleSheet.flatten(style) || {};
+ const { background } = resolveAvatarColors({ theme, backgroundColor });
return (
{
const theme = useInternalTheme(themeOverrides);
- const { backgroundColor = theme.colors?.primary, ...restStyle } =
- StyleSheet.flatten(style) || {};
- const textColor =
- customColor ??
- getContrastingColor(backgroundColor, white, 'rgba(0, 0, 0, .54)');
+ const { backgroundColor, ...restStyle } = StyleSheet.flatten(style) || {};
+ const { background, textColor } = resolveAvatarColors({
+ theme,
+ backgroundColor,
+ color: customColor,
+ });
const { fontScale } = useWindowDimensions();
return (
@@ -77,8 +76,8 @@ const AvatarText = ({
{
width: size,
height: size,
- borderRadius: size / 2,
- backgroundColor,
+ borderRadius: cornerFull,
+ backgroundColor: background,
},
styles.container,
restStyle,
@@ -88,6 +87,7 @@ const AvatarText = ({
{
+ const usingDefault = backgroundColor == null;
+ const background = backgroundColor ?? theme.colors.primaryContainer;
+ const textColor =
+ color ??
+ (usingDefault
+ ? theme.colors.onPrimaryContainer
+ : getContrastingColor(background, white, 'rgba(0, 0, 0, .54)'));
+ return { background, textColor };
+};
diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx
index 4f28cf0390..5c4a24623d 100644
--- a/src/components/Banner.tsx
+++ b/src/components/Banner.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { Animated, StyleSheet, View } from 'react-native';
+import { Animated, Easing, StyleSheet, View } from 'react-native';
import type { StyleProp, ViewStyle } from 'react-native';
import type { LayoutChangeEvent } from 'react-native';
@@ -14,6 +14,7 @@ import { useInternalTheme } from '../core/theming';
import type { $Omit, $RemoveChildren, Theme, ThemeProp } from '../types';
const DEFAULT_MAX_WIDTH = 960;
+const ICON_SIZE = 40;
export type Props = $Omit<$RemoveChildren, 'mode'> & {
/**
@@ -148,6 +149,7 @@ const Banner = ({
const showCallback = useLatestCallback(onShowAnimationFinished);
const hideCallback = useLatestCallback(onHideAnimationFinished);
+ const { duration, easing } = theme.motion;
const { scale } = theme.animation;
const opacity = position.interpolate({
@@ -159,20 +161,22 @@ const Banner = ({
if (visible) {
// show
Animated.timing(position, {
- duration: 250 * scale,
+ duration: duration.medium1 * scale,
toValue: 1,
useNativeDriver: false,
+ easing: Easing.bezier(...easing.standard),
}).start(showCallback);
} else {
// hide
Animated.timing(position, {
- duration: 200 * scale,
+ duration: duration.short4 * scale,
toValue: 0,
useNativeDriver: false,
+ easing: Easing.bezier(...easing.standard),
}).start(hideCallback);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [visible, position, scale]);
+ }, [visible, position, duration, easing, scale]);
const handleLayout = ({ nativeEvent }: LayoutChangeEvent) => {
const { height } = nativeEvent.layout;
@@ -221,7 +225,7 @@ const Banner = ({
{icon ? (
-
+
) : null}
)}
{
Animated.timing(spinAnim, {
toValue: sortDirection === 'ascending' ? 0 : 1,
- duration: 150,
+ duration: duration.short3 * scale,
+ easing: Easing.bezier(...easing.standard),
useNativeDriver: true,
}).start();
- }, [sortDirection, spinAnim]);
+ }, [sortDirection, spinAnim, duration, easing, scale]);
const textColor = theme.colors.onSurface;
@@ -132,6 +142,7 @@ const DataTableTitle = ({
{icon}
{
hideCallback = undefined;
});
+ it('uses zero-duration animations when animation scale is disabled', async () => {
+ const timingSpy = jest.spyOn(Animated, 'timing');
+ const view = await render(
+
+ Text
+
+ );
+
+ expect(timingSpy).toHaveBeenLastCalledWith(
+ expect.anything(),
+ expect.objectContaining({ duration: 0 })
+ );
+
+ await view.rerender(
+
+ Text
+
+ );
+
+ expect(timingSpy).toHaveBeenLastCalledWith(
+ expect.anything(),
+ expect.objectContaining({ duration: 0 })
+ );
+
+ timingSpy.mockRestore();
+ });
+
describe('when component is rendered hidden', () => {
// This behaviour is probably a bug. Needs triage before next version.
it('will fire onHideAnimationFinished on mount', async () => {
diff --git a/src/components/__tests__/DataTable.test.tsx b/src/components/__tests__/DataTable.test.tsx
index 49f863720a..de147e6520 100644
--- a/src/components/__tests__/DataTable.test.tsx
+++ b/src/components/__tests__/DataTable.test.tsx
@@ -1,4 +1,6 @@
-import { describe, expect, it } from '@jest/globals';
+import { Animated } from 'react-native';
+
+import { describe, expect, it, jest } from '@jest/globals';
import { render, screen } from '../../test-utils';
import Checkbox from '../Checkbox';
@@ -20,6 +22,26 @@ describe('DataTable.Header', () => {
});
describe('DataTable.Title', () => {
+ it('uses zero-duration sort animation when animation scale is disabled', async () => {
+ const timingSpy = jest.spyOn(Animated, 'timing');
+
+ await render(
+
+ Dessert
+
+ );
+
+ expect(timingSpy).toHaveBeenLastCalledWith(
+ expect.anything(),
+ expect.objectContaining({ duration: 0 })
+ );
+
+ timingSpy.mockRestore();
+ });
+
it('renders data table title with sort icon', async () => {
const tree = (
await render(
diff --git a/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap b/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap
index 9e55666cef..074f5ba05d 100644
--- a/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap
+++ b/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap
@@ -5,8 +5,8 @@ exports[`renders avatar with icon 1`] = `
style={
[
{
- "backgroundColor": "rgba(103, 80, 164, 1)",
- "borderRadius": 32,
+ "backgroundColor": "rgba(234, 221, 255, 1)",
+ "borderRadius": 9999,
"height": 64,
"width": 64,
},
@@ -26,7 +26,7 @@ exports[`renders avatar with icon 1`] = `
style={
[
{
- "color": "#ffffff",
+ "color": "rgba(33, 0, 93, 1)",
"fontSize": 38.4,
},
[
@@ -56,7 +56,7 @@ exports[`renders avatar with icon and custom background color 1`] = `
[
{
"backgroundColor": "#f44336",
- "borderRadius": 32,
+ "borderRadius": 9999,
"height": 64,
"width": 64,
},
@@ -105,8 +105,8 @@ exports[`renders avatar with image 1`] = `
style={
[
{
- "backgroundColor": "rgba(103, 80, 164, 1)",
- "borderRadius": 32,
+ "backgroundColor": "rgba(234, 221, 255, 1)",
+ "borderRadius": 9999,
"height": 64,
"width": 64,
},
@@ -123,7 +123,7 @@ exports[`renders avatar with image 1`] = `
}
style={
{
- "borderRadius": 32,
+ "borderRadius": 9999,
"height": 64,
"width": 64,
}
@@ -137,8 +137,8 @@ exports[`renders avatar with text 1`] = `
style={
[
{
- "backgroundColor": "rgba(103, 80, 164, 1)",
- "borderRadius": 32,
+ "backgroundColor": "rgba(234, 221, 255, 1)",
+ "borderRadius": 9999,
"height": 64,
"width": 64,
},
@@ -172,7 +172,14 @@ exports[`renders avatar with text 1`] = `
"textAlignVertical": "center",
},
{
- "color": "#ffffff",
+ "fontFamily": "System",
+ "fontSize": 16,
+ "fontWeight": "500",
+ "letterSpacing": 0.15,
+ "lineHeight": 24,
+ },
+ {
+ "color": "rgba(33, 0, 93, 1)",
"fontSize": 32,
"lineHeight": 64,
},
@@ -192,7 +199,7 @@ exports[`renders avatar with text and custom background color 1`] = `
[
{
"backgroundColor": "#f44336",
- "borderRadius": 32,
+ "borderRadius": 9999,
"height": 64,
"width": 64,
},
@@ -225,6 +232,13 @@ exports[`renders avatar with text and custom background color 1`] = `
"textAlign": "center",
"textAlignVertical": "center",
},
+ {
+ "fontFamily": "System",
+ "fontSize": 16,
+ "fontWeight": "500",
+ "letterSpacing": 0.15,
+ "lineHeight": 24,
+ },
{
"color": "#ffffff",
"fontSize": 32,
@@ -245,8 +259,8 @@ exports[`renders avatar with text and custom colors 1`] = `
style={
[
{
- "backgroundColor": "rgba(103, 80, 164, 1)",
- "borderRadius": 32,
+ "backgroundColor": "rgba(234, 221, 255, 1)",
+ "borderRadius": 9999,
"height": 64,
"width": 64,
},
@@ -279,6 +293,13 @@ exports[`renders avatar with text and custom colors 1`] = `
"textAlign": "center",
"textAlignVertical": "center",
},
+ {
+ "fontFamily": "System",
+ "fontSize": 16,
+ "fontWeight": "500",
+ "letterSpacing": 0.15,
+ "lineHeight": 24,
+ },
{
"color": "#FFFFFF",
"fontSize": 32,
@@ -299,8 +320,8 @@ exports[`renders avatar with text and custom size 1`] = `
style={
[
{
- "backgroundColor": "rgba(103, 80, 164, 1)",
- "borderRadius": 48,
+ "backgroundColor": "rgba(234, 221, 255, 1)",
+ "borderRadius": 9999,
"height": 96,
"width": 96,
},
@@ -334,7 +355,14 @@ exports[`renders avatar with text and custom size 1`] = `
"textAlignVertical": "center",
},
{
- "color": "#ffffff",
+ "fontFamily": "System",
+ "fontSize": 16,
+ "fontWeight": "500",
+ "letterSpacing": 0.15,
+ "lineHeight": 24,
+ },
+ {
+ "color": "rgba(33, 0, 93, 1)",
"fontSize": 48,
"lineHeight": 96,
},
diff --git a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap
index e9bc774f78..8685854cd8 100644
--- a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap
+++ b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap
@@ -55,14 +55,18 @@ exports[`DataTable.Cell renders data table cell 1`] = `
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
- undefined,
+ [
+ {
+ "fontFamily": "System",
+ "fontSize": 14,
+ "fontWeight": "400",
+ "letterSpacing": 0.25,
+ "lineHeight": 20,
+ },
+ undefined,
+ ],
]
}
testID="undefined-text-container"
@@ -129,14 +133,18 @@ exports[`DataTable.Cell renders right aligned data table cell 1`] = `
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
- undefined,
+ [
+ {
+ "fontFamily": "System",
+ "fontSize": 14,
+ "fontWeight": "400",
+ "letterSpacing": 0.25,
+ "lineHeight": 20,
+ },
+ undefined,
+ ],
]
}
testID="undefined-text-container"
@@ -214,28 +222,30 @@ exports[`DataTable.Header renders data table header 1`] = `
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
- "alignItems": "center",
+ "fontFamily": "System",
"fontSize": 12,
"fontWeight": "500",
- "lineHeight": 24,
- },
- {
- "maxHeight": 48,
- },
- {},
- {
- "color": "rgba(73, 69, 79, 1)",
+ "letterSpacing": 0.5,
+ "lineHeight": 16,
},
- undefined,
+ [
+ {
+ "alignItems": "center",
+ "lineHeight": 24,
+ },
+ {
+ "maxHeight": 48,
+ },
+ {},
+ {
+ "color": "rgba(73, 69, 79, 1)",
+ },
+ undefined,
+ ],
],
]
}
@@ -295,28 +305,30 @@ exports[`DataTable.Header renders data table header 1`] = `
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
- "alignItems": "center",
+ "fontFamily": "System",
"fontSize": 12,
"fontWeight": "500",
- "lineHeight": 24,
- },
- {
- "maxHeight": 48,
- },
- {},
- {
- "color": "rgba(73, 69, 79, 1)",
+ "letterSpacing": 0.5,
+ "lineHeight": 16,
},
- undefined,
+ [
+ {
+ "alignItems": "center",
+ "lineHeight": 24,
+ },
+ {
+ "maxHeight": 48,
+ },
+ {},
+ {
+ "color": "rgba(73, 69, 79, 1)",
+ },
+ undefined,
+ ],
],
]
}
@@ -353,21 +365,24 @@ exports[`DataTable.Pagination renders data table pagination 1`] = `
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
+ "fontFamily": "System",
"fontSize": 12,
- "marginRight": 16,
- },
- {
- "color": "rgba(73, 69, 79, 1)",
+ "fontWeight": "400",
+ "letterSpacing": 0.4,
+ "lineHeight": 16,
},
+ [
+ {
+ "marginRight": 16,
+ },
+ {
+ "color": "rgba(73, 69, 79, 1)",
+ },
+ ],
],
]
}
@@ -689,21 +704,24 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
+ "fontFamily": "System",
"fontSize": 12,
- "marginRight": 16,
- },
- {
- "color": "rgba(73, 69, 79, 1)",
+ "fontWeight": "400",
+ "letterSpacing": 0.4,
+ "lineHeight": 16,
},
+ [
+ {
+ "marginRight": 16,
+ },
+ {
+ "color": "rgba(73, 69, 79, 1)",
+ },
+ ],
],
]
}
@@ -1307,21 +1325,24 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = `
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
+ "fontFamily": "System",
"fontSize": 12,
- "marginRight": 16,
- },
- {
- "color": "rgba(73, 69, 79, 1)",
+ "fontWeight": "400",
+ "letterSpacing": 0.4,
+ "lineHeight": 16,
},
+ [
+ {
+ "marginRight": 16,
+ },
+ {
+ "color": "rgba(73, 69, 79, 1)",
+ },
+ ],
],
]
}
@@ -1656,21 +1677,24 @@ exports[`DataTable.Pagination renders data table pagination with options select
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
+ "fontFamily": "System",
"fontSize": 12,
- "marginRight": 16,
- },
- {
- "color": "rgba(73, 69, 79, 1)",
+ "fontWeight": "400",
+ "letterSpacing": 0.4,
+ "lineHeight": 16,
},
+ [
+ {
+ "marginRight": 16,
+ },
+ {
+ "color": "rgba(73, 69, 79, 1)",
+ },
+ ],
],
]
}
@@ -1893,21 +1917,24 @@ exports[`DataTable.Pagination renders data table pagination with options select
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
+ "fontFamily": "System",
"fontSize": 12,
- "marginRight": 16,
- },
- {
- "color": "rgba(73, 69, 79, 1)",
+ "fontWeight": "400",
+ "letterSpacing": 0.4,
+ "lineHeight": 16,
},
+ [
+ {
+ "marginRight": 16,
+ },
+ {
+ "color": "rgba(73, 69, 79, 1)",
+ },
+ ],
],
]
}
@@ -2582,28 +2609,30 @@ exports[`DataTable.Title renders data table title with press handler 1`] = `
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
- "alignItems": "center",
+ "fontFamily": "System",
"fontSize": 12,
"fontWeight": "500",
- "lineHeight": 24,
- },
- {
- "maxHeight": 48,
+ "letterSpacing": 0.5,
+ "lineHeight": 16,
},
- {},
- {
- "marginLeft": 8,
- },
- undefined,
+ [
+ {
+ "alignItems": "center",
+ "lineHeight": 24,
+ },
+ {
+ "maxHeight": 48,
+ },
+ {},
+ {
+ "marginLeft": 8,
+ },
+ undefined,
+ ],
],
]
}
@@ -2710,28 +2739,30 @@ exports[`DataTable.Title renders data table title with sort icon 1`] = `
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
- "alignItems": "center",
+ "fontFamily": "System",
"fontSize": 12,
"fontWeight": "500",
- "lineHeight": 24,
+ "letterSpacing": 0.5,
+ "lineHeight": 16,
},
- {
- "maxHeight": 48,
- },
- {},
- {
- "marginLeft": 8,
- },
- undefined,
+ [
+ {
+ "alignItems": "center",
+ "lineHeight": 24,
+ },
+ {
+ "maxHeight": 48,
+ },
+ {},
+ {
+ "marginLeft": 8,
+ },
+ undefined,
+ ],
],
]
}
@@ -2796,28 +2827,30 @@ exports[`DataTable.Title renders right aligned data table title 1`] = `
},
{
"color": "rgba(29, 27, 32, 1)",
- "fontFamily": "System",
- "fontWeight": "400",
- "letterSpacing": 0,
- },
- {
"writingDirection": "ltr",
},
[
{
- "alignItems": "center",
+ "fontFamily": "System",
"fontSize": 12,
"fontWeight": "500",
- "lineHeight": 24,
+ "letterSpacing": 0.5,
+ "lineHeight": 16,
},
- {
- "maxHeight": 48,
- },
- {},
- {
- "color": "rgba(73, 69, 79, 1)",
- },
- undefined,
+ [
+ {
+ "alignItems": "center",
+ "lineHeight": 24,
+ },
+ {
+ "maxHeight": 48,
+ },
+ {},
+ {
+ "color": "rgba(73, 69, 79, 1)",
+ },
+ undefined,
+ ],
],
]
}