diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index fe6ee80702..709abe6aaf 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -2,4 +2,4 @@
*Enter description to help the reviewer understand what's the change about...*
## Changelog
-*Add a quick message for our users about this change (include Compoennt name, relevant props and general purpose of the PR)*
+*Add a quick message for our users about this change (include Component name, relevant props and general purpose of the PR)*
diff --git a/.npmignore b/.npmignore
index 3934605551..3fc18293fb 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,10 +1,12 @@
example/
docs/
+docuilib/
+eslint-rules/
extensions/
node_modules/
.npmignore
.watchmanconfig
-
+src/**/__tests__/*.*
#################
# from .gitignore:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 31e383ef15..ec864c779a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,7 +8,7 @@ When contributing to this repository, please first discuss the change you wish t
## Pull Request Process
1. First make sure the environment is working and synced with master. For installation details go [here](https://github.com/wix/react-native-ui-lib/blob/master/markdowns/getting-started/setup.md#demo-app)
-2. Before submitting a PR we suggest running `npm run prepush` command that verifies our lint, TS and tests were not broken.
+2. Before submitting a PR we suggest running `npm run pre-push` command that verifies our lint, TS and tests were not broken.
3. Please use our PR prefixes accordingly. `fix` for bugfix, `feat` for feature, `infra` for infrastructure changes and `docs` for documentation related changes (e.g `fix/ButtonStyle`, `feat/Avatar_colorProp`)
4. Please don't change our PR template.
- In the Description section add everything that can help the reviewer and the reviewing process, like a description of the issue and the way you decided to go about it, screenshots or gifs, etc.
diff --git a/demo/src/demoApp.js b/demo/src/demoApp.js
index c0fa880e8e..048952fed8 100644
--- a/demo/src/demoApp.js
+++ b/demo/src/demoApp.js
@@ -128,3 +128,30 @@ Navigation.events().registerAppLaunchedListener(() => {
registerScreens(Navigation.registerComponent.bind(Navigation));
getDefaultScreenAndStartApp();
});
+
+
+/* Setting Intl Polyfills
+This is due to lack of Intl support in Hermes engine
+ */
+
+if (global.HermesInternal) {
+ if (Constants.isIOS) {
+
+ // Polyfills required to use Intl with Hermes engine
+ require('@formatjs/intl-getcanonicallocales/polyfill').default;
+ require('@formatjs/intl-locale/polyfill').default;
+ require('@formatjs/intl-pluralrules/polyfill').default;
+ require('@formatjs/intl-pluralrules/locale-data/en').default;
+ require('@formatjs/intl-numberformat/polyfill').default;
+ require('@formatjs/intl-numberformat/locale-data/en').default;
+ require('@formatjs/intl-datetimeformat/polyfill').default;
+ require('@formatjs/intl-datetimeformat/locale-data/en').default;
+ require('@formatjs/intl-datetimeformat/add-all-tz').default;
+ } else {
+ require('@formatjs/intl-getcanonicallocales/polyfill');
+ require('@formatjs/intl-locale/polyfill');
+ require('@formatjs/intl-datetimeformat/polyfill');
+ require('@formatjs/intl-datetimeformat/locale-data/en');
+ require('@formatjs/intl-datetimeformat/add-all-tz');
+ }
+}
diff --git a/demo/src/screens/MainScreen.js b/demo/src/screens/MainScreen.js
index 7127f45f36..4c4e33c24c 100644
--- a/demo/src/screens/MainScreen.js
+++ b/demo/src/screens/MainScreen.js
@@ -2,7 +2,8 @@ import _ from 'lodash';
import React, {Component} from 'react';
import AsyncStorage from '@react-native-community/async-storage';
import PropTypes from 'prop-types';
-import {StyleSheet, FlatList, ViewPropTypes} from 'react-native';
+import {StyleSheet, FlatList} from 'react-native';
+import {ViewPropTypes} from 'deprecated-react-native-prop-types';
import {Navigation} from 'react-native-navigation';
import {gestureHandlerRootHOC} from 'react-native-gesture-handler';
import {
diff --git a/demo/src/screens/__tests__/AvatarScreen.spec.js b/demo/src/screens/__tests__/AvatarScreen.spec.js
new file mode 100644
index 0000000000..a4d3be50bb
--- /dev/null
+++ b/demo/src/screens/__tests__/AvatarScreen.spec.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import renderer from 'react-test-renderer';
+
+describe('AvatarScreen', () => {
+ let AvatarScreen;
+
+ beforeEach(() => {
+ AvatarScreen = require('../componentScreens/AvatarsScreen').default;
+ });
+
+ it('renders screen', () => {
+ const tree = renderer.create().toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/demo/src/screens/__tests__/__snapshots__/AvatarScreen.spec.js.snap b/demo/src/screens/__tests__/__snapshots__/AvatarScreen.spec.js.snap
new file mode 100644
index 0000000000..3cd68781d4
--- /dev/null
+++ b/demo/src/screens/__tests__/__snapshots__/AvatarScreen.spec.js.snap
@@ -0,0 +1,2756 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`AvatarScreen renders screen 1`] = `
+
+
+
+
+ Custom Background
+
+
+
+
+
+
+
+ Empty Avatar with ribbon
+
+
+
+
+
+ New
+
+
+
+
+
+
+ Initials with Color
+
+
+
+
+ AD
+
+
+
+
+ New
+
+
+
+
+
+
+ Initials, badge ("online")
+
+
+
+
+ ES
+
+
+
+
+
+
+
+
+
+ Image, badge ("away")
+
+
+
+
+
+
+
+
+
+
+
+ Smaller size, Badge ("offline")
+
+
+
+
+
+
+
+
+
+
+
+ Image with fade in animation
+
+
+
+
+
+
+
+
+
+
+ Big pimple
+
+
+
+
+
+
+
+
+
+
+
+ Icon badge
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GIF
+
+
+
+
+
+
+
+
+ Invalid Gravatar (see logs)
+
+
+
+
+ 🤦
+
+
+
+
+
+
+
+ Monitored Avatar (see logs)
+
+
+
+
+ ?!
+
+
+
+
+
+
+
+ Empty Gravatar
+
+
+
+
+
+
+
+
+ With custom badge label
+
+
+
+
+ LD
+
+
+
+
+
+ +2
+
+
+
+
+
+
+
+`;
diff --git a/demo/src/screens/componentScreens/CheckboxScreen.tsx b/demo/src/screens/componentScreens/CheckboxScreen.tsx
index 4ff8c9ee06..b07a9891a8 100644
--- a/demo/src/screens/componentScreens/CheckboxScreen.tsx
+++ b/demo/src/screens/componentScreens/CheckboxScreen.tsx
@@ -14,16 +14,34 @@ export default class CheckboxScreen extends Component {
render() {
return (
-
-
+
+
Checkbox
-
- this.setState({value1})}
- style={styles.checkbox}
- />
+
+ Customizable UI
+
+ this.setState({value1})}/>
+ this.setState({value2})}
+ borderRadius={2}
+ size={30}
+ color={Colors.purple30}
+ selectedIcon={Assets.icons.x}
+ marginL-s5
+ />
+
+ this.setState({value3})}
+ borderRadius={5}
+ size={18}
+ color={Colors.grey10}
+ iconColor={Colors.green10}
+ marginL-s5
+ />
+
this.setState({value6})}
containerStyle={styles.checkbox}
/>
- this.setState({value2})}
- borderRadius={2}
- size={30}
- color={Colors.purple30}
- selectedIcon={Assets.icons.x}
- style={styles.checkbox}
- />
- this.setState({value3})}
- borderRadius={5}
- size={18}
- color={Colors.grey10}
- iconColor={Colors.green10}
- style={styles.checkbox}
- />
-
+
+
- Disabled:
+ Disabled States
{
{_.times(3, this.renderItem)}
{!this.props.scrollReachedProps.isScrollAtEnd && (
-
+ // @ts-expect-error
+
)}
diff --git a/jest-setup.js b/jest-setup.js
index f2635985c9..7066218e2a 100644
--- a/jest-setup.js
+++ b/jest-setup.js
@@ -10,6 +10,9 @@ jest.mock('@react-native-community/netinfo', () => {});
jest.mock('react-native-reanimated', () => {
const reactNativeReanimated = require('react-native-reanimated/mock');
reactNativeReanimated.interpolateColor = jest.fn(v => v); // TODO: See this https://github.com/software-mansion/react-native-reanimated/issues/2749
+ reactNativeReanimated.FadeIn = {
+ duration: jest.fn()
+ };
return reactNativeReanimated;
});
global.__reanimatedWorkletInit = jest.fn();
diff --git a/lib/components/WheelPicker/index.tsx b/lib/components/WheelPicker/index.tsx
index 430af68c34..75fe62ea76 100644
--- a/lib/components/WheelPicker/index.tsx
+++ b/lib/components/WheelPicker/index.tsx
@@ -10,14 +10,6 @@ import {Typography, Colors} from '../../../src/style';
import {PickerPackage, CommunityPickerPackage} from '../../../src/optionalDependencies';
const Picker = PickerPackage?.Picker || CommunityPickerPackage?.Picker || (() => null);
-if (!PickerPackage) {
- if (CommunityPickerPackage) {
- console.warn(`RNUILib Picker will soon migrate to use "@react-native-picker/picker" package instead of '@react-native-community/picker'`);
- } else {
- console.error(`RNUILib Picker requires installing "@react-native-picker/picker" dependency`);
- }
-}
-
const WheelPickerNative = requireNativeComponent('WheelPicker');
export type WheelPickerProps = {
diff --git a/package.json b/package.json
index 5c9bf837d5..267f63b51c 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"babel-plugin-transform-inline-environment-variables": "^0.0.2",
"color": "^3.1.0",
"commons-validator-js": "^1.0.237",
+ "deprecated-react-native-prop-types": "^2.3.0",
"hoist-non-react-statics": "^3.0.0",
"lodash": "^4.17.21",
"memoize-one": "^5.0.5",
@@ -57,6 +58,11 @@
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.10.1",
"@babel/runtime": "^7.12.5",
+ "@formatjs/intl-datetimeformat": "^6.0.3",
+ "@formatjs/intl-getcanonicallocales": "^2.0.2",
+ "@formatjs/intl-locale": "^3.0.3",
+ "@formatjs/intl-numberformat": "^8.0.4",
+ "@formatjs/intl-pluralrules": "^5.0.3",
"@react-native-community/async-storage": "^1.6.2",
"@react-native-community/blur": "^3.4.1",
"@react-native-community/datetimepicker": "^3.4.6",
@@ -129,7 +135,9 @@
"setupFiles": [
"./jest-setup.js"
],
- "testMatch": ["**/*.spec.(js|tsx)"],
+ "testMatch": [
+ "**/*.spec.(js|tsx)"
+ ],
"moduleNameMapper": {
"^react-native-reanimated$": "/node_modules/react-native-reanimated/src/Animated.js"
}
diff --git a/src/components/animatedImage/index.js b/src/components/animatedImage/index.js
deleted file mode 100644
index 34146d1ecc..0000000000
--- a/src/components/animatedImage/index.js
+++ /dev/null
@@ -1,73 +0,0 @@
-// TODO: consider unify this component functionality with our Image component
-import _ from 'lodash';
-import PropTypes from 'prop-types';
-import React from 'react';
-import {Animated, StyleSheet} from 'react-native';
-import View from '../../components/view';
-import Image from '../../components/image';
-import {BaseComponent} from '../../commons';
-
-/**
- * @description: Image component that fades-in the image with animation once it's loaded
- * @extends: Animated.Image
- * @gif: https://media.giphy.com/media/l0HU7jj0ivEFyZIA0/giphy.gif
- * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/AnimatedImageScreen.js
- */
-class AnimatedImage extends BaseComponent {
- static displayName = 'AnimatedImage';
- static propTypes = {
- /**
- * Image prop Types
- */
- ...Image.propTypes,
- /**
- * Additional spacing styles for the container
- */
- containerStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number]),
- /**
- * Duration for the fade animation when the image is loaded
- */
- animationDuration: PropTypes.number,
- /**
- * A component to render while the image is loading
- */
- loader: PropTypes.element
- };
-
- static defaultProps = {
- animationDuration: 300
- };
-
- constructor(props) {
- super(props);
- this.state = {opacity: new Animated.Value(0), isLoading: true};
- }
-
- onLoad = (...args) => {
- this.setState({isLoading: false}, () => {
- const animationParams = {toValue: 1, duration: this.props.animationDuration, useNativeDriver: true};
- Animated.timing(this.state.opacity, animationParams).start();
- });
- _.invoke(this.props, 'onLoad', ...args);
- };
-
- render() {
- const {containerStyle, source, loader, style, testID, ...others} = this.props;
- return (
-
-
- {this.state.isLoading && loader && (
- {loader}
- )}
-
- );
- }
-}
-
-export default AnimatedImage;
diff --git a/src/components/animatedImage/index.tsx b/src/components/animatedImage/index.tsx
new file mode 100644
index 0000000000..c2f2b93db7
--- /dev/null
+++ b/src/components/animatedImage/index.tsx
@@ -0,0 +1,74 @@
+// TODO: consider unify this component functionality with our Image component
+import React, {useState, useCallback} from 'react';
+import {StyleSheet, StyleProp, ViewStyle, NativeSyntheticEvent, ImageLoadEventData} from 'react-native';
+import Animated, {FadeIn} from 'react-native-reanimated';
+import View from '../../components/view';
+import Image, {ImageProps} from '../../components/image';
+
+const UIAnimatedImage = Animated.createAnimatedComponent(Image);
+
+export interface AnimatedImageProps extends ImageProps {
+ /**
+ * Additional spacing styles for the container
+ */
+ containerStyle?: StyleProp;
+ /**
+ * Duration for the fade animation when the image is loaded
+ */
+ animationDuration?: number;
+ /**
+ * A component to render while the image is loading
+ */
+ loader?: React.ReactElement;
+}
+
+/**
+ * @description: Image component that fades-in the image with animation once it's loaded
+ * @extends: Animated.Image
+ * @gif: https://media.giphy.com/media/l0HU7jj0ivEFyZIA0/giphy.gif
+ * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/AnimatedImageScreen.js
+ */
+const AnimatedImage = (props: AnimatedImageProps) => {
+ const {
+ containerStyle,
+ source,
+ loader,
+ style,
+ onLoad: propsOnLoad,
+ animationDuration = 300,
+ testID,
+ ...others
+ } = props;
+ const [isLoading, setIsLoading] = useState(true);
+
+ const onLoad = useCallback((event: NativeSyntheticEvent) => {
+ setIsLoading(false);
+ propsOnLoad?.(event);
+ },
+ [setIsLoading, propsOnLoad]);
+
+ return (
+
+
+ {isLoading && loader && {loader}}
+
+ );
+};
+
+AnimatedImage.displayName = 'AnimatedImage';
+
+export default AnimatedImage;
+
+const styles = StyleSheet.create({
+ loader: {
+ ...StyleSheet.absoluteFillObject,
+ justifyContent: 'center'
+ }
+});
diff --git a/src/components/animatedScanner/index.js b/src/components/animatedScanner/index.js
index a5a7c10180..a6fe7116b2 100644
--- a/src/components/animatedScanner/index.js
+++ b/src/components/animatedScanner/index.js
@@ -1,7 +1,8 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
-import {ViewPropTypes, StyleSheet, Animated} from 'react-native';
+import {StyleSheet, Animated} from 'react-native';
+import {ViewPropTypes} from 'deprecated-react-native-prop-types';
import {Colors} from '../../style';
import {BaseComponent} from '../../commons';
import View from '../../components/view';
diff --git a/src/components/avatar/__tests__/index.spec.js b/src/components/avatar/__tests__/index.spec.js
index ffc1226253..70c87dc3e1 100644
--- a/src/components/avatar/__tests__/index.spec.js
+++ b/src/components/avatar/__tests__/index.spec.js
@@ -1,16 +1,25 @@
+import React from 'react';
+import {StyleSheet} from 'react-native';
+import {render} from '@testing-library/react-native';
import {Avatar} from '../index';
+function verifyBadgeSize(renderTree, expectedSize) {
+ const badge = renderTree.getByTestId('avatar.onlineBadge');
+ const style = StyleSheet.flatten(badge.props.style);
+ expect(style.width).toEqual(expectedSize);
+ expect(style.height).toEqual(expectedSize);
+}
describe('Avatar Badge', () => {
describe('badgeProps.size supports number', () => {
it('should return 99 as the size number given', () => {
- const uut = new Avatar({badgeProps: {size: 99}});
- expect(uut.getBadgeSize()).toEqual(99);
+ const renderTree = render();
+ verifyBadgeSize(renderTree, 99);
});
it('should return default when passing 0 as size', () => {
- const uut = new Avatar({badgeProps: {size: 0}});
- expect(uut.getBadgeSize()).toEqual(10);
+ const renderTree = render();
+ verifyBadgeSize(renderTree, 10);
});
});
});
diff --git a/src/components/avatar/index.tsx b/src/components/avatar/index.tsx
index d539df86a7..03997c890b 100644
--- a/src/components/avatar/index.tsx
+++ b/src/components/avatar/index.tsx
@@ -1,5 +1,5 @@
import _ from 'lodash';
-import React, {PropsWithChildren, PureComponent} from 'react';
+import React, {PropsWithChildren, useEffect, useMemo, forwardRef} from 'react';
import {
StyleSheet,
ImageSourcePropType,
@@ -11,10 +11,8 @@ import {
TextStyle,
AccessibilityProps
} from 'react-native';
-import memoize from 'memoize-one';
import {LogService} from '../../services';
import {Colors, BorderRadiuses} from '../../style';
-import {forwardRef, asBaseComponent} from '../../commons/new';
import {extractAccessibilityProps} from '../../commons/modifiers';
import Badge, {BadgeProps} from '../badge';
import View from '../view';
@@ -23,6 +21,7 @@ import Image, {ImageProps} from '../image';
// @ts-ignore
import AnimatedImage from '../animatedImage';
import * as AvatarHelper from '../../helpers/AvatarHelper';
+import {useThemeProps} from '../../hooks';
export enum BadgePosition {
TOP_RIGHT = 'TOP_RIGHT',
@@ -155,43 +154,59 @@ export type AvatarProps = Pick &
testID?: string;
}>;
+interface Statics {
+ badgePosition: typeof BadgePosition;
+}
+
/**
* @description: Avatar component for displaying user profile images
* @extends: TouchableOpacity, Image
* @image: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Avatar/Avarat_1.png?raw=true, https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Avatar/Avarat_2.png?raw=true
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/AvatarsScreen.tsx
*/
-class Avatar extends PureComponent {
- static displayName = 'Avatar';
-
- styles: ReturnType;
-
- constructor(props: AvatarProps) {
- super(props);
-
- this.styles = createStyles(props);
-
- if (props.imageSource) {
+const Avatar = forwardRef((props: AvatarProps, ref: React.ForwardedRef) => {
+ const themeProps = useThemeProps(props, 'Avatar');
+ const {
+ imageSource,
+ source,
+ size = 50,
+ labelColor = Colors.$textDefault,
+ badgeProps = {},
+ badgePosition = BadgePosition.TOP_RIGHT,
+ testID,
+ ribbonLabel,
+ customRibbon,
+ ribbonStyle,
+ ribbonLabelStyle,
+ animate = false,
+ imageStyle,
+ onImageLoadStart,
+ onImageLoadEnd,
+ onImageLoadError,
+ imageProps,
+ label,
+ name,
+ backgroundColor,
+ useAutoColors,
+ autoColorsConfig,
+ containerStyle,
+ onPress,
+ children
+ } = themeProps;
+ const {size: _badgeSize, borderWidth: badgeBorderWidth = 0} = badgeProps;
+ const badgeSize = _badgeSize || DEFAULT_BADGE_SIZE;
+
+ useEffect(() => {
+ if (imageSource) {
LogService.warn('uilib: imageSource prop is deprecated, use source instead.');
}
- }
+ }, [imageSource]);
- static defaultProps = {
- animate: false,
- size: 50,
- labelColor: Colors.$textDefault,
- badgePosition: BadgePosition.TOP_RIGHT
- };
-
- static badgePosition = BadgePosition;
-
- get source() {
- return this.props.source || this.props.imageSource;
- }
-
- getContainerStyle(): StyleProp {
- const {size} = this.props;
+ const _source = useMemo(() => {
+ return source || imageSource;
+ }, [source, imageSource]);
+ const _baseContainerStyle: StyleProp = useMemo(() => {
return {
width: size,
height: size,
@@ -199,132 +214,48 @@ class Avatar extends PureComponent {
justifyContent: 'center',
borderRadius: BorderRadiuses.br100
};
- }
+ }, [size]);
- getInitialsContainer(): StyleProp {
+ const initialsStyle = useMemo(() => {
return {
- ...StyleSheet.absoluteFillObject,
- alignItems: 'center',
- justifyContent: 'center',
- borderRadius: BorderRadiuses.br100
+ color: labelColor,
+ backgroundColor: 'transparent',
+ lineHeight: undefined
};
- }
-
- getRibbonStyle(): StyleProp {
- const {size} = this.props;
+ }, [labelColor]);
+ const _baseRibbonStyle: StyleProp = useMemo(() => {
return {
position: 'absolute',
top: '10%',
left: size / 1.7,
borderRadius: size / 2
};
- }
-
- getBadgeBorderWidth = () => _.get(this.props, 'badgeProps.borderWidth', 0);
-
- getBadgeColor() {
- return _.get(this.props, 'badgeProps.backgroundColor');
- }
+ }, [size]);
- getBadgeSize = () => {
- return this.props?.badgeProps?.size || DEFAULT_BADGE_SIZE;
- };
+ const _ribbonStyle: StyleProp = useMemo(() => {
+ return [_baseRibbonStyle, styles.ribbon, ribbonStyle];
+ }, [_baseRibbonStyle, ribbonStyle]);
- getBadgePosition = (): object => {
- const {size, badgePosition} = this.props;
+ const _badgePosition: StyleProp = useMemo(() => {
const radius = size / 2;
const x = Math.sqrt(radius ** 2 * 2);
const y = x - radius;
- const shift = Math.sqrt(y ** 2 / 2) - (this.getBadgeSize() + this.getBadgeBorderWidth() * 2) / 2;
+ const shift = Math.sqrt(y ** 2 / 2) - (badgeSize + badgeBorderWidth * 2) / 2;
const badgeLocation = _.split(_.toLower(badgePosition), '_', 2);
- const badgeAlignment = {position: 'absolute', [badgeLocation[0]]: shift, [badgeLocation[1]]: shift};
+ return {position: 'absolute', [badgeLocation[0]]: shift, [badgeLocation[1]]: shift};
+ }, [size, badgeBorderWidth, badgeSize, badgePosition]);
- return badgeAlignment;
- };
-
- renderBadge() {
- const {testID, badgeProps} = this.props;
-
- if (badgeProps || this.getBadgeColor()) {
- return (
-
- );
- }
- }
-
- renderRibbon() {
- const {ribbonLabel, ribbonStyle, ribbonLabelStyle, customRibbon} = this.props;
- if (ribbonLabel) {
- return customRibbon ? (
- {customRibbon}
- ) : (
-
-
- {ribbonLabel}
-
-
- );
- }
- }
-
- renderImage() {
- const {
- animate,
- // @ts-ignore
- onImageLoadStart,
- onImageLoadEnd,
- onImageLoadError,
- testID,
- imageProps,
- imageStyle
- } = this.props;
- const hasImage = !_.isUndefined(this.source);
- const ImageContainer = animate ? AnimatedImage : Image;
-
- if (hasImage) {
- return (
-
- );
- }
- }
-
- getText = memoize((label, name) => {
+ const text = useMemo(() => {
let text = label;
if (_.isNil(label) && !_.isNil(name)) {
text = AvatarHelper.getInitials(name);
}
return text;
- });
+ }, [label, name]);
- get text() {
- const {label, name} = this.props;
- return this.getText(label, name);
- }
-
- getBackgroundColor = memoize((text, avatarColors, hashFunction, defaultColor) => {
- return AvatarHelper.getBackgroundColor(text, avatarColors, hashFunction, defaultColor);
- });
-
- get backgroundColor() {
- const {backgroundColor, useAutoColors, autoColorsConfig, name} = this.props;
+ const _backgroundColor = useMemo(() => {
if (backgroundColor) {
return backgroundColor;
}
@@ -335,87 +266,136 @@ class Avatar extends PureComponent {
defaultColor = Colors.$backgroundNeutralLight
} = autoColorsConfig || {};
if (useAutoColors) {
- return this.getBackgroundColor(name, avatarColors, hashFunction, defaultColor);
+ return AvatarHelper.getBackgroundColor(name, avatarColors, hashFunction, defaultColor);
} else {
return defaultColor;
}
- }
+ }, [backgroundColor, autoColorsConfig, useAutoColors, name]);
- render() {
- const {
- labelColor: color,
- onPress,
- containerStyle,
- children,
- size,
- testID,
- //@ts-ignore
- forwardedRef
- } = this.props;
- const Container = onPress ? TouchableOpacity : View;
- const hasImage = !_.isUndefined(this.source);
+ const _containerStyle: StyleProp = useMemo(() => {
+ return [_baseContainerStyle, containerStyle];
+ }, [_baseContainerStyle, containerStyle]);
+
+ const textStyle = useMemo(() => {
const fontSizeToImageSizeRatio = 0.32;
const fontSize = size * fontSizeToImageSizeRatio;
- const text = this.text;
-
- return (
-
-
- {!_.isUndefined(text) && (
-
- {text}
-
- )}
+ return [{fontSize}, initialsStyle, {color: labelColor}];
+ }, [size, initialsStyle, labelColor]);
+
+ const textContainerStyle = useMemo(() => {
+ const hasImage = !_.isUndefined(_source);
+ return [
+ styles.initialsContainer,
+ {backgroundColor: _backgroundColor},
+ hasImage && styles.initialsContainerWithInset
+ ];
+ }, [_source, _backgroundColor]);
+
+ const accessibilityProps = useMemo(() => {
+ return extractAccessibilityProps(props);
+ }, [props]);
+
+ const _imageStyle = useMemo(() => {
+ return [_baseContainerStyle, StyleSheet.absoluteFillObject, imageStyle];
+ }, [_baseContainerStyle, imageStyle]);
+
+ const renderImage = () => {
+ const hasImage = !_.isUndefined(_source);
+
+ if (hasImage) {
+ const ImageContainer = animate ? AnimatedImage : Image;
+ return (
+
+ );
+ }
+ };
+
+ const renderBadge = () => {
+ if (!_.isEmpty(badgeProps)) {
+ return (
+
+ );
+ }
+ };
+
+ const renderRibbon = () => {
+ if (ribbonLabel) {
+ return (
+
+
+ {ribbonLabel}
+
- {this.renderImage()}
- {this.renderBadge()}
- {this.renderRibbon()}
- {children}
-
- );
- }
-}
+ );
+ }
+ };
-function createStyles(props: AvatarProps) {
- const {labelColor} = props;
- const styles = StyleSheet.create({
- initialsContainerWithInset: {
- top: 1,
- right: 1,
- bottom: 1,
- left: 1
- },
- initials: {
- color: labelColor,
- backgroundColor: 'transparent',
- lineHeight: undefined
- },
- ribbon: {
- backgroundColor: Colors.$backgroundPrimaryHeavy,
- paddingHorizontal: 6,
- paddingVertical: 3
+ const renderCustomRibbon = () => {
+ if (customRibbon) {
+ return {customRibbon};
}
- });
+ };
- return styles;
-}
+ const Container = onPress ? TouchableOpacity : View;
+
+ return (
+
+
+ {!_.isUndefined(text) && (
+
+ {text}
+
+ )}
+
+ {renderImage()}
+ {renderBadge()}
+ {renderCustomRibbon()}
+ {renderRibbon()}
+ {children}
+
+ );
+});
+
+const styles = StyleSheet.create({
+ initialsContainer: {
+ ...StyleSheet.absoluteFillObject,
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: BorderRadiuses.br100
+ },
+ initialsContainerWithInset: {
+ top: 1,
+ right: 1,
+ bottom: 1,
+ left: 1
+ },
+ ribbon: {
+ backgroundColor: Colors.$backgroundPrimaryHeavy,
+ paddingHorizontal: 6,
+ paddingVertical: 3
+ }
+});
+// @ts-expect-error
+Avatar.badgePosition = BadgePosition;
export {Avatar}; // For tests
-export default asBaseComponent(forwardRef(Avatar));
+export default Avatar as typeof Avatar & Statics;
diff --git a/src/components/baseInput/index.tsx b/src/components/baseInput/index.tsx
index 5943bb566b..166a1e0bbe 100644
--- a/src/components/baseInput/index.tsx
+++ b/src/components/baseInput/index.tsx
@@ -2,7 +2,7 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import 'react';
-import {ViewPropTypes, TextInput as RNTextInput} from 'react-native';
+import {ViewPropTypes, TextInputPropTypes} from 'deprecated-react-native-prop-types';
import {Colors, Typography} from '../../style';
import {BaseComponent} from '../../commons';
import Validators from './Validators';
@@ -19,7 +19,7 @@ export default class BaseInput extends BaseComponent {
static displayName = 'BaseInput';
static propTypes = {
- ...RNTextInput.propTypes,
+ ...TextInputPropTypes,
// ...BaseComponent.propTypes,
/**
* text color
diff --git a/src/components/button/__tests__/__snapshots__/index.spec.js.snap b/src/components/button/__tests__/__snapshots__/index.spec.js.snap
index f001c88ec9..380301ef0d 100644
--- a/src/components/button/__tests__/__snapshots__/index.spec.js.snap
+++ b/src/components/button/__tests__/__snapshots__/index.spec.js.snap
@@ -58,8 +58,8 @@ exports[`Button backgroundColor should return backgroundColor according to modif
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -129,8 +129,8 @@ exports[`Button backgroundColor should return backgroundColor according to prop
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -200,8 +200,8 @@ exports[`Button backgroundColor should return defined theme backgroundColor 1`]
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -276,8 +276,8 @@ exports[`Button backgroundColor should return theme disabled color if button is
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -347,8 +347,8 @@ exports[`Button backgroundColor should return undefined if this button is link 1
Object {
"color": "#5A48F5",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -420,8 +420,8 @@ exports[`Button backgroundColor should return undefined if this button is outlin
Object {
"color": "#5A48F5",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -491,8 +491,8 @@ exports[`Button border radius should return 0 border radius when border radius p
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -562,8 +562,8 @@ exports[`Button border radius should return 0 border radius when button is full
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -633,8 +633,8 @@ exports[`Button border radius should return given border radius when use plain n
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -710,7 +710,7 @@ exports[`Button container size should avoid minWidth limitation if avoidMinWidth
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -786,7 +786,7 @@ exports[`Button container size should have no padding if avoidInnerPadding prop
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -862,7 +862,7 @@ exports[`Button container size should have no padding of button is a link nor mi
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -965,7 +965,7 @@ exports[`Button container size should have no padding of button is an icon butto
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -1037,8 +1037,8 @@ exports[`Button container size should reduce padding by outlineWidth in case of
Object {
"color": "#5A48F5",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -1108,8 +1108,8 @@ exports[`Button container size should return style for large button 1`] = `
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -1181,8 +1181,8 @@ exports[`Button container size should return style for large button 2`] = `
Object {
"color": "#5A48F5",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -1258,7 +1258,7 @@ exports[`Button container size should return style for medium button 1`] = `
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -1336,7 +1336,7 @@ exports[`Button container size should return style for medium button 2`] = `
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -1406,8 +1406,8 @@ exports[`Button container size should return style for round button 1`] = `
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -1483,7 +1483,7 @@ exports[`Button container size should return style for small button 1`] = `
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -1561,7 +1561,7 @@ exports[`Button container size should return style for small button 2`] = `
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -1637,7 +1637,7 @@ exports[`Button container size should return style for xSmall button 1`] = `
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -1715,7 +1715,7 @@ exports[`Button container size should return style for xSmall button 2`] = `
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -1787,8 +1787,8 @@ exports[`Button hyperlink should render button as a hyperlink 1`] = `
Object {
"color": "#5A48F5",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -2210,8 +2210,8 @@ exports[`Button icon should return the right spacing according to button size wh
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -2287,7 +2287,7 @@ exports[`Button icon should return the right spacing according to button size wh
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -2363,7 +2363,7 @@ exports[`Button icon should return the right spacing according to button size wh
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -2439,7 +2439,7 @@ exports[`Button icon should return the right spacing according to button size wh
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -2509,8 +2509,8 @@ exports[`Button label size should return style for large button 1`] = `
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -2586,7 +2586,7 @@ exports[`Button label size should return style for medium button 1`] = `
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -2662,7 +2662,7 @@ exports[`Button label size should return style for small button 1`] = `
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -2738,7 +2738,7 @@ exports[`Button label size should return style for xSmall button 1`] = `
"fontWeight": "400",
"lineHeight": 20,
},
- Object {},
+ undefined,
undefined,
],
]
@@ -2808,8 +2808,8 @@ exports[`Button labelColor should return Theme linkColor color for link 1`] = `
Object {
"color": "#FFD54E",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -2879,8 +2879,8 @@ exports[`Button labelColor should return color according to color modifier 1`] =
Object {
"color": "#D52712",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -2950,8 +2950,8 @@ exports[`Button labelColor should return color according to color prop 1`] = `
Object {
"color": "green",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3026,8 +3026,8 @@ exports[`Button labelColor should return disabled text color according to theme
Object {
"color": "#D2D6D8",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3097,8 +3097,8 @@ exports[`Button labelColor should return linkColor color for link 1`] = `
Object {
"color": "#FDB893",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3225,8 +3225,8 @@ exports[`Button link should render button as a link 1`] = `
Object {
"color": "#5A48F5",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3298,8 +3298,8 @@ exports[`Button outline should render button with an outline 1`] = `
Object {
"color": "#5A48F5",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3371,8 +3371,8 @@ exports[`Button outline should render button with an outlineColor 1`] = `
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3444,8 +3444,8 @@ exports[`Button outline should render button with outline and outlineColor 1`] =
Object {
"color": "blue",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3517,8 +3517,8 @@ exports[`Button outline should return custom borderWidth according to outlineWid
Object {
"color": "#5A48F5",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3595,8 +3595,8 @@ exports[`Button outline should return disabled color for outline if button is di
Object {
"color": "#D2D6D8",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3666,8 +3666,8 @@ exports[`Button outline should return undefined when link is true, even when out
Object {
"color": "#5A48F5",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
@@ -3737,8 +3737,8 @@ exports[`Button should render default button 1`] = `
Object {
"color": "#FFFFFF",
},
- Object {},
- Object {},
+ undefined,
+ undefined,
undefined,
],
]
diff --git a/src/components/button/index.tsx b/src/components/button/index.tsx
index 47d1864c23..79cb43cfd7 100644
--- a/src/components/button/index.tsx
+++ b/src/components/button/index.tsx
@@ -1,10 +1,8 @@
import _ from 'lodash';
import React, {PureComponent} from 'react';
-import {Platform, StyleSheet, LayoutAnimation, LayoutChangeEvent, ImageStyle} from 'react-native';
+import {Platform, StyleSheet, LayoutAnimation, LayoutChangeEvent, ImageStyle, TextStyle} from 'react-native';
import {asBaseComponent, forwardRef, Constants} from '../../commons/new';
import {Colors, Typography, BorderRadiuses} from 'style';
-// @ts-ignore need to migrate to commonsNew
-import {modifiers} from 'commons';
import TouchableOpacity from '../touchableOpacity';
import Text from '../text';
import Image from '../image';
@@ -14,8 +12,6 @@ export {ButtonSize, ButtonAnimationDirection, ButtonProps};
import {PADDINGS, HORIZONTAL_PADDINGS, MIN_WIDTH, DEFAULT_SIZE} from './ButtonConstants';
-const {extractColorValue, extractTypographyValue} = modifiers;
-
class Button extends PureComponent {
static displayName = 'Button';
@@ -77,16 +73,15 @@ class Button extends PureComponent {
}
getBackgroundColor() {
- const {backgroundColor: themeBackgroundColor, modifiers} = this.props;
- const {disabled, outline, disabledBackgroundColor, backgroundColor: propsBackgroundColor} = this.props;
- const {backgroundColor: stateBackgroundColor} = modifiers;
+ const {disabled, outline, disabledBackgroundColor, backgroundColor, modifiers} = this.props;
+ const {backgroundColor: modifiersBackgroundColor} = modifiers;
if (!outline && !this.isLink) {
if (disabled) {
return disabledBackgroundColor || Colors.$backgroundDisabled;
}
- return propsBackgroundColor || stateBackgroundColor || themeBackgroundColor || Colors.$backgroundPrimaryHeavy;
+ return backgroundColor || modifiersBackgroundColor || Colors.$backgroundPrimaryHeavy;
}
return 'transparent';
}
@@ -100,7 +95,8 @@ class Button extends PureComponent {
}
getLabelColor() {
- const {linkColor, outline, outlineColor, disabled, color: propsColor, backgroundColor} = this.props;
+ const {linkColor, outline, outlineColor, disabled, color: propsColor, backgroundColor, modifiers} = this.props;
+ const {color: modifiersColor} = modifiers;
const isLink = this.isLink;
let color: string | undefined = Colors.$textDefaultLight;
@@ -116,27 +112,34 @@ class Button extends PureComponent {
return Colors.$textDisabled;
}
- color = propsColor || extractColorValue(this.props) || color;
+ color = propsColor || modifiersColor || color;
return color;
}
getLabelSizeStyle() {
const size = this.props.size || DEFAULT_SIZE;
- const LABEL_STYLE_BY_SIZE: Dictionary