Skip to content

Commit

Permalink
Mudanças na API do modo escuro (#41)
Browse files Browse the repository at this point in the history
# Contexto
Ao passar a usar as APIs do modo escuro no SGLearner, percebi que a implementação seria mais fácil se fossem feitas algumas mudanças no DS.

# Mudanças
A mudança principal foi na função `toggleTheme`: antes ela simplesmente trocava o tema. Mas a interface que teremos para a escolha não será simplesmente um toggle mas um modal com as opções "claro" e "escuro". Daí achei que seria mais natural se o DS fornecesse uma função para mudar para um tema fornecido. Mudei a `toggleTheme` para uma função `setTheme`.

Houve também outras mudanças menores, que vou explicar com comentários no PR.

# Como testar

* Ver que testes automatizados continuam passando
* Apontar o SGLearner para a branch do DS com as mudanças e ver que é possível usar a `setTheme` e outras APIs
  • Loading branch information
gabrielguilhoto authored Mar 6, 2024
1 parent 9042e69 commit 7f4af01
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 62 deletions.
6 changes: 1 addition & 5 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
export {
DarkModeEnabledProvider,
useDarkMode,
defaultTheme,
} from './themes/DarkModeEnabledContext';
export * from './themes';

export * from './tokens';
export * from './token-presets';
35 changes: 17 additions & 18 deletions themes/DarkModeEnabledContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,31 @@ import {
type ReactNode,
} from 'react';

import { type ThemeType, defaultTheme } from './themes';
import { type Theme, defaultTheme } from './themes';

const storeToggledTheme = async (
currentTheme: ThemeType,
setPersistedTheme: (value: ThemeType) => Promise<void>
currentTheme: Theme,
setPersistedTheme: (value: Theme) => Promise<void>
): Promise<void> => {
await setPersistedTheme(currentTheme);
};

interface DarkModeEnabledContextProps {
theme: ThemeType;
toggleDarkMode: () => void;
theme: Theme;
setTheme: (theme: Theme) => void;
}

const DarkModeEnabledContext = createContext<DarkModeEnabledContextProps>({
theme: defaultTheme,
toggleDarkMode: () => {},
setTheme: () => {},
});

const DarkModeEnabledProvider: React.FC<{
children: ReactNode;
setPersistedTheme: (value: ThemeType) => Promise<void>;
getPersistedTheme: () => Promise<ThemeType | null>;
setPersistedTheme: (value: Theme) => Promise<void>;
getPersistedTheme: () => Promise<Theme | null>;
}> = ({ children, setPersistedTheme, getPersistedTheme }) => {
const [theme, setTheme] = useState<ThemeType>(defaultTheme);
const [theme, setTheme] = useState<Theme>(defaultTheme);

useEffect(() => {
const setStoredThemeAsDefault = async (): Promise<void> => {
Expand All @@ -45,18 +45,17 @@ const DarkModeEnabledProvider: React.FC<{
});
}, []);

const toggleDarkMode = (): void => {
setTheme((prevTheme) => {
const currentTheme = prevTheme === 'light' ? 'dark' : 'light';
storeToggledTheme(currentTheme, setPersistedTheme).catch((e) => {
console.log(e);
});
return currentTheme;
const setAndStoreTheme = (theme: Theme): void => {
setTheme(theme);
storeToggledTheme(theme, setPersistedTheme).catch((e) => {
console.log(e);
});
};

return (
<DarkModeEnabledContext.Provider value={{ theme, toggleDarkMode }}>
<DarkModeEnabledContext.Provider
value={{ theme, setTheme: setAndStoreTheme }}
>
{children}
</DarkModeEnabledContext.Provider>
);
Expand All @@ -65,4 +64,4 @@ const DarkModeEnabledProvider: React.FC<{
const useDarkMode = (): DarkModeEnabledContextProps =>
useContext(DarkModeEnabledContext);

export { DarkModeEnabledProvider, useDarkMode, defaultTheme };
export { DarkModeEnabledContext, DarkModeEnabledProvider, useDarkMode };
51 changes: 25 additions & 26 deletions themes/__tests__/DarkModeEnabledContext.test.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import React from 'react';
import AsyncStorage from '@react-native-community/async-storage';
import { act, fireEvent, render, screen } from '@testing-library/react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import {
DarkModeEnabledProvider,
defaultTheme,
useDarkMode,
} from '../DarkModeEnabledContext';
import { type ThemeType } from '../themes';
import { type Theme, defaultTheme } from '../themes';

const ThemeDisplay = (): React.JSX.Element => {
const { theme } = useDarkMode();
return <p>Tema atual: {theme}</p>;
};

const ThemeToggle = (): React.JSX.Element => {
const { toggleDarkMode } = useDarkMode();
return <button onClick={toggleDarkMode}>Trocar de tema</button>;
const { theme, setTheme } = useDarkMode();
return (
<button
onClick={() => {
setTheme(theme === 'light' ? 'dark' : 'light');
}}
>
Trocar de tema
</button>
);
};

const getPersistedTheme = async (): Promise<ThemeType | null> => {
return (await AsyncStorage.getItem('dsa_theme')) as ThemeType | null;
const getPersistedTheme = async (): Promise<Theme | null> => {
return (await AsyncStorage.getItem('dsa_theme')) as Theme | null;
};

const setPersistedTheme = async (value: ThemeType): Promise<void> => {
const setPersistedTheme = async (value: Theme): Promise<void> => {
await AsyncStorage.setItem('dsa_theme', value);
};

Expand Down Expand Up @@ -72,27 +79,19 @@ test('DarkModeEnabledProvider theme is stored at toggle', async () => {
});

test('DarkModeEnabledProvider uses stored theme as current', async () => {
await AsyncStorage.setItem('dsa_theme', 'dark').then(async () => {
await AsyncStorage.getItem('dsa_theme').then(async (theme) => {
await act(async () => customRender()).then(() => {
expect(theme).toBe('dark');

setTimeout(() => {
expect(screen.getByText(/^Tema atual:/)).toHaveTextContent(
`Tema atual: dark`
);
}, 500);
});
});
await AsyncStorage.setItem('dsa_theme', 'dark');

const component = customRender();
await waitFor(() => {
expect(component.container).toHaveTextContent('Tema atual: dark');
});

fireEvent.click(screen.getByText(/^Trocar de tema/));
expect(screen.getByText(/^Tema atual:/)).toHaveTextContent(
`Tema atual: light`
fireEvent.click(component.getByText(/^Trocar de tema/));
expect(component.getByText(/^Tema atual:/)).toHaveTextContent(
'Tema atual: light'
);
await AsyncStorage.getItem('dsa_theme').then((theme) => {
expect(theme).toBe('light');
});

expect(await AsyncStorage.getItem('dsa_theme')).toBe('light');
});

test('DarkModeEnabledProvider toggle back theme to light and persist correctly', async () => {
Expand Down
6 changes: 3 additions & 3 deletions themes/getThemeToken.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as defaultTokens from '../built-tokens/js/semantic-tokens';
import * as darkTokens from '../built-tokens/js/dark-tokens';
import * as lightTokens from '../built-tokens/js/light-tokens';
import type { ThemeType } from './themes';
import type { Theme } from './themes';

function getThemeToken(
theme: ThemeType,
theme: Theme,
tokenName: keyof typeof darkTokens
): string {
if (theme === 'dark') {
Expand All @@ -16,7 +16,7 @@ function getThemeToken(
return defaultTokens[tokenName];
}

export const getThemeTokenValue = (token: string, theme: ThemeType): string => {
export const getThemeTokenValue = (token: string, theme: Theme): string => {
if (typeof token === 'string' && token.includes('DSA_COLOR')) {
const regexpSize =
/(DSA_COLOR_BACKGROUND_|DSA_COLOR_TEXT_|DSA_COLOR_BORDER_|DSA_COLOR_SPECIALPAGES_|DSA_COLOR_SUBJECTS_|DSA_COLOR_CONTENTBOX_|DSA_COLOR_HIGHLIGHT_|DSA_COLOR_BUTTON_|DSA_COLOR_FEEDBACK_)\w+/;
Expand Down
7 changes: 7 additions & 0 deletions themes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export {
DarkModeEnabledContext,
DarkModeEnabledProvider,
useDarkMode,
} from './DarkModeEnabledContext';

export { type Theme, themes, labelsByTheme, defaultTheme } from './themes';
8 changes: 5 additions & 3 deletions themes/themes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export type ThemeType = 'dark' | 'light';
export type Theme = 'dark' | 'light';

export const themes: ThemeType[] = ['dark', 'light'];
export const themes: Theme[] = ['dark', 'light'];

export const defaultTheme: ThemeType = 'light';
export const labelsByTheme = { dark: 'Escuro', light: 'Claro' };

export const defaultTheme: Theme = 'light';
16 changes: 9 additions & 7 deletions utils/CustomDesignTokenDocBlock/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import {
DarkModeEnabledProvider,
useDarkMode,
} from '../../themes/DarkModeEnabledContext';
import { type ThemeType } from '../../themes/themes';
import { type Theme } from '../../themes/themes';
import * as allTokens from '../../built-tokens/js/tokens';
import AsyncStorage from '@react-native-community/async-storage';

type FontStyle = 'normal' | 'italic' | 'oblique';

interface Token {
name: string;
value: FontStyle | ThemeType | string;
value: FontStyle | Theme | string;
}
interface CustomDesignTokenDocBlockProps {
blockType: 'table';
Expand All @@ -33,7 +33,7 @@ interface ThemeSwitchProps {

const ThemeSwitch: React.FC<ThemeSwitchProps> = (props) => {
const { hideTitle } = props;
const { theme, toggleDarkMode } = useDarkMode();
const { theme, setTheme } = useDarkMode();

return (
<div className="theme-switch">
Expand All @@ -48,7 +48,9 @@ const ThemeSwitch: React.FC<ThemeSwitchProps> = (props) => {
type="checkbox"
id="switch"
checked={theme === 'dark'}
onChange={toggleDarkMode}
onChange={() => {
setTheme(theme === 'light' ? 'dark' : 'light');
}}
/>
<label htmlFor="switch">Ativar modo escuro</label>
</div>
Expand Down Expand Up @@ -238,11 +240,11 @@ const CustomDesignTokenDocBlock: React.FC<CustomDesignTokenDocBlockProps> = (
) => {
const { previewType } = props;

const getPersistedTheme = async (): Promise<ThemeType | null> => {
return (await AsyncStorage.getItem('dsa_theme')) as ThemeType | null;
const getPersistedTheme = async (): Promise<Theme | null> => {
return (await AsyncStorage.getItem('dsa_theme')) as Theme | null;
};

const setPersistedTheme = async (value: ThemeType): Promise<void> => {
const setPersistedTheme = async (value: Theme): Promise<void> => {
await AsyncStorage.setItem('dsa_theme', value);
};

Expand Down

0 comments on commit 7f4af01

Please sign in to comment.