diff --git a/eslint.config.mjs b/eslint.config.mjs index 2affe1c..1c16299 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -54,7 +54,7 @@ export default [ rules: { 'observation/no-function-without-logging': 'error', - 'react-native/no-unused-styles': 'error', + 'react-native/no-unused-styles': 'off', 'react-native/no-inline-styles': 'off', 'prettier/prettier': 'error', diff --git a/package.json b/package.json index 354ddf1..7907606 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@observation.org/react-native-components", - "version": "1.72.0", + "version": "1.73.0", "main": "src/index.ts", "repository": "git@github.com:observation/react-native-components.git", "author": "Observation.org", diff --git a/src/components/BackButton.tsx b/src/components/BackButton.tsx index a845d92..3a8530b 100644 --- a/src/components/BackButton.tsx +++ b/src/components/BackButton.tsx @@ -3,18 +3,22 @@ import React from 'react' import { NavigationProp, ParamListBase } from '@react-navigation/native' import IconButton from '../components/IconButton' -import { theme } from '../styles' +import { useTheme } from '../theme/ThemeProvider' type Props = { navigation: NavigationProp } -const BackButton = ({ navigation }: Props) => ( - navigation.goBack()} - icon={{ name: 'chevron-left', size: theme.icon.size.xxl, color: theme.color.primary500 }} - /> -) +const BackButton = ({ navigation }: Props) => { + const theme = useTheme() + + return ( + navigation.goBack()} + icon={{ name: 'chevron-left', size: theme.icon.size.xxl, color: theme.color.primary500 }} + /> + ) +} export default BackButton diff --git a/src/components/BottomSheet.tsx b/src/components/BottomSheet.tsx index d8bb26d..47d9b04 100644 --- a/src/components/BottomSheet.tsx +++ b/src/components/BottomSheet.tsx @@ -1,11 +1,13 @@ import React from 'react' import { StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-native' -import { useTheme } from '@react-navigation/native' +import { useTheme as useNavigationTheme } from '@react-navigation/native' import LargeButton, { LargeButtonProps } from './LargeButton' -import { shadow, theme } from '../styles' +import { Theme } from '../@types/theme' +import { shadow } from '../styles' import textStyle from '../styles/text' +import { useStyles, useTheme } from '../theme/ThemeProvider' type Props = { title?: string @@ -17,7 +19,10 @@ type Props = { } const BottomSheet = ({ title, text, buttons = [], style, testID, children }: Props) => { - const { colors } = useTheme() + const { colors } = useNavigationTheme() + const theme = useTheme() + const styles = useStyles(createStyles) + const buttonsMarginTop = title || text ? theme.margin.common : 0 return ( @@ -59,27 +64,28 @@ const BottomSheet = ({ title, text, buttons = [], style, testID, children }: Pro export default BottomSheet -const styles = StyleSheet.create({ - container: { - ...shadow.normal.ios, - borderTopWidth: 1 / 3, - borderTopColor: theme.color.grey300, - }, - bottomSheetContainer: { - ...shadow.normal.android, - }, - bottomSheet: { - flexDirection: 'column', - margin: theme.margin.common, - }, - buttonContainer: { - marginHorizontal: -theme.margin.half, - flexDirection: 'row', - alignItems: 'flex-start', - }, - buttonStyle: { - flex: 1, - margin: 0, - marginHorizontal: theme.margin.half, - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + container: { + ...shadow.normal.ios, + borderTopWidth: 1 / 3, + borderTopColor: theme.color.grey300, + }, + bottomSheetContainer: { + ...shadow.normal.android, + }, + bottomSheet: { + flexDirection: 'column', + margin: theme.margin.common, + }, + buttonContainer: { + marginHorizontal: -theme.margin.half, + flexDirection: 'row', + alignItems: 'flex-start', + }, + buttonStyle: { + flex: 1, + margin: 0, + marginHorizontal: theme.margin.half, + }, + }) diff --git a/src/components/BrandIcon.tsx b/src/components/BrandIcon.tsx index 77241b1..ae42359 100644 --- a/src/components/BrandIcon.tsx +++ b/src/components/BrandIcon.tsx @@ -3,7 +3,7 @@ import React from 'react' import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome' import BrandIcons, { BrandIconName } from '../lib/BrandIcons' -import { theme } from '../styles' +import { useTheme } from '../theme/ThemeProvider' type Props = { name: BrandIconName @@ -12,6 +12,7 @@ type Props = { } export const BrandIcon = ({ name, color, size }: Props) => { + const theme = useTheme() const icon = BrandIcons[name] const iconColor = color ?? theme.color.primary500 const iconSize = size ?? theme.icon.size.l diff --git a/src/components/Checkbox.tsx b/src/components/Checkbox.tsx index 7414b79..ac9cd91 100644 --- a/src/components/Checkbox.tsx +++ b/src/components/Checkbox.tsx @@ -3,7 +3,7 @@ import { StyleProp, StyleSheet, TouchableOpacity, View, ViewStyle } from 'react- import { Icon } from './Icon' import Log from '../lib/Log' -import { theme } from '../styles' +import { useTheme } from '../theme/ThemeProvider' type Props = { enabled: boolean @@ -21,16 +21,18 @@ const Checkbox = ({ containerStyle, iconContainerStyle, children, - lineHeight = theme.margin.large, + lineHeight, testID = 'pressable', }: Props) => { Log.debug('Checkbox') + const theme = useTheme() + const size = lineHeight ?? theme.margin.large return ( - + {enabled ? ( ) : ( diff --git a/src/components/Chip.tsx b/src/components/Chip.tsx index 1055a82..7ad39d2 100644 --- a/src/components/Chip.tsx +++ b/src/components/Chip.tsx @@ -10,8 +10,10 @@ import { ViewStyle, } from 'react-native' -import { fontSize, theme } from '../styles' +import { Theme } from '../@types/theme' +import { fontSize } from '../styles' import appTextStyle from '../styles/text' +import { useStyles, useTheme } from '../theme/ThemeProvider' type Props = { text?: string @@ -22,6 +24,8 @@ type Props = { } const Chip = ({ text, textStyle, containerStyle, onPress, disabled }: Props) => { + const theme = useTheme() + const styles = useStyles(createStyles) const [borderRadius, setBorderRadius] = useState(theme.margin.common) return ( @@ -37,25 +41,26 @@ const Chip = ({ text, textStyle, containerStyle, onPress, disabled }: Props) => ) } -const styles = StyleSheet.create({ - chipTextContainer: { - flexDirection: 'row', - justifyContent: 'center', - }, - chipText: { - ...appTextStyle.body, - color: theme.color.white, - lineHeight: theme.margin.common, - fontSize: fontSize.medium, - }, - chipContainer: { - backgroundColor: theme.color.accentLime400, - paddingHorizontal: theme.margin.common, - paddingVertical: theme.margin.half, - borderRadius: theme.margin.common, - minWidth: 44, - justifyContent: 'center', - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + chipTextContainer: { + flexDirection: 'row', + justifyContent: 'center', + }, + chipText: { + ...appTextStyle.body, + color: theme.color.white, + lineHeight: theme.margin.common, + fontSize: fontSize.medium, + }, + chipContainer: { + backgroundColor: theme.color.accentLime400, + paddingHorizontal: theme.margin.common, + paddingVertical: theme.margin.half, + borderRadius: theme.margin.common, + minWidth: 44, + justifyContent: 'center', + }, + }) export default Chip diff --git a/src/components/ContentImage.tsx b/src/components/ContentImage.tsx index 971f991..ce9b1c4 100644 --- a/src/components/ContentImage.tsx +++ b/src/components/ContentImage.tsx @@ -4,7 +4,9 @@ import { Dimensions, Image, StyleSheet, Text, TouchableOpacity, View } from 'rea import ScalableImage from 'react-native-scalable-image' import Lightbox from './Lightbox' -import { font, rounded, shadow, theme } from '../styles' +import { Theme } from '../@types/theme' +import { font, rounded, shadow } from '../styles' +import { useTheme } from '../theme/ThemeProvider' type Props = { src: string @@ -12,6 +14,8 @@ type Props = { } const ContentImage = ({ src, alt }: Props) => { + const theme = useTheme() + const styles = createStyles(theme) const [photoIndex, setPhotoIndex] = React.useState() if (!alt) { return @@ -53,43 +57,44 @@ const ContentImage = ({ src, alt }: Props) => { export default ContentImage -const styles = StyleSheet.create({ - outerContainer: { - margin: -theme.margin.common, - marginBottom: -theme.margin.half, - ...shadow.normal.ios, - }, - innerContainer: { - flexDirection: 'row', - margin: theme.margin.common, - backgroundColor: theme.color.white, - ...shadow.normal.android, - ...rounded.large, - borderWidth: 1, - borderColor: theme.color.grey50, - }, - imageContainer: { - margin: theme.margin.common, - marginRight: theme.margin.half, - ...rounded.large, - }, - image: { - height: 80, - width: 80, - }, - textContainer: { - flex: 1, - marginRight: theme.margin.common, - marginLeft: theme.margin.half, - justifyContent: 'center', - }, - title: { - ...font.smallBold, - color: theme.color.black, - marginBottom: theme.margin.quarter, - }, - description: { - ...font.small, - color: theme.color.grey500, - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + outerContainer: { + margin: -theme.margin.common, + marginBottom: -theme.margin.half, + ...shadow.normal.ios, + }, + innerContainer: { + flexDirection: 'row', + margin: theme.margin.common, + backgroundColor: theme.color.white, + ...shadow.normal.android, + ...rounded.large, + borderWidth: 1, + borderColor: theme.color.grey50, + }, + imageContainer: { + margin: theme.margin.common, + marginRight: theme.margin.half, + ...rounded.large, + }, + image: { + height: 80, + width: 80, + }, + textContainer: { + flex: 1, + marginRight: theme.margin.common, + marginLeft: theme.margin.half, + justifyContent: 'center', + }, + title: { + ...font.smallBold, + color: theme.color.black, + marginBottom: theme.margin.quarter, + }, + description: { + ...font.small, + color: theme.color.grey500, + }, + }) diff --git a/src/components/Date.tsx b/src/components/Date.tsx index 4220242..ac85ce6 100644 --- a/src/components/Date.tsx +++ b/src/components/Date.tsx @@ -3,24 +3,27 @@ import { StyleProp, ViewStyle } from 'react-native' import { Icon } from './Icon' import IconText from './IconText' -import { theme } from '../styles' import textStyle from '../styles/text' +import { useTheme } from '../theme/ThemeProvider' type Props = { date: string containerStyle?: StyleProp } -const Date = ({ date, containerStyle }: Props) => ( - } - text={date} - style={{ - containerStyle, - textStyle: textStyle.light, - }} - singleLineText - /> -) +const Date = ({ date, containerStyle }: Props) => { + const theme = useTheme() + return ( + } + text={date} + style={{ + containerStyle, + textStyle: textStyle.light, + }} + singleLineText + /> + ) +} export default Date diff --git a/src/components/Disclose.tsx b/src/components/Disclose.tsx index 6d90b06..eab5663 100644 --- a/src/components/Disclose.tsx +++ b/src/components/Disclose.tsx @@ -2,9 +2,10 @@ import React from 'react' import { StyleProp, StyleSheet, Text, TextStyle, TouchableOpacity, View, ViewStyle } from 'react-native' import { Icon } from './Icon' +import { Theme } from '../@types/theme' import Log from '../lib/Log' -import { theme } from '../styles' import appTextStyle from '../styles/text' +import { useStyles, useTheme } from '../theme/ThemeProvider' type Props = { text: string @@ -15,6 +16,9 @@ type Props = { const Disclose = ({ text, onPress, textStyle, containerStyle }: Props) => { Log.debug('Disclose') + const theme = useTheme() + const styles = useStyles(createStyles) + return ( @@ -27,12 +31,13 @@ const Disclose = ({ text, onPress, textStyle, containerStyle }: Props) => { export default Disclose -const styles = StyleSheet.create({ - containerStyle: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - marginHorizontal: theme.margin.common, - backgroundColor: theme.color.white, - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + containerStyle: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginHorizontal: theme.margin.common, + backgroundColor: theme.color.white, + }, + }) diff --git a/src/components/DocumentLink.tsx b/src/components/DocumentLink.tsx index 42d0285..3282568 100644 --- a/src/components/DocumentLink.tsx +++ b/src/components/DocumentLink.tsx @@ -3,8 +3,8 @@ import { StyleProp, ViewStyle } from 'react-native' import { Icon } from './Icon' import IconText from './IconText' -import { theme } from '../styles' import textStyle from '../styles/text' +import { useTheme } from '../theme/ThemeProvider' type Props = { onPress?: () => void @@ -13,16 +13,19 @@ type Props = { label: string } -const DocumentLink = ({ onPress, containerStyle, label }: Props) => ( - } - text={label} - style={{ - containerStyle, - textStyle: textStyle.link, - }} - onPress={onPress} - /> -) +const DocumentLink = ({ onPress, containerStyle, label }: Props) => { + const theme = useTheme() + return ( + } + text={label} + style={{ + containerStyle, + textStyle: textStyle.link, + }} + onPress={onPress} + /> + ) +} export default DocumentLink diff --git a/src/components/FilterButton.tsx b/src/components/FilterButton.tsx deleted file mode 100644 index 91654c5..0000000 --- a/src/components/FilterButton.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react' -import { StyleProp, StyleSheet, TouchableOpacity, ViewStyle } from 'react-native' - -import { rounded, theme } from '../styles' - -type Props = { - content: (_: { color: string }) => React.ReactElement - onPress?: () => void - active?: boolean - style?: StyleProp -} - -const FilterButton = ({ content, onPress, active = false, style }: Props) => { - const buttonStyle = active ? styles.activeButton : styles.button - const contentColor = active ? theme.color.white : theme.color.grey800 - return ( - - {content({ color: contentColor })} - - ) -} - -export default FilterButton - -const styles = StyleSheet.create({ - container: { - ...rounded.normal, - justifyContent: 'center', - }, - button: { - backgroundColor: theme.color.white, - }, - activeButton: { - backgroundColor: theme.color.primary500, - }, -}) diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index f8aab01..f21b59d 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -3,7 +3,7 @@ import React from 'react' import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome' import Icons, { IconName } from '../lib/Icons' -import { theme } from '../styles' +import { useTheme } from '../theme/ThemeProvider' export type IconStyleProp = 'light' | 'solid' @@ -20,6 +20,7 @@ export type IconProps = IconAppearanceProps & { } export const Icon = ({ name, color, size, testID, style, rotation }: IconProps): React.ReactElement => { + const theme = useTheme() const iconStyle = style ?? 'light' const icon = iconStyle === 'light' ? Icons[name].light : Icons[name].solid const iconColor = color ?? theme.color.primary500 diff --git a/src/components/IconButton.tsx b/src/components/IconButton.tsx index 32e897a..d17fff4 100644 --- a/src/components/IconButton.tsx +++ b/src/components/IconButton.tsx @@ -2,7 +2,7 @@ import React from 'react' import { StyleProp, TouchableOpacity, ViewStyle } from 'react-native' import { Icon, IconProps } from './Icon' -import { theme } from '../styles' +import { useTheme } from '../theme/ThemeProvider' type Props = { containerStyle?: StyleProp @@ -13,17 +13,20 @@ type Props = { testID?: string } -const IconButton = ({ containerStyle, disabled, onPress, icon, accessibilityLabel, testID = 'pressable' }: Props) => ( - - - -) +const IconButton = ({ containerStyle, disabled, onPress, icon, accessibilityLabel, testID = 'pressable' }: Props) => { + const theme = useTheme() + return ( + + + + ) +} export default IconButton diff --git a/src/components/IconText.tsx b/src/components/IconText.tsx index 40aec60..bea9cf6 100644 --- a/src/components/IconText.tsx +++ b/src/components/IconText.tsx @@ -1,7 +1,8 @@ import React from 'react' import { StyleProp, StyleSheet, Text, TextStyle, TouchableOpacity, View, ViewStyle } from 'react-native' -import { theme } from '../styles' +import { Theme } from '../@types/theme' +import { useStyles } from '../theme/ThemeProvider' type IconTextStyle = { containerStyle?: StyleProp @@ -18,6 +19,8 @@ type Props = { } const IconText = ({ icon, text, style, onPress, singleLineText = false }: Props) => { + const styles = useStyles(createStyles) + const content = ( {icon} @@ -36,14 +39,15 @@ const IconText = ({ icon, text, style, onPress, singleLineText = false }: Props) ) } -const styles = StyleSheet.create({ - containerStyle: { - flexDirection: 'row', - }, - iconContainer: { - justifyContent: 'center', - marginRight: theme.margin.half, - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + containerStyle: { + flexDirection: 'row', + }, + iconContainer: { + justifyContent: 'center', + marginRight: theme.margin.half, + }, + }) export default IconText diff --git a/src/components/IconView.tsx b/src/components/IconView.tsx index 8a869b0..e3a674a 100644 --- a/src/components/IconView.tsx +++ b/src/components/IconView.tsx @@ -1,7 +1,7 @@ import React from 'react' import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native' -import { theme } from '../styles' +import { useTheme } from '../theme/ThemeProvider' type Props = { icon?: React.ReactElement @@ -11,16 +11,20 @@ type Props = { iconContainerStyle?: StyleProp } -const IconView = ({ icon, lineHeight = theme.margin.large, children, containerStyle, iconContainerStyle }: Props) => ( - - {icon && ( - - {icon} - - )} - {children} - -) +const IconView = ({ icon, lineHeight, children, containerStyle, iconContainerStyle }: Props) => { + const theme = useTheme() + const size = lineHeight ?? theme.margin.large + return ( + + {icon && ( + + {icon} + + )} + {children} + + ) +} export default IconView diff --git a/src/components/InputField.tsx b/src/components/InputField.tsx index 7006071..d6a23da 100644 --- a/src/components/InputField.tsx +++ b/src/components/InputField.tsx @@ -3,8 +3,10 @@ import { Platform, StyleProp, StyleSheet, Text, TextInput, TextInputProps, View, import { Icon } from './Icon' import IconText from './IconText' -import { font, inputStyles, layout, rounded, theme } from '../styles' +import { Theme } from '../@types/theme' +import { font, inputStyles, layout, rounded } from '../styles' import textStyle from '../styles/text' +import { useStyles, useTheme } from '../theme/ThemeProvider' type Props = { containerStyle?: StyleProp @@ -31,6 +33,8 @@ const InputField = ({ errorMessage, disabled = false, }: Props) => { + const theme = useTheme() + const styles = useStyles(createStyles) const [isFocused, setIsFocused] = useState(false) const inputRef = useRef(null) const didAutoFocus = useRef(false) @@ -98,29 +102,30 @@ const InputField = ({ export default InputField -const styles = StyleSheet.create({ - containerStyle: { - flexDirection: 'column', - }, - labelStyle: { - ...textStyle.inputLabel, - marginBottom: theme.margin.half, - }, - inputStyle: { - ...rounded.normal, - ...inputStyles.input, - ...textStyle.input, - }, - rightIcon: { - ...layout.absoluteRight, - justifyContent: 'center', - }, - errorStyle: { - ...font.small, - color: theme.color.error500, - }, - descriptionStyle: { - ...font.small, - color: theme.color.grey800, - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + containerStyle: { + flexDirection: 'column', + }, + labelStyle: { + ...textStyle.inputLabel, + marginBottom: theme.margin.half, + }, + inputStyle: { + ...rounded.normal, + ...inputStyles.input, + ...textStyle.input, + }, + rightIcon: { + ...layout.absoluteRight, + justifyContent: 'center', + }, + errorStyle: { + ...font.small, + color: theme.color.error500, + }, + descriptionStyle: { + ...font.small, + color: theme.color.grey800, + }, + }) diff --git a/src/components/LargeButton.tsx b/src/components/LargeButton.tsx index 34f2ccc..9eb307b 100644 --- a/src/components/LargeButton.tsx +++ b/src/components/LargeButton.tsx @@ -2,12 +2,14 @@ import React from 'react' import { StyleProp, StyleSheet, Text, TextStyle, TouchableOpacity, View, ViewStyle } from 'react-native' import { Icon } from './Icon' +import { Theme } from '../@types/theme' import { IconName } from '../lib/Icons' import * as LargeButtonStyles from '../lib/LargeButtonStyles' import { LargeButtonStyle } from '../lib/LargeButtonStyles' import Log from '../lib/Log' -import { rounded, theme } from '../styles' +import { rounded } from '../styles' import appTextStyle from '../styles/text' +import { useStyles, useTheme } from '../theme/ThemeProvider' type LargeButtonProps = { title: string @@ -22,7 +24,7 @@ type LargeButtonProps = { testID?: string } -const getStyle = (secondary?: boolean, disabled?: boolean, danger?: boolean): LargeButtonStyle => { +const getStyle = (theme: Theme, secondary?: boolean, disabled?: boolean, danger?: boolean): LargeButtonStyle => { Log.trace('LargeButton:getStyle') const enabled = !disabled @@ -30,20 +32,20 @@ const getStyle = (secondary?: boolean, disabled?: boolean, danger?: boolean): La switch (true) { case primary && enabled && danger: - return LargeButtonStyles.primaryDanger + return LargeButtonStyles.primaryDanger(theme) case primary && enabled && !danger: - return LargeButtonStyles.primary + return LargeButtonStyles.primary(theme) case primary && disabled && !danger: - return LargeButtonStyles.primaryDisabled + return LargeButtonStyles.primaryDisabled(theme) case secondary && enabled && danger: - return LargeButtonStyles.secondaryDanger + return LargeButtonStyles.secondaryDanger(theme) case secondary && enabled && !danger: - return LargeButtonStyles.secondary + return LargeButtonStyles.secondary(theme) case secondary && disabled && !danger: - return LargeButtonStyles.secondaryDisabled + return LargeButtonStyles.secondaryDisabled(theme) default: - return LargeButtonStyles.primary + return LargeButtonStyles.primary(theme) } } @@ -59,7 +61,9 @@ const LargeButton = ({ onPressIn, testID = 'touchable-opacity', }: LargeButtonProps) => { - const { textStyle, buttonStyle, iconColor } = getStyle(secondary, disabled, danger) + const theme = useTheme() + const styles = useStyles(createStyles) + const { textStyle, buttonStyle, iconColor } = getStyle(theme, secondary, disabled, danger) return ( + StyleSheet.create({ + container: { + ...rounded.normal, + margin: theme.margin.common, + height: 32, + justifyContent: 'center', + alignItems: 'center', + }, + title: { + textAlignVertical: 'center', + ...appTextStyle.lead, + }, + titleContainer: { + marginHorizontal: theme.margin.common, + flexDirection: 'row', + }, + iconContainerStyle: { + justifyContent: 'center', + paddingRight: theme.margin.half, + }, + }) diff --git a/src/components/Lightbox.tsx b/src/components/Lightbox.tsx index 5c5a0ec..4958136 100644 --- a/src/components/Lightbox.tsx +++ b/src/components/Lightbox.tsx @@ -6,44 +6,60 @@ import Color from 'color' import { Icon } from './Icon' import PageIndicator from './PageIndicator' -import { font, layout, theme } from '../styles' +import { Theme } from '../@types/theme' +import { font, layout } from '../styles' import textStyle from '../styles/text' +import { useStyles, useTheme } from '../theme/ThemeProvider' const hitSlop = { top: 16, left: 16, bottom: 16, right: 16 } +type getLightboxHeaderComponentParams = { + theme: Theme + styles: ReturnType + numberOfPages: number + onClose: () => void +} + const getLightboxHeaderComponent = - (numberOfPages: number, onClose: () => void) => - ({ imageIndex }: { imageIndex: number }) => ( - - - - - - - - onClose()} hitSlop={hitSlop}> - - + (params: getLightboxHeaderComponentParams) => + ({ imageIndex }: { imageIndex: number }) => { + const { theme, styles, numberOfPages, onClose } = params + return ( + + + + + + + + onClose()} hitSlop={hitSlop}> + + + - - - ) + + ) + } + +type getLightboxFooterComponentParams = { + theme: Theme + styles: ReturnType + title?: string + description?: string + content?: React.ReactElement + style?: LightboxStyle + onPressDelete?: () => void + onPressCrop?: () => void +} -const getLightboxFooterComponent = - ( - title?: string, - description?: string, - content?: React.ReactElement, - style?: LightboxStyle, - onPressDelete?: () => void, - onPressCrop?: () => void, - ) => - () => ( +const getLightboxFooterComponent = (params: getLightboxFooterComponentParams) => () => { + const { theme, styles, title, description, content, style, onPressDelete, onPressCrop } = params + return ( {title && ( @@ -78,6 +94,7 @@ const getLightboxFooterComponent = ) +} type LightboxStyle = { descriptionTextStyle: TextStyle @@ -108,6 +125,8 @@ const Lightbox = ({ content, style, }: Props) => { + const theme = useTheme() + const styles = useStyles(createStyles) const initialImageIndex = index ?? 0 const [currentImageIndex, setCurrentImageIndex] = useState() @@ -126,68 +145,76 @@ const Lightbox = ({ swipeToCloseEnabled={false} onImageIndexChange={setCurrentImageIndex} onRequestClose={onClose} - HeaderComponent={getLightboxHeaderComponent(photos.length, onClose)} - FooterComponent={getLightboxFooterComponent( + HeaderComponent={getLightboxHeaderComponent({ + theme, + styles, + numberOfPages: photos.length, + onClose, + })} + FooterComponent={getLightboxFooterComponent({ + theme, + styles, title, description, - content?.(imageIndex), + content: content?.(imageIndex), style, onPressDelete, - showCrop ? onPressCrop : undefined, - )} + onPressCrop: showCrop ? onPressCrop : undefined, + })} /> ) } export default Lightbox -const styles = StyleSheet.create({ - lightboxFooterContainer: { - ...layout.absoluteBottom, - backgroundColor: '#00000077', - }, - lightboxHeaderContainer: { - ...layout.absoluteTop, - backgroundColor: '#00000077', - }, - lightboxHeader: { - flex: 1, - flexDirection: 'row', - alignContent: 'space-between', - padding: theme.margin.common, - }, - lightboxFooter: { - paddingHorizontal: theme.margin.common, - paddingVertical: theme.margin.half, - }, - pageIndicator: { - alignItems: 'center', - justifyContent: 'center', - }, - closeButton: { - alignItems: 'center', - alignSelf: 'flex-end', - justifyContent: 'center', - }, - footerItem: { - marginVertical: theme.margin.quarter, - }, - title: { - ...font.largeBold, - lineHeight: 24, - color: 'white', - }, - description: { - ...textStyle.body, - color: theme.color.white, - }, - buttonsContainer: { - flexDirection: 'row', - marginVertical: theme.margin.large, - marginHorizontal: theme.margin.common, - }, - buttonContainer: { - flex: 0.5, - alignItems: 'center', - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + lightboxFooterContainer: { + ...layout.absoluteBottom, + backgroundColor: '#00000077', + }, + lightboxHeaderContainer: { + ...layout.absoluteTop, + backgroundColor: '#00000077', + }, + lightboxHeader: { + flex: 1, + flexDirection: 'row', + alignContent: 'space-between', + padding: theme.margin.common, + }, + lightboxFooter: { + paddingHorizontal: theme.margin.common, + paddingVertical: theme.margin.half, + }, + pageIndicator: { + alignItems: 'center', + justifyContent: 'center', + }, + closeButton: { + alignItems: 'center', + alignSelf: 'flex-end', + justifyContent: 'center', + }, + footerItem: { + marginVertical: theme.margin.quarter, + }, + title: { + ...font.largeBold, + lineHeight: 24, + color: 'white', + }, + description: { + ...textStyle.body, + color: theme.color.white, + }, + buttonsContainer: { + flexDirection: 'row', + marginVertical: theme.margin.large, + marginHorizontal: theme.margin.common, + }, + buttonContainer: { + flex: 0.5, + alignItems: 'center', + }, + }) diff --git a/src/components/Location.tsx b/src/components/Location.tsx index dc908ec..7427f5c 100644 --- a/src/components/Location.tsx +++ b/src/components/Location.tsx @@ -3,24 +3,27 @@ import { StyleProp, ViewStyle } from 'react-native' import { Icon } from './Icon' import IconText from './IconText' -import { theme } from '../styles' import textStyle from '../styles/text' +import { useTheme } from '../theme/ThemeProvider' type Props = { location: string containerStyle?: StyleProp } -const Location = ({ location, containerStyle }: Props) => ( - } - text={location} - style={{ - containerStyle, - textStyle: textStyle.light, - }} - singleLineText - /> -) +const Location = ({ location, containerStyle }: Props) => { + const theme = useTheme() + return ( + } + text={location} + style={{ + containerStyle, + textStyle: textStyle.light, + }} + singleLineText + /> + ) +} export default Location diff --git a/src/components/Message.tsx b/src/components/Message.tsx index 97a433c..8501e8b 100644 --- a/src/components/Message.tsx +++ b/src/components/Message.tsx @@ -2,8 +2,8 @@ import React from 'react' import { StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-native' import LargeButton, { LargeButtonProps } from '../components/LargeButton' -import { theme } from '../styles' import textStyle from '../styles/text' +import { useTheme } from '../theme/ThemeProvider' type Props = { title?: string @@ -12,38 +12,41 @@ type Props = { style?: StyleProp } -const Message = ({ title, text, buttons, style }: Props) => ( - - {title && ( - - {title} +const Message = ({ title, text, buttons, style }: Props) => { + const theme = useTheme() + return ( + + {title && ( + + {title} + + )} + + {text} - )} - - {text} + {buttons && buttons.length > 0 && ( + + {buttons.map((button, i) => ( + + ))} + + )} - {buttons && buttons.length > 0 && ( - - {buttons.map((button, i) => ( - - ))} - - )} - -) + ) +} export default Message diff --git a/src/components/MoreInfo.tsx b/src/components/MoreInfo.tsx index e4cd23c..d618e0b 100644 --- a/src/components/MoreInfo.tsx +++ b/src/components/MoreInfo.tsx @@ -3,8 +3,8 @@ import { StyleProp, ViewStyle } from 'react-native' import { Icon } from './Icon' import IconText from './IconText' -import { theme } from '../styles' import textStyle from '../styles/text' +import { useTheme } from '../theme/ThemeProvider' type Props = { onPress?: () => void @@ -13,16 +13,19 @@ type Props = { label: string } -const MoreInfo = ({ onPress, containerStyle, label }: Props) => ( - } - text={label} - style={{ - containerStyle, - textStyle: textStyle.link, - }} - onPress={onPress} - /> -) +const MoreInfo = ({ onPress, containerStyle, label }: Props) => { + const theme = useTheme() + return ( + } + text={label} + style={{ + containerStyle, + textStyle: textStyle.link, + }} + onPress={onPress} + /> + ) +} export default MoreInfo diff --git a/src/components/Notification.tsx b/src/components/Notification.tsx index 66fc22a..1f6ba81 100644 --- a/src/components/Notification.tsx +++ b/src/components/Notification.tsx @@ -1,31 +1,35 @@ import React from 'react' import { StyleProp, Text, View, ViewStyle } from 'react-native' -import { font, theme } from '../styles' +import { font } from '../styles' +import { useTheme } from '../theme/ThemeProvider' type Props = { count: number style?: StyleProp } -const Notification = ({ count, style }: Props) => ( - - - {count} - - -) +const Notification = ({ count, style }: Props) => { + const theme = useTheme() + return ( + + + {count} + + + ) +} export default Notification diff --git a/src/components/NotificationPopup.tsx b/src/components/NotificationPopup.tsx index 958adab..7bceb0e 100644 --- a/src/components/NotificationPopup.tsx +++ b/src/components/NotificationPopup.tsx @@ -2,10 +2,11 @@ import React from 'react' import { StyleSheet, Text, TouchableOpacity, View } from 'react-native' import { Icon } from './Icon' +import { Theme } from '../@types/theme' import LargeButton, { LargeButtonProps } from '../components/LargeButton' import Popup from '../components/Popup' -import { theme } from '../styles' import textStyle from '../styles/text' +import { useStyles, useTheme } from '../theme/ThemeProvider' type NotificationPopupStaticProps = { title: string @@ -19,71 +20,76 @@ type NotificationPopupProps = NotificationPopupStaticProps & { visible: boolean } -const NotificationPopup = ({ visible, title, message, leftButton, rightButton, onClose }: NotificationPopupProps) => ( - - - - {title} +const NotificationPopup = ({ visible, title, message, leftButton, rightButton, onClose }: NotificationPopupProps) => { + const theme = useTheme() + const styles = useStyles(createStyles) + return ( + + + + {title} - {onClose && ( - - - - )} - - - {message} - - - - - + {onClose && ( + + + + )} - - {rightButton && } + + {message} + + + + + + + + {rightButton && } + - - -) + + ) +} -const styles = StyleSheet.create({ - mainContainer: { - backgroundColor: 'white', - margin: theme.margin.common, - padding: theme.margin.common, - borderRadius: theme.margin.common, - }, - header: { - flexDirection: 'row', - justifyContent: 'space-between', - }, - title: { - flex: 1, - ...textStyle.title, - }, - closeButton: { - marginLeft: theme.margin.half, - }, - body: { - marginTop: theme.margin.common, - marginBottom: theme.margin.double, - }, - footer: { - flexDirection: 'row', - }, - buttonContainer: { - width: '50%', - }, - leftButton: { - margin: 0, - marginRight: theme.margin.quarter, - }, - rightButton: { - margin: 0, - marginLeft: theme.margin.quarter, - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + mainContainer: { + backgroundColor: 'white', + margin: theme.margin.common, + padding: theme.margin.common, + borderRadius: theme.margin.common, + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + title: { + flex: 1, + ...textStyle.title, + }, + closeButton: { + marginLeft: theme.margin.half, + }, + body: { + marginTop: theme.margin.common, + marginBottom: theme.margin.double, + }, + footer: { + flexDirection: 'row', + }, + buttonContainer: { + width: '50%', + }, + leftButton: { + margin: 0, + marginRight: theme.margin.quarter, + }, + rightButton: { + margin: 0, + marginLeft: theme.margin.quarter, + }, + }) export default NotificationPopup diff --git a/src/components/PageIndicator.tsx b/src/components/PageIndicator.tsx index bb5241c..9cc0b6d 100644 --- a/src/components/PageIndicator.tsx +++ b/src/components/PageIndicator.tsx @@ -1,8 +1,10 @@ import React from 'react' import { StyleSheet, View } from 'react-native' +import { Theme } from '../@types/theme' import Log from '../lib/Log' -import { rounded, theme } from '../styles' +import { rounded } from '../styles' +import { useStyles } from '../theme/ThemeProvider' /** Maximum number of dots to display, should be odd */ const maximumNumberOfDots = 7 @@ -17,6 +19,7 @@ type Props = { const PageIndicator = ({ currentPage, numberOfPages }: Props) => { Log.debug('PageIndicator', currentPage, numberOfPages) + const styles = useStyles(createStyles) const getIndicators = () => { Log.debug('PageIndicator:getIndicators') @@ -64,24 +67,25 @@ const PageIndicator = ({ currentPage, numberOfPages }: Props) => { export default PageIndicator -const styles = StyleSheet.create({ - containerStyle: { - flexDirection: 'row', - alignSelf: 'center', - }, - dot: { - alignSelf: 'center', - aspectRatio: 1, - width: 8, - margin: 4, - ...rounded.normal, - backgroundColor: theme.color.grey500, - }, - smallDot: { - margin: 6, - width: 4, - }, - currentDot: { - backgroundColor: theme.color.white, - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + containerStyle: { + flexDirection: 'row', + alignSelf: 'center', + }, + dot: { + alignSelf: 'center', + aspectRatio: 1, + width: 8, + margin: 4, + ...rounded.normal, + backgroundColor: theme.color.grey500, + }, + smallDot: { + margin: 6, + width: 4, + }, + currentDot: { + backgroundColor: theme.color.white, + }, + }) diff --git a/src/components/Panel.tsx b/src/components/Panel.tsx index a57af7f..bbef9c3 100644 --- a/src/components/Panel.tsx +++ b/src/components/Panel.tsx @@ -1,7 +1,9 @@ import React from 'react' import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native' -import { shadow, theme } from '../styles' +import { Theme } from '../@types/theme' +import { shadow } from '../styles' +import { useStyles } from '../theme/ThemeProvider' type Props = { children?: React.ReactNode @@ -12,6 +14,7 @@ type Props = { /** Display content in a full width panel with a shadow */ const Panel = ({ children, topShadow = true, containerStyle, panelStyle }: Props) => { + const styles = useStyles(createStyles) return ( {children} @@ -21,16 +24,17 @@ const Panel = ({ children, topShadow = true, containerStyle, panelStyle }: Props export default Panel -const styles = StyleSheet.create({ - panelContainer: { - marginTop: theme.margin.quarter, - ...shadow.small.ios, - }, - panel: { - paddingVertical: theme.margin.common, - backgroundColor: theme.color.white, - ...shadow.small.android, - borderTopWidth: 1, - borderColor: theme.color.grey50, - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + panelContainer: { + marginTop: theme.margin.quarter, + ...shadow.small.ios, + }, + panel: { + paddingVertical: theme.margin.common, + backgroundColor: theme.color.white, + ...shadow.small.android, + borderTopWidth: 1, + borderColor: theme.color.grey50, + }, + }) diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx index 4c8e5bc..9039b97 100644 --- a/src/components/Popup.tsx +++ b/src/components/Popup.tsx @@ -3,8 +3,10 @@ import { Modal, StyleSheet, View } from 'react-native' import { BlurView } from '@react-native-community/blur' +import { Theme } from '../@types/theme' import useShowBlurView from '../hooks/useShowBlurView' -import { layout, theme } from '../styles' +import { layout } from '../styles' +import { useStyles } from '../theme/ThemeProvider' type Props = { visible: boolean @@ -13,6 +15,7 @@ type Props = { const Popup = ({ children, visible }: Props) => { const showBlurView = useShowBlurView() + const styles = useStyles(createStyles) return ( @@ -26,17 +29,18 @@ const Popup = ({ children, visible }: Props) => { export default Popup -const styles = StyleSheet.create({ - modalBackground: { - flex: 1, - alignItems: 'stretch', - justifyContent: 'center', - flexDirection: 'column', - backgroundColor: theme.overlay.grey60, - }, - viewPort: { - flexDirection: 'column', - justifyContent: 'center', - backgroundColor: 'transparent', - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + modalBackground: { + flex: 1, + alignItems: 'stretch', + justifyContent: 'center', + flexDirection: 'column', + backgroundColor: theme.overlay.grey60, + }, + viewPort: { + flexDirection: 'column', + justifyContent: 'center', + backgroundColor: 'transparent', + }, + }) diff --git a/src/components/ProgressBarList.tsx b/src/components/ProgressBarList.tsx index 2057d28..2430abe 100644 --- a/src/components/ProgressBarList.tsx +++ b/src/components/ProgressBarList.tsx @@ -2,8 +2,10 @@ import React from 'react' import { StyleSheet, Text, View } from 'react-native' import { Icon } from './Icon' +import { Theme } from '../@types/theme' import ProgressBar from '../data/ProgressBar' -import { font, theme } from '../styles' +import { font } from '../styles' +import { useStyles, useTheme } from '../theme/ThemeProvider' type Props = { progressBars: ProgressBar[] @@ -11,6 +13,8 @@ type Props = { } const ProgressBarList = ({ progressBars, separator }: Props) => { + const theme = useTheme() + const styles = useStyles(createStyles) const getMarginBottom = (i: number) => (i === progressBars.length - 1 ? 0 : borderRadius) // With small percentages (<10%) the gray bar is visible because of the border radius: @@ -107,59 +111,60 @@ const ProgressBarList = ({ progressBars, separator }: Props) => { const barHeight = 16 const borderRadius = barHeight / 2 -const styles = StyleSheet.create({ - mainContainer: { - width: '100%', - flexDirection: 'row', - }, - labelsContainer: { - maxWidth: '50%', - }, - labelContainer: { - justifyContent: 'center', - height: barHeight, - borderTopLeftRadius: borderRadius, - borderBottomLeftRadius: borderRadius, - paddingLeft: borderRadius, - }, - label: { - ...font.small, - color: theme.overlay.white70, - justifyContent: 'center', - lineHeight: barHeight, - }, - barsContainer: { - flex: 1, - }, - greyBar: { - flexDirection: 'row', - borderTopRightRadius: borderRadius, - borderBottomRightRadius: borderRadius, - backgroundColor: theme.color.grey300, - width: '100%', - }, - numbersContainer: { - justifyContent: 'center', - height: barHeight, - borderTopRightRadius: borderRadius, - borderBottomRightRadius: borderRadius, - paddingRight: borderRadius, - }, - denominator: { - ...font.small, - color: theme.overlay.white70, - lineHeight: barHeight, - }, - numerator: { - ...font.smallBold, - color: theme.color.white, - lineHeight: barHeight, - }, - checkContainer: { - justifyContent: 'center', - marginLeft: borderRadius, - height: barHeight, - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + mainContainer: { + width: '100%', + flexDirection: 'row', + }, + labelsContainer: { + maxWidth: '50%', + }, + labelContainer: { + justifyContent: 'center', + height: barHeight, + borderTopLeftRadius: borderRadius, + borderBottomLeftRadius: borderRadius, + paddingLeft: borderRadius, + }, + label: { + ...font.small, + color: theme.overlay.white70, + justifyContent: 'center', + lineHeight: barHeight, + }, + barsContainer: { + flex: 1, + }, + greyBar: { + flexDirection: 'row', + borderTopRightRadius: borderRadius, + borderBottomRightRadius: borderRadius, + backgroundColor: theme.color.grey300, + width: '100%', + }, + numbersContainer: { + justifyContent: 'center', + height: barHeight, + borderTopRightRadius: borderRadius, + borderBottomRightRadius: borderRadius, + paddingRight: borderRadius, + }, + denominator: { + ...font.small, + color: theme.overlay.white70, + lineHeight: barHeight, + }, + numerator: { + ...font.smallBold, + color: theme.color.white, + lineHeight: barHeight, + }, + checkContainer: { + justifyContent: 'center', + marginLeft: borderRadius, + height: barHeight, + }, + }) export default ProgressBarList diff --git a/src/components/Tooltip.tsx b/src/components/Tooltip.tsx index db53847..320e66b 100644 --- a/src/components/Tooltip.tsx +++ b/src/components/Tooltip.tsx @@ -2,9 +2,11 @@ import React from 'react' import { StyleProp, StyleSheet, Text, TouchableOpacity, View, ViewStyle } from 'react-native' import { Icon, IconProps } from './Icon' +import { Theme } from '../@types/theme' import LargeButton, { LargeButtonProps } from '../components/LargeButton' -import { lineHeight, shadow, theme } from '../styles' +import { lineHeight, shadow } from '../styles' import textStyle from '../styles/text' +import { useStyles, useTheme } from '../theme/ThemeProvider' type TooltipProps = { title: string @@ -28,75 +30,81 @@ const Tooltip = ({ style, testID, children, -}: TooltipProps) => ( - - - - - {icon && ( - - +}: TooltipProps) => { + const theme = useTheme() + const styles = useStyles(createStyles) + + return ( + + + + + {icon && ( + + + + )} + + {title} - )} - - {title} + {closable && ( + + + + + + )} + + {children} + + {text} - {closable && ( - - - - + {buttons && buttons.length > 0 && ( + + {buttons.map((button) => ( + + ))} )} - {children} - - {text} - - {buttons && buttons.length > 0 && ( - - {buttons.map((button) => ( - - ))} - - )} - -) + ) +} export default Tooltip export type { TooltipProps } -const styles = StyleSheet.create({ - tooltipContainer: { - borderBottomLeftRadius: 16, - borderBottomRightRadius: 16, - overflow: 'hidden', - backgroundColor: 'white', - ...shadow.normal.android, - }, - tooltip: { - flexDirection: 'column', - margin: theme.margin.common, - }, - iconContainer: { - height: lineHeight.medium, - justifyContent: 'center', - }, -}) +const createStyles = (theme: Theme) => + StyleSheet.create({ + tooltipContainer: { + borderBottomLeftRadius: 16, + borderBottomRightRadius: 16, + overflow: 'hidden', + backgroundColor: 'white', + ...shadow.normal.android, + }, + tooltip: { + flexDirection: 'column', + margin: theme.margin.common, + }, + iconContainer: { + height: lineHeight.medium, + justifyContent: 'center', + }, + }) diff --git a/src/components/WebLink.tsx b/src/components/WebLink.tsx index 5b6bb0d..6d8d945 100644 --- a/src/components/WebLink.tsx +++ b/src/components/WebLink.tsx @@ -3,8 +3,8 @@ import { StyleProp, TextStyle, ViewStyle } from 'react-native' import { Icon } from './Icon' import IconText from './IconText' -import { theme } from '../styles' import textStyles from '../styles/text' +import { useTheme } from '../theme/ThemeProvider' type Props = { onPress: () => void @@ -13,16 +13,19 @@ type Props = { text: string } -const WebLink = ({ onPress, containerStyle, text, textStyle }: Props) => ( - } - text={text} - style={{ - containerStyle, - textStyle: [textStyles.link, textStyle], - }} - onPress={onPress} - /> -) +const WebLink = ({ onPress, containerStyle, text, textStyle }: Props) => { + const theme = useTheme() + return ( + } + text={text} + style={{ + containerStyle, + textStyle: [textStyles.link, textStyle], + }} + onPress={onPress} + /> + ) +} export default WebLink diff --git a/src/components/__tests__/BrandIcon.test.tsx b/src/components/__tests__/BrandIcon.test.tsx index af8fedb..e04c4b3 100644 --- a/src/components/__tests__/BrandIcon.test.tsx +++ b/src/components/__tests__/BrandIcon.test.tsx @@ -2,7 +2,6 @@ import React from 'react' import { render } from '@testing-library/react-native' -import { theme } from '../../styles' import { BrandIcon } from '../BrandIcon' describe('BrandIcon', () => { @@ -13,7 +12,7 @@ describe('BrandIcon', () => { }) test('With color', () => { - const { toJSON } = render() + const { toJSON } = render() expect(toJSON()).toMatchSnapshot() }) diff --git a/src/components/__tests__/FilterButton.test.tsx b/src/components/__tests__/FilterButton.test.tsx deleted file mode 100644 index 4a362cd..0000000 --- a/src/components/__tests__/FilterButton.test.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import { Text } from 'react-native' - -import { fireEvent, render } from '@testing-library/react-native' - -import theme from '../../styles/theme' -import FilterButton from '../FilterButton' - -const content = jest.fn(({}) => Some text) -const onPress = jest.fn() - -describe('FilterButton', () => { - describe('Rendering', () => { - test('Enabled', () => { - const { toJSON } = render() - expect(toJSON()).toMatchSnapshot() - expect(content).toHaveBeenCalledWith({ color: theme.color.white }) - }) - - test('Disabled', () => { - const { toJSON } = render() - expect(toJSON()).toMatchSnapshot() - expect(content).toHaveBeenCalledWith({ color: theme.color.grey800 }) - }) - }) - - describe('Interaction', () => { - test('Click', async () => { - const { getByText } = render( - , - ) - await fireEvent.press(getByText('Some text')) - expect(onPress).toHaveBeenCalled() - }) - }) -}) diff --git a/src/components/__tests__/Message.test.tsx b/src/components/__tests__/Message.test.tsx index 8c2b30d..f9bd790 100644 --- a/src/components/__tests__/Message.test.tsx +++ b/src/components/__tests__/Message.test.tsx @@ -2,7 +2,6 @@ import React from 'react' import { render } from '@testing-library/react-native' -import theme from '../../styles/theme' import Message from '../Message' jest.mock('../../components/LargeButton', () => 'mock-large-button') @@ -37,7 +36,7 @@ describe('Message', () => { test('With style', () => { // GIVEN - const { toJSON } = render() + const { toJSON } = render() // THEN expect(toJSON()).toMatchSnapshot() diff --git a/src/components/__tests__/__snapshots__/BrandIcon.test.tsx.snap b/src/components/__tests__/__snapshots__/BrandIcon.test.tsx.snap index d825207..88289be 100644 --- a/src/components/__tests__/__snapshots__/BrandIcon.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BrandIcon.test.tsx.snap @@ -11,7 +11,7 @@ exports[`BrandIcon Rendering Normal 1`] = ` exports[`BrandIcon Rendering With color 1`] = ` - - Some text - - -`; - -exports[`FilterButton Rendering Enabled 1`] = ` - - - Some text - - -`; diff --git a/src/index.ts b/src/index.ts index 978661e..b7a91b3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,6 @@ import ContentImage from './components/ContentImage' import Date from './components/Date' import Disclose from './components/Disclose' import DocumentLink from './components/DocumentLink' -import FilterButton from './components/FilterButton' import IconButton from './components/IconButton' import IconText from './components/IconText' import IconView from './components/IconView' @@ -34,6 +33,7 @@ import BrandIcons from './lib/BrandIcons' import Icons, { IconName } from './lib/Icons' import { setLogConfiguration } from './lib/Log' import { openUrl } from './lib/Url' +import { ThemeProvider } from './theme/ThemeProvider' export { Accordion, @@ -46,7 +46,6 @@ export { Date, Disclose, DocumentLink, - FilterButton, IconButton, IconText, IconView, @@ -66,6 +65,7 @@ export { ProgressBar, ProgressBarList, TextLink, + ThemeProvider, Tooltip, WebLink, openUrl, @@ -79,3 +79,5 @@ export * from './components/BrandIcon' export type { FontName, FontStyle } from './@types/font' export * from './styles' + +export * from './theme/ThemeProvider' diff --git a/src/lib/LargeButtonStyles.ts b/src/lib/LargeButtonStyles.ts index 1a1db63..ea19dbb 100644 --- a/src/lib/LargeButtonStyles.ts +++ b/src/lib/LargeButtonStyles.ts @@ -1,6 +1,6 @@ import { StyleProp, TextStyle, ViewStyle } from 'react-native' -import { theme } from '../styles' +import { Theme } from '../@types/theme' type LargeButtonStyle = { buttonStyle: StyleProp @@ -8,31 +8,31 @@ type LargeButtonStyle = { iconColor: string } -const primary: LargeButtonStyle = { +const primary = (theme: Theme): LargeButtonStyle => ({ buttonStyle: { backgroundColor: theme.color.primary500, }, textStyle: { color: theme.color.white }, iconColor: theme.color.white, -} +}) -const primaryDisabled: LargeButtonStyle = { +const primaryDisabled = (theme: Theme): LargeButtonStyle => ({ buttonStyle: { backgroundColor: theme.color.grey300, }, textStyle: { color: theme.color.white }, iconColor: theme.color.white, -} +}) -const primaryDanger: LargeButtonStyle = { +const primaryDanger = (theme: Theme): LargeButtonStyle => ({ buttonStyle: { backgroundColor: theme.color.error500, }, textStyle: { color: theme.color.white }, iconColor: theme.color.white, -} +}) -const secondary: LargeButtonStyle = { +const secondary = (theme: Theme): LargeButtonStyle => ({ buttonStyle: { borderColor: theme.color.primary500, borderWidth: 2, @@ -40,9 +40,9 @@ const secondary: LargeButtonStyle = { }, textStyle: { color: theme.color.primary500 }, iconColor: theme.color.primary500, -} +}) -const secondaryDisabled: LargeButtonStyle = { +const secondaryDisabled = (theme: Theme): LargeButtonStyle => ({ buttonStyle: { borderColor: theme.color.grey300, borderWidth: 2, @@ -50,9 +50,9 @@ const secondaryDisabled: LargeButtonStyle = { }, textStyle: { color: theme.color.grey300 }, iconColor: theme.color.grey300, -} +}) -const secondaryDanger: LargeButtonStyle = { +const secondaryDanger = (theme: Theme): LargeButtonStyle => ({ buttonStyle: { borderColor: theme.color.error500, borderWidth: 2, @@ -60,7 +60,7 @@ const secondaryDanger: LargeButtonStyle = { }, textStyle: { color: theme.color.error500 }, iconColor: theme.color.error500, -} +}) export type { LargeButtonStyle } diff --git a/src/theme/ThemeProvider.tsx b/src/theme/ThemeProvider.tsx new file mode 100644 index 0000000..1d45819 --- /dev/null +++ b/src/theme/ThemeProvider.tsx @@ -0,0 +1,18 @@ +/* eslint-disable observation/no-function-without-logging */ +import { createContext, useContext, useMemo } from 'react' + +import { Theme } from '../@types/theme' +import defaultTheme from '../styles/theme' + +const ThemeContext = createContext(defaultTheme) + +export const ThemeProvider = ({ theme, children }: { theme: Theme; children: React.ReactNode }) => { + return {children} +} + +export const useTheme = () => useContext(ThemeContext) + +export const useStyles = (stylesFactory: (theme: Theme) => T): T => { + const theme = useTheme() + return useMemo(() => stylesFactory(theme), [theme]) +}