Skip to content

Commit

Permalink
Merge pull request #32569 from margelo/@chrispader/theme-switching-es…
Browse files Browse the repository at this point in the history
…lint-rules

Theme switching ESLint rules
  • Loading branch information
grgia authored Jan 5, 2024
2 parents 095161a + 99ca991 commit 2d455bf
Show file tree
Hide file tree
Showing 45 changed files with 320 additions and 227 deletions.
32 changes: 32 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,45 @@ const restrictedImportPaths = [
importNames: ['CSSProperties'],
message: "Please use 'ViewStyle', 'TextStyle', 'ImageStyle' from 'react-native' instead.",
},
{
name: '@styles/index',
importNames: ['default', 'defaultStyles'],
message: 'Do not import styles directly. Please use the `useThemeStyles` hook or `withThemeStyles` HOC instead.',
},
{
name: '@styles/utils',
importNames: ['default', 'DefaultStyleUtils'],
message: 'Do not import StyleUtils directly. Please use the `useStyleUtils` hook or `withStyleUtils` HOC instead.',
},
{
name: '@styles/theme',
importNames: ['default', 'defaultTheme'],

message: 'Do not import themes directly. Please use the `useTheme` hook or `withTheme` HOC instead.',
},
{
name: '@styles/theme/illustrations',
message: 'Do not import theme illustrations directly. Please use the `useThemeIllustrations` hook instead.',
},
];

const restrictedImportPatterns = [
{
group: ['**/assets/animations/**/*.json'],
message: "Do not import animations directly. Please use the 'src/components/LottieAnimations' import instead.",
},
{
group: ['@styles/theme/themes/**'],
message: 'Do not import themes directly. Please use the `useTheme` hook or `withTheme` HOC instead.',
},
{
group: ['@styles/utils/**', '!@styles/utils/FontUtils', '!@styles/utils/types'],
message: 'Do not import style util functions directly. Please use the `useStyleUtils` hook or `withStyleUtils` HOC instead.',
},
{
group: ['@styles/theme/illustrations/themes/**'],
message: 'Do not import theme illustrations directly. Please use the `useThemeIllustrations` hook instead.',
},
];

module.exports = {
Expand Down
4 changes: 2 additions & 2 deletions src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {defaultHTMLElementModels, RenderHTMLConfigProvider, TRenderEngineProvide
import _ from 'underscore';
import useThemeStyles from '@hooks/useThemeStyles';
import convertToLTR from '@libs/convertToLTR';
import singleFontFamily from '@styles/utils/fontFamily/singleFontFamily';
import FontUtils from '@styles/utils/FontUtils';
import * as HTMLEngineUtils from './htmlEngineUtils';
import htmlRenderers from './HTMLRenderers';

Expand Down Expand Up @@ -82,7 +82,7 @@ function BaseHTMLEngineProvider(props) {
baseStyle={styles.webViewStyles.baseFontStyle}
tagsStyles={styles.webViewStyles.tagStyles}
enableCSSInlineProcessing={false}
systemFonts={_.values(singleFontFamily)}
systemFonts={_.values(FontUtils.fontFamily.single)}
domVisitors={{
// eslint-disable-next-line no-param-reassign
onText: (text) => (text.data = convertToLTR(text.data)),
Expand Down
11 changes: 5 additions & 6 deletions src/components/Onfido/BaseOnfidoWeb.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import _ from 'underscore';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import Log from '@libs/Log';
import fontFamily from '@styles/utils/fontFamily';
import fontWeightBold from '@styles/utils/fontWeight/bold';
import FontUtils from '@styles/utils/FontUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import './index.css';
Expand All @@ -18,11 +17,11 @@ function initializeOnfido({sdkToken, onSuccess, onError, onUserExit, preferredLo
containerId: CONST.ONFIDO.CONTAINER_ID,
useMemoryHistory: true,
customUI: {
fontFamilyTitle: `${fontFamily.EXP_NEUE}, -apple-system, serif`,
fontFamilySubtitle: `${fontFamily.EXP_NEUE}, -apple-system, serif`,
fontFamilyBody: `${fontFamily.EXP_NEUE}, -apple-system, serif`,
fontFamilyTitle: `${FontUtils.fontFamily.platform.EXP_NEUE}, -apple-system, serif`,
fontFamilySubtitle: `${FontUtils.fontFamily.platform.EXP_NEUE}, -apple-system, serif`,
fontFamilyBody: `${FontUtils.fontFamily.platform.EXP_NEUE}, -apple-system, serif`,
fontSizeTitle: `${variables.fontSizeLarge}px`,
fontWeightTitle: fontWeightBold,
fontWeightTitle: FontUtils.fontWeight.bold,
fontWeightSubtitle: 400,
fontSizeSubtitle: `${variables.fontSizeNormal}px`,
colorContentTitle: theme.text,
Expand Down
7 changes: 4 additions & 3 deletions src/components/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import React from 'react';
import {Text as RNText, StyleSheet} from 'react-native';
import type {TextProps as RNTextProps, TextStyle} from 'react-native';
import useTheme from '@hooks/useTheme';
import fontFamily from '@styles/utils/fontFamily';
import type {FontUtilsType} from '@styles/utils/FontUtils';
import FontUtils from '@styles/utils/FontUtils';
import variables from '@styles/variables';
import type ChildrenProps from '@src/types/utils/ChildrenProps';

Expand All @@ -22,7 +23,7 @@ type TextProps = RNTextProps &
children: React.ReactNode;

/** The family of the font to use */
family?: keyof typeof fontFamily;
family?: keyof FontUtilsType['fontFamily']['platform'];
};

function Text({color, fontSize = variables.fontSizeNormal, textAlign = 'left', children, family = 'EXP_NEUE', style = {}, ...props}: TextProps, ref: ForwardedRef<RNText>) {
Expand All @@ -32,7 +33,7 @@ function Text({color, fontSize = variables.fontSizeNormal, textAlign = 'left', c
color: color ?? theme.text,
fontSize,
textAlign,
fontFamily: fontFamily[family],
fontFamily: FontUtils.fontFamily.platform[family],
...StyleSheet.flatten(style),
};

Expand Down
7 changes: 4 additions & 3 deletions src/components/ThemeIllustrationsProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, {useMemo} from 'react';
import useThemePreference from '@hooks/useThemePreference';
import ThemeIllustrationsContext from '@styles/theme/context/ThemeIllustrationsContext';
import Illustrations from '@styles/theme/illustrations';
// eslint-disable-next-line no-restricted-imports
import illustrations from '@styles/theme/illustrations';

type ThemeIllustrationsProviderProps = {
children: React.ReactNode;
Expand All @@ -10,9 +11,9 @@ type ThemeIllustrationsProviderProps = {
function ThemeIllustrationsProvider({children}: ThemeIllustrationsProviderProps) {
const themePreference = useThemePreference();

const illustrations = useMemo(() => Illustrations[themePreference], [themePreference]);
const themeIllustrations = useMemo(() => illustrations[themePreference], [themePreference]);

return <ThemeIllustrationsContext.Provider value={illustrations}>{children}</ThemeIllustrationsContext.Provider>;
return <ThemeIllustrationsContext.Provider value={themeIllustrations}>{children}</ThemeIllustrationsContext.Provider>;
}

ThemeIllustrationsProvider.displayName = 'ThemeIllustrationsProvider';
Expand Down
1 change: 1 addition & 0 deletions src/components/ThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import PropTypes from 'prop-types';
import React, {useMemo} from 'react';
import useThemePreferenceWithStaticOverride from '@hooks/useThemePreferenceWithStaticOverride';
// eslint-disable-next-line no-restricted-imports
import themes from '@styles/theme';
import ThemeContext from '@styles/theme/context/ThemeContext';
import type {ThemePreferenceWithoutSystem} from '@styles/theme/types';
Expand Down
10 changes: 6 additions & 4 deletions src/components/ThemeStylesProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import React, {useMemo} from 'react';
import useTheme from '@hooks/useTheme';
import stylesGenerator from '@styles/index';
// eslint-disable-next-line no-restricted-imports
import styles from '@styles/index';
import ThemeStylesContext from '@styles/theme/context/ThemeStylesContext';
// eslint-disable-next-line no-restricted-imports
import createStyleUtils from '@styles/utils';

type ThemeStylesProviderProps = React.PropsWithChildren;

function ThemeStylesProvider({children}: ThemeStylesProviderProps) {
const theme = useTheme();

const styles = useMemo(() => stylesGenerator(theme), [theme]);
const StyleUtils = useMemo(() => createStyleUtils(theme, styles), [theme, styles]);
const contextValue = useMemo(() => ({styles, StyleUtils}), [styles, StyleUtils]);
const themeStyles = useMemo(() => styles(theme), [theme]);
const StyleUtils = useMemo(() => createStyleUtils(theme, themeStyles), [theme, themeStyles]);
const contextValue = useMemo(() => ({styles: themeStyles, StyleUtils}), [themeStyles, StyleUtils]);

return <ThemeStylesContext.Provider value={contextValue}>{children}</ThemeStylesContext.Provider>;
}
Expand Down
4 changes: 3 additions & 1 deletion src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {memo, useEffect, useRef} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import Onyx, {withOnyx} from 'react-native-onyx';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import KeyboardShortcut from '@libs/KeyboardShortcut';
Expand Down Expand Up @@ -130,8 +131,9 @@ const modalScreenListeners = {

function AuthScreens({lastUpdateIDAppliedToClient, session, lastOpenedPublicRoomID, isUsingMemoryOnlyKeys = false}: AuthScreensProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {isSmallScreenWidth} = useWindowDimensions();
const screenOptions = getRootNavigatorScreenOptions(isSmallScreenWidth, styles);
const screenOptions = getRootNavigatorScreenOptions(isSmallScreenWidth, styles, StyleUtils);
const isInitialRender = useRef(true);

if (isInitialRender.current) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type {StackCardInterpolatedStyle, StackCardInterpolationProps} from '@react-navigation/stack';
import {Animated} from 'react-native';
import type {StyleUtilsType} from '@styles/utils';
import variables from '@styles/variables';

type ModalCardStyleInterpolator = (
isSmallScreenWidth: boolean,
isFullScreenModal: boolean,
stackCardInterpolationProps: StackCardInterpolationProps,
outputRangeMultiplier?: number,
) => StackCardInterpolatedStyle;
type CreateModalCardStyleInterpolator = (StyleUtils: StyleUtilsType) => ModalCardStyleInterpolator;

const createModalCardStyleInterpolator: CreateModalCardStyleInterpolator =
(StyleUtils) =>
(isSmallScreenWidth, isFullScreenModal, {current: {progress}, inverted, layouts: {screen}}, outputRangeMultiplier = 1) => {
const translateX = Animated.multiply(
progress.interpolate({
inputRange: [0, 1],
outputRange: [outputRangeMultiplier * (isSmallScreenWidth ? screen.width : variables.sideBarWidth), 0],
extrapolate: 'clamp',
}),
inverted,
);

const cardStyle = StyleUtils.getCardStyles(screen.width);

if (!isFullScreenModal || isSmallScreenWidth) {
cardStyle.transform = [{translateX}];
}

return {
containerStyle: {
overflow: 'hidden',
},
cardStyle,
};
};

export default createModalCardStyleInterpolator;
138 changes: 73 additions & 65 deletions src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type {StackCardInterpolationProps, StackNavigationOptions} from '@react-navigation/stack';
import type {ThemeStyles} from '@styles/index';
import getNavigationModalCardStyle from '@styles/utils/getNavigationModalCardStyles';
import type {StyleUtilsType} from '@styles/utils';
import variables from '@styles/variables';
import CONFIG from '@src/CONFIG';
import modalCardStyleInterpolator from './modalCardStyleInterpolator';
import createModalCardStyleInterpolator from './createModalCardStyleInterpolator';

type ScreenOptions = Record<string, StackNavigationOptions>;

Expand All @@ -17,75 +17,83 @@ const commonScreenOptions: StackNavigationOptions = {

const SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER = -1;

export default (isSmallScreenWidth: boolean, themeStyles: ThemeStyles): ScreenOptions => ({
rightModalNavigator: {
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props),
presentation: 'transparentModal',

// We want pop in RHP since there are some flows that would work weird otherwise
animationTypeForReplace: 'pop',
cardStyle: {
...getNavigationModalCardStyle(),

// This is necessary to cover translated sidebar with overlay.
width: isSmallScreenWidth ? '100%' : '200%',
// Excess space should be on the left so we need to position from right.
right: 0,
type GetRootNavigatorScreenOptions = (isSmallScreenWidth: boolean, styles: ThemeStyles, StyleUtils: StyleUtilsType) => ScreenOptions;

const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScreenWidth, themeStyles, StyleUtils) => {
const modalCardStyleInterpolator = createModalCardStyleInterpolator(StyleUtils);

return {
rightModalNavigator: {
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props),
presentation: 'transparentModal',

// We want pop in RHP since there are some flows that would work weird otherwise
animationTypeForReplace: 'pop',
cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),

// This is necessary to cover translated sidebar with overlay.
width: isSmallScreenWidth ? '100%' : '200%',
// Excess space should be on the left so we need to position from right.
right: 0,
},
},
},
leftModalNavigator: {
...commonScreenOptions,
cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props, SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER),
presentation: 'transparentModal',

// We want pop in LHP since there are some flows that would work weird otherwise
animationTypeForReplace: 'pop',
cardStyle: {
...getNavigationModalCardStyle(),

// This is necessary to cover translated sidebar with overlay.
width: isSmallScreenWidth ? '100%' : '200%',

// LHP should be displayed in place of the sidebar
left: isSmallScreenWidth ? 0 : -variables.sideBarWidth,
leftModalNavigator: {
...commonScreenOptions,
cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props, SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER),
presentation: 'transparentModal',

// We want pop in LHP since there are some flows that would work weird otherwise
animationTypeForReplace: 'pop',
cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),

// This is necessary to cover translated sidebar with overlay.
width: isSmallScreenWidth ? '100%' : '200%',

// LHP should be displayed in place of the sidebar
left: isSmallScreenWidth ? 0 : -variables.sideBarWidth,
},
},
},
homeScreen: {
title: CONFIG.SITE_TITLE,
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props),

cardStyle: {
...getNavigationModalCardStyle(),
width: isSmallScreenWidth ? '100%' : variables.sideBarWidth,

// We need to shift the sidebar to not be covered by the StackNavigator so it can be clickable.
marginLeft: isSmallScreenWidth ? 0 : -variables.sideBarWidth,
...(isSmallScreenWidth ? {} : themeStyles.borderRight),
homeScreen: {
title: CONFIG.SITE_TITLE,
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, false, props),

cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),
width: isSmallScreenWidth ? '100%' : variables.sideBarWidth,

// We need to shift the sidebar to not be covered by the StackNavigator so it can be clickable.
marginLeft: isSmallScreenWidth ? 0 : -variables.sideBarWidth,
...(isSmallScreenWidth ? {} : themeStyles.borderRight),
},
},
},

fullScreen: {
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, props),
cardStyle: {
...getNavigationModalCardStyle(),
fullScreen: {
...commonScreenOptions,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, props),
cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),

// This is necessary to cover whole screen. Including translated sidebar.
marginLeft: isSmallScreenWidth ? 0 : -variables.sideBarWidth,
// This is necessary to cover whole screen. Including translated sidebar.
marginLeft: isSmallScreenWidth ? 0 : -variables.sideBarWidth,
},
},
},

centralPaneNavigator: {
title: CONFIG.SITE_TITLE,
...commonScreenOptions,
animationEnabled: isSmallScreenWidth,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, props),
centralPaneNavigator: {
title: CONFIG.SITE_TITLE,
...commonScreenOptions,
animationEnabled: isSmallScreenWidth,
cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator(isSmallScreenWidth, true, props),

cardStyle: {
...getNavigationModalCardStyle(),
paddingRight: isSmallScreenWidth ? 0 : variables.sideBarWidth,
cardStyle: {
...StyleUtils.getNavigationModalCardStyle(),
paddingRight: isSmallScreenWidth ? 0 : variables.sideBarWidth,
},
},
},
});
};
};

export default getRootNavigatorScreenOptions;
Loading

0 comments on commit 2d455bf

Please sign in to comment.