diff --git a/example/package-lock.json b/example/package-lock.json index 63cedac8..0c852fb3 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -14,6 +14,7 @@ "@react-navigation/native-stack": "^6.9.10", "react": "18.1.0", "react-native": "0.70.6", + "react-native-drop-shadow": "^0.0.6", "react-native-gesture-handler": "^2.9.0", "react-native-icomoon": "^0.1.1", "react-native-paper": "^5.1.4", @@ -7645,6 +7646,17 @@ "nullthrows": "^1.1.1" } }, + "node_modules/react-native-drop-shadow": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/react-native-drop-shadow/-/react-native-drop-shadow-0.0.6.tgz", + "integrity": "sha512-eIFZoU7yhr90xB0/Gfuolt/Eelk0F+ohy3piutuYFA+xJx1xtJ/t9xWaaTPwTGXK+56CTnp8mAX4VPDz464NZg==", + "dependencies": { + "logkitty": "^0.7.1" + }, + "peerDependencies": { + "react-native": ">=0.61.5" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.9.0.tgz", @@ -15296,6 +15308,14 @@ "nullthrows": "^1.1.1" } }, + "react-native-drop-shadow": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/react-native-drop-shadow/-/react-native-drop-shadow-0.0.6.tgz", + "integrity": "sha512-eIFZoU7yhr90xB0/Gfuolt/Eelk0F+ohy3piutuYFA+xJx1xtJ/t9xWaaTPwTGXK+56CTnp8mAX4VPDz464NZg==", + "requires": { + "logkitty": "^0.7.1" + } + }, "react-native-gesture-handler": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.9.0.tgz", diff --git a/example/package.json b/example/package.json index aa18b339..ef632c67 100644 --- a/example/package.json +++ b/example/package.json @@ -15,6 +15,7 @@ "@react-navigation/native-stack": "^6.9.10", "react": "18.1.0", "react-native": "0.70.6", + "react-native-drop-shadow": "^0.0.6", "react-native-gesture-handler": "^2.9.0", "react-native-icomoon": "^0.1.1", "react-native-paper": "^5.1.4", diff --git a/example/src/App.tsx b/example/src/App.tsx index e1c7bdf2..dc8b1376 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -48,7 +48,12 @@ const App = () => { return ( - + diff --git a/example/src/Buttons/ButtonsPage.tsx b/example/src/Buttons/ButtonsPage.tsx index 2aecfb33..00af87d2 100644 --- a/example/src/Buttons/ButtonsPage.tsx +++ b/example/src/Buttons/ButtonsPage.tsx @@ -1,18 +1,95 @@ import React from 'react'; -import { StyleSheet } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { Button, Screen } from 'smartway-react-native-ui'; export const ButtonsPage = () => { return ( - - + + + + + + + + + + + + + + + + + + + + + + + + + + + ); }; const styles = StyleSheet.create({ container: { + flexDirection: 'row', + justifyContent: 'center', + }, + buttonContainer: { alignItems: 'center', }, + button: { + margin: 6, + }, + outlinedButton: { + margin: 5, + }, }); diff --git a/example/src/Dialog/DialogPage.tsx b/example/src/Dialog/DialogPage.tsx index 4556ca41..9dea8446 100644 --- a/example/src/Dialog/DialogPage.tsx +++ b/example/src/Dialog/DialogPage.tsx @@ -1,32 +1,37 @@ import React, { useState } from 'react'; -import { StyleSheet } from 'react-native'; +import { StatusBar, StyleSheet } from 'react-native'; import { Button, Dialog, Screen } from 'smartway-react-native-ui'; export const DialogPage = () => { - const [modalVisible, setModalVisible] = useState(false); - - const showModal = () => { - setModalVisible(true); - }; - - const hideModal = () => { - setModalVisible(false); - }; + const [singleOptionDialog, setSingleOptionDialog] = useState(false); + const [twoOptionsDialog, setTwoOptionsDialog] = useState(false); return ( - + setSingleOptionDialog(false)} + onDismiss={() => setSingleOptionDialog(false)} + /> + + confirmButtonLabel={'Valider'} + onDismiss={() => setTwoOptionsDialog(false)} + onConfirm={() => setTwoOptionsDialog(false)} + /> ); }; diff --git a/jest.config.ts b/jest.config.ts index f254c7c0..c0ce1f81 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -15,6 +15,10 @@ const jestConfig: JestConfigWithTsJest = { }, testMatch: ['**/?(*.)test.(ts|tsx)'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + transformIgnorePatterns: [ + 'node_modules/(?!(@react-native|react-native|react-native-drop-shadow)/)', + ], + moduleDirectories: ['node_modules', 'src'], setupFiles: [ './node_modules/react-native-gesture-handler/jestSetup.js', './node_modules/react-native/jest/setup.js', diff --git a/package-lock.json b/package-lock.json index 0ec4a53d..80473a8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "@gorhom/bottom-sheet": "*", "react": "*", "react-native": "*", + "react-native-drop-shadow": "*", "react-native-gesture-handler": "*", "react-native-icomoon": "*", "react-native-paper": "*", @@ -15264,6 +15265,18 @@ "nullthrows": "^1.1.1" } }, + "node_modules/react-native-drop-shadow": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/react-native-drop-shadow/-/react-native-drop-shadow-0.0.6.tgz", + "integrity": "sha512-eIFZoU7yhr90xB0/Gfuolt/Eelk0F+ohy3piutuYFA+xJx1xtJ/t9xWaaTPwTGXK+56CTnp8mAX4VPDz464NZg==", + "peer": true, + "dependencies": { + "logkitty": "^0.7.1" + }, + "peerDependencies": { + "react-native": ">=0.61.5" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.9.0.tgz", diff --git a/package.json b/package.json index 89e9d7c6..6c042c46 100644 --- a/package.json +++ b/package.json @@ -57,12 +57,12 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", + "jest": "^29.3.1", "jest-junit": "^15.0.0", "pod-install": "^0.1.0", "prettier": "2.8.4", "react": "18.1.0", "react-native": "0.70.6", - "jest": "^29.3.1", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", "typescript": "^4.9.5" @@ -80,6 +80,8 @@ "react-native-reanimated": "*", "react-native-safe-area-context": "*", "react-native-svg": "*", - "react-native-vector-icons": "*" - } + "react-native-vector-icons": "*", + "react-native-drop-shadow": "*" + }, + "dependencies": {} } diff --git a/src/__tests__/components/__snapshots__/ActionCard.test.tsx.snap b/src/__tests__/components/__snapshots__/ActionCard.test.tsx.snap index 66e23aee..fc6d963a 100644 --- a/src/__tests__/components/__snapshots__/ActionCard.test.tsx.snap +++ b/src/__tests__/components/__snapshots__/ActionCard.test.tsx.snap @@ -24,65 +24,92 @@ exports[`MODULE | ActionCard component renders correctly 1`] = ` - - - - - - Test - + + + + + + + + Test + + + @@ -115,65 +142,92 @@ exports[`MODULE | ActionCard component renders correctly in disabled state 1`] = - - - - - - Test - + + + + + + + + Test + + + diff --git a/src/components/Screen.tsx b/src/components/Screen.tsx index 8e63f635..c4d828b8 100644 --- a/src/components/Screen.tsx +++ b/src/components/Screen.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { SafeAreaView, ViewStyle, StatusBar, StyleSheet } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useTheme } from '../styles/themes'; type Props = { @@ -11,11 +12,13 @@ type Props = { export const Screen = ({ children, style, testID, statusBarColor }: Props) => { const theme = useTheme(); + const insets = useSafeAreaInsets(); const styles = StyleSheet.create({ screen: { flex: 1, backgroundColor: theme.sw.colors.neutral[50], + marginTop: insets.top, paddingLeft: theme.sw.spacing.m, paddingRight: theme.sw.spacing.m, ...style, @@ -25,6 +28,7 @@ export const Screen = ({ children, style, testID, statusBarColor }: Props) => { return ( diff --git a/src/components/actionCard/ActionCard.tsx b/src/components/actionCard/ActionCard.tsx index 4783d045..c6e0b718 100644 --- a/src/components/actionCard/ActionCard.tsx +++ b/src/components/actionCard/ActionCard.tsx @@ -5,6 +5,7 @@ import { useTheme } from '../../styles/themes'; import { Button } from '../buttons/Button'; import { Icon } from '../icons/Icon'; import { Body } from '../typography/Body'; +import DropShadow from 'react-native-drop-shadow'; interface Props { children?: ReactNode; @@ -33,12 +34,11 @@ export const ActionCard = ({ container: { backgroundColor: disabled ? theme.sw.colors.neutral[100] : theme.sw.colors.neutral[50], borderWidth: 1, - borderColor: disabled ? theme.sw.colors.neutral[400] : theme.sw.colors.neutral[200], + borderColor: disabled ? theme.sw.colors.neutral[400] : theme.sw.colors.neutral[400], width: '100%', borderRadius: 20, alignItems: 'center', ...style, - elevation: 2, }, close: { position: 'absolute', @@ -60,25 +60,47 @@ export const ActionCard = ({ paddingBottom: theme.sw.spacing.xl, alignItems: 'center', }, + bigShadow: { + shadowColor: '#919EAB1F', + shadowOffset: { + width: 0, + height: 12, + }, + shadowOpacity: 0.12, + shadowRadius: 24, + }, + smallShadow: { + shadowColor: '#919EAB80', + shadowOffset: { + width: 0, + height: 0, + }, + shadowOpacity: 0.2, + shadowRadius: 2, + }, }); return ( - - {onClose && ( - - )} - - {title} - - {children} - {bottomChildren && ( - - - {bottomChildren} + + + + {onClose && ( + + )} + + {title} + + {children} + {bottomChildren && ( + + + {bottomChildren} + + )} - )} - + + ); }; diff --git a/src/components/appBar/AppBar.tsx b/src/components/appBar/AppBar.tsx index 0ebbfb13..cff7f2c5 100644 --- a/src/components/appBar/AppBar.tsx +++ b/src/components/appBar/AppBar.tsx @@ -63,6 +63,7 @@ export const AppBar = ({ body: { paddingRight: theme.sw.spacing.m, color: disabled ? theme.sw.colors.neutral[500] : theme.sw.colors.neutral[800], + lineHeight: 26, }, }); diff --git a/src/components/buttons/BaseButtonProps.ts b/src/components/buttons/BaseButtonProps.ts index 4a0629b8..5c906a35 100644 --- a/src/components/buttons/BaseButtonProps.ts +++ b/src/components/buttons/BaseButtonProps.ts @@ -1,10 +1,20 @@ import type { ReactNode } from 'react'; import type { TextStyle, ViewStyle } from 'react-native'; +export type ButtonStatus = + | 'default' + | 'primary' + | 'information' + | 'success' + | 'warning' + | 'error' + | 'neutral'; export interface BaseButtonProps { children?: ReactNode; style?: ViewStyle; labelStyle?: TextStyle; onClick?: () => void; testID?: string; + disabled?: boolean; + status?: ButtonStatus; } diff --git a/src/components/buttons/Button.tsx b/src/components/buttons/Button.tsx index 727524df..0a8d1095 100644 --- a/src/components/buttons/Button.tsx +++ b/src/components/buttons/Button.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { TextButton } from './TextButton'; -import { Button as BaseButton } from 'react-native-paper'; import { FilledButton } from './FilledButton'; import type { BaseButtonProps } from './BaseButtonProps'; +import { OutlinedButton } from './OutlinedButton'; interface ButtonProps extends BaseButtonProps { - mode?: 'text' | 'filled'; + mode?: 'filled' | 'outlined' | 'text'; } export const Button = ({ @@ -14,25 +14,48 @@ export const Button = ({ style, labelStyle, onClick, + status = 'primary', + disabled, testID, }: ButtonProps) => { if (mode === 'text') { return ( - + {children} ); } else if (mode === 'filled') { return ( - + {children} ); } else { return ( - + {children} - + ); } }; diff --git a/src/components/buttons/ButtonColors.ts b/src/components/buttons/ButtonColors.ts new file mode 100644 index 00000000..d6e16e68 --- /dev/null +++ b/src/components/buttons/ButtonColors.ts @@ -0,0 +1,26 @@ +import type { ThemType } from 'src/styles/themes'; +import type { ButtonStatus } from './BaseButtonProps'; + +export const getButtonColors = ( + theme: ThemType, + status?: ButtonStatus, + disabled?: boolean, +): string => { + if (disabled) { + return theme.sw.colors.neutral[500]; + } + switch (status) { + case 'primary': + return theme.sw.colors.primary[400]; + case 'information': + return theme.sw.colors.information[400]; + case 'success': + return theme.sw.colors.success[400]; + case 'warning': + return theme.sw.colors.warning[400]; + case 'error': + return theme.sw.colors.error[400]; + default: + return theme.sw.colors.neutral[700]; + } +}; diff --git a/src/components/buttons/FilledButton.tsx b/src/components/buttons/FilledButton.tsx index 68b0723d..f415e900 100644 --- a/src/components/buttons/FilledButton.tsx +++ b/src/components/buttons/FilledButton.tsx @@ -1,32 +1,51 @@ import React from 'react'; -import type { TextStyle, ViewStyle } from 'react-native'; +import { StyleSheet } from 'react-native'; import { Button as BaseButton } from 'react-native-paper'; import { useTheme } from '../../styles/themes'; import type { BaseButtonProps } from './BaseButtonProps'; +import { getButtonColors } from './ButtonColors'; -export const FilledButton = ({ children, style, labelStyle, onClick, testID }: BaseButtonProps) => { +export const FilledButton = ({ + children, + style, + labelStyle, + onClick, + testID, + disabled, + status, +}: BaseButtonProps) => { const theme = useTheme(); - const buttonStyle: ViewStyle = { - borderRadius: 8, - backgroundColor: theme.sw.colors.primary[400], - ...style, - }; - const _labelStyle: TextStyle = { - fontFamily: 'PublicSans-Regular', - fontSize: 16, - lineHeight: 19, - color: theme.sw.colors.neutral[50], - paddingVertical: theme.sw.spacing.s, - paddingHorizontal: theme.sw.spacing.l, - // Overrides default margin of Paper component - marginVertical: 0, - marginHorizontal: 0, - ...labelStyle, - }; + const buttonColor = getButtonColors(theme, status, disabled); + const transparencyValue = '3D'; + + const styles = StyleSheet.create({ + button: { + borderRadius: 8, + backgroundColor: disabled ? buttonColor + transparencyValue : buttonColor, + ...style, + }, + label: { + fontFamily: 'PublicSans-Regular', + fontSize: 16, + lineHeight: 26, + color: disabled ? theme.sw.colors.neutral[500] : theme.sw.colors.neutral[50], + paddingVertical: theme.sw.spacing.s, + paddingHorizontal: theme.sw.spacing.l, + fontWeight: 'bold', + marginVertical: 0, + marginHorizontal: 0, + ...labelStyle, + }, + }); return ( - + {children} ); diff --git a/src/components/buttons/OutlinedButton.tsx b/src/components/buttons/OutlinedButton.tsx new file mode 100644 index 00000000..152ec1ae --- /dev/null +++ b/src/components/buttons/OutlinedButton.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { StyleSheet } from 'react-native'; +import { Button as BaseButton } from 'react-native-paper'; +import { useTheme } from '../../styles/themes'; +import type { BaseButtonProps } from './BaseButtonProps'; +import { getButtonColors } from './ButtonColors'; + +export const OutlinedButton = ({ + children, + style, + labelStyle, + onClick, + status, + disabled, + testID, +}: BaseButtonProps) => { + const theme = useTheme(); + + const transparencyValue = '7A'; + const buttonColor = getButtonColors(theme, status, disabled); + + const styles = StyleSheet.create({ + button: { + borderRadius: 8, + borderWidth: 1, + borderColor: buttonColor + transparencyValue, + ...style, + }, + label: { + fontFamily: 'PublicSans-Regular', + fontSize: 16, + lineHeight: 26, + fontWeight: 'bold', + paddingVertical: theme.sw.spacing.s, + paddingHorizontal: theme.sw.spacing.l, + marginVertical: 0, + marginHorizontal: 0, + color: buttonColor, + ...labelStyle, + }, + }); + + return ( + + {children} + + ); +}; diff --git a/src/components/buttons/TextButton.tsx b/src/components/buttons/TextButton.tsx index 73af27bd..e6bc8d62 100644 --- a/src/components/buttons/TextButton.tsx +++ b/src/components/buttons/TextButton.tsx @@ -1,32 +1,47 @@ import React from 'react'; -import type { TextStyle, ViewStyle } from 'react-native'; +import { StyleSheet } from 'react-native'; import { Button as BaseButton } from 'react-native-paper'; import { useTheme } from '../../styles/themes'; import type { BaseButtonProps } from './BaseButtonProps'; +import { getButtonColors } from './ButtonColors'; -export const TextButton = ({ children, style, labelStyle, onClick, testID }: BaseButtonProps) => { +export const TextButton = ({ + children, + style, + labelStyle, + onClick, + status, + disabled, + testID, +}: BaseButtonProps) => { const theme = useTheme(); - const buttonstyle: ViewStyle = { - borderRadius: 0, - ...style, - }; - const _labelStyle: TextStyle = { - color: theme.sw.colors.primary[400], - fontSize: 16, - lineHeight: 19, - fontFamily: 'PublicSans-Regular', - padding: theme.sw.spacing.s, - // Overrides default margin of Paper component - marginVertical: 0, - marginHorizontal: 0, - ...labelStyle, - }; + const buttonColor = getButtonColors(theme, status, disabled); + + const styles = StyleSheet.create({ + button: { + borderRadius: 0, + ...style, + }, + label: { + color: buttonColor, + fontSize: 16, + lineHeight: 26, + fontFamily: 'PublicSans-Regular', + padding: theme.sw.spacing.s, + fontWeight: 'bold', + // Overrides default margin of Paper component + marginVertical: 0, + marginHorizontal: 0, + ...labelStyle, + }, + }); + return ( diff --git a/src/components/dialogs/Dialog.tsx b/src/components/dialogs/Dialog.tsx index e0a9cb78..eb84b0ad 100644 --- a/src/components/dialogs/Dialog.tsx +++ b/src/components/dialogs/Dialog.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { TextStyle, View, ViewStyle } from 'react-native'; -import { Dialog as BaseDialog } from 'react-native-paper'; +import { StyleSheet, TextStyle, View, ViewStyle } from 'react-native'; +import { Dialog as BaseDialog, Portal } from 'react-native-paper'; import { useTheme } from '../../styles/themes'; import { Button } from '../buttons/Button'; import { Body } from '../typography/Body'; @@ -14,7 +14,7 @@ interface DialogProps { actionsStyle?: ViewStyle; title: string; content: string; - dismissButtonLabel: string; + dismissButtonLabel?: string; confirmButtonLabel: string; onDismiss: () => void; onConfirm: () => void; @@ -22,46 +22,75 @@ interface DialogProps { export const Dialog = (props: DialogProps) => { const theme = useTheme(); - const _dialogStyle: ViewStyle = { - borderRadius: theme.sw.spacing.l, - padding: theme.sw.spacing.l, - marginTop: 0, - backgroundColor: theme.sw.colors.neutral[50], - ...props.style, - }; - const _titleStyle: TextStyle = { - marginBottom: theme.sw.spacing.l, - ...props.titleStyle, - }; - - const _contentStyle: ViewStyle = { - marginBottom: theme.sw.spacing.l, - ...props.contentStyle, - }; - - const _actionsStyle: ViewStyle = { - flexDirection: 'row', - justifyContent: 'flex-end', - ...props.actionsStyle, - }; + const styles = StyleSheet.create({ + dialog: { + borderRadius: theme.sw.spacing.l, + padding: theme.sw.spacing.l, + paddingTop: theme.sw.spacing.xl, + marginTop: 0, + backgroundColor: theme.sw.colors.neutral[50], + ...props.style, + }, + title: { + marginBottom: theme.sw.spacing.l, + ...props.titleStyle, + }, + content: { + marginBottom: theme.sw.spacing.l, + color: theme.sw.colors.neutral[600], + ...props.contentStyle, + }, + actions: { + flexDirection: 'row', + justifyContent: 'flex-end', + ...props.actionsStyle, + }, + leftOption: { + color: theme.sw.colors.neutral[800], + fontWeight: 'bold', + marginRight: theme.sw.spacing.m, + }, + rightOption: { + flex: props.dismissButtonLabel ? 0 : 1, + }, + }); return ( - - - - {props.title} - - {props.content} - - - + + + + + {props.title} + + {props.content} + + {props.onDismiss && props.dismissButtonLabel && ( + + )} + + + - - + + ); };