Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[docs] Fix useStorageState regressions #41223

Merged
merged 28 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
edc3d33
[utils] Port `useLocalStorageState` hook from Toolpad (#41096)
Janpot Feb 21, 2024
babbcf8
Fix useStorageState regressions
Janpot Feb 21, 2024
3111b22
Update useLocalStorageState.ts
Janpot Feb 21, 2024
d5c5b00
Update ThemeModeToggle.tsx
Janpot Feb 21, 2024
ee0901e
Update ThemeModeToggle.tsx
Janpot Feb 21, 2024
977f7ed
Update useLocalStorageState.ts
Janpot Feb 21, 2024
ac7599c
Update useLocalStorageState.ts
Janpot Feb 21, 2024
4038038
Update useLocalStorageState.ts
Janpot Feb 21, 2024
0f37e1f
Update useLocalStorageState.ts
Janpot Feb 21, 2024
ebdf2bc
convention; no blank space here
oliviertassinari Feb 24, 2024
39ef9ba
convention
oliviertassinari Feb 24, 2024
4bfc8f6
need to return null for the first render as we migh need special hand…
oliviertassinari Feb 24, 2024
6041a93
simplify the logic
oliviertassinari Feb 24, 2024
c22dc5c
add no-ssr
oliviertassinari Feb 24, 2024
f234945
early exit for duplicated work
oliviertassinari Feb 24, 2024
6e58a21
fix lousy code hygiene
oliviertassinari Feb 24, 2024
9f9ddc3
omg really
oliviertassinari Feb 24, 2024
d49bc1d
remove console logs
oliviertassinari Feb 24, 2024
3f94ebf
group related logic
oliviertassinari Feb 24, 2024
b6c1a28
leave comments to make sure future iteration will take steps toward t…
oliviertassinari Feb 24, 2024
c209835
good to go
oliviertassinari Feb 24, 2024
6c9b1e9
fix ci
oliviertassinari Feb 24, 2024
a1a54fd
Merge branch 'heads/upstream/master' into storageRegs
Janpot Feb 27, 2024
c363951
Merge branch 'heads/upstream/master' into storageRegs
Janpot Mar 12, 2024
dd935db
Fix types
Janpot Mar 12, 2024
cd4e274
update
Janpot Mar 12, 2024
371978b
Update comment
Janpot Mar 12, 2024
e217b3d
Update useLocalStorageState.ts
Janpot Mar 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 39 additions & 48 deletions docs/src/components/header/ThemeModeToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,25 @@ import DarkModeOutlined from '@mui/icons-material/DarkModeOutlined';
import LightModeOutlined from '@mui/icons-material/LightModeOutlined';
import useMediaQuery from '@mui/material/useMediaQuery';
import { useChangeTheme } from 'docs/src/modules/components/ThemeContext';
import useLocalStorageState from '@mui/utils/useLocalStorageState';

function CssVarsModeToggle(props: { onChange: (checked: boolean) => void }) {
const [mounted, setMounted] = React.useState(false);
function CssVarsModeToggle(props: { onChange: (newMode: string) => void }) {
const { mode, systemMode, setMode } = useColorScheme();
React.useEffect(() => {
setMounted(true);
}, []);
const calculatedMode = mode === 'system' ? systemMode : mode;

return (
<Tooltip title={calculatedMode === 'dark' ? 'Turn on the light' : 'Turn off the light'}>
<IconButton
color="primary"
disableTouchRipple
disabled={!calculatedMode}
onClick={() => {
props.onChange(calculatedMode === 'light');
setMode(calculatedMode === 'dark' ? 'light' : 'dark');
const newMode = calculatedMode === 'dark' ? 'light' : 'dark';
props.onChange(newMode);
setMode(newMode);
}}
>
{!calculatedMode || !mounted
{!calculatedMode
? null
: {
light: <DarkModeOutlined fontSize="small" />,
Expand All @@ -37,55 +36,47 @@ function CssVarsModeToggle(props: { onChange: (checked: boolean) => void }) {
}

export default function ThemeModeToggle() {
const theme = useTheme();
// TODO implement the dark mode toggle with useColorScheme().
// This already take cares of locale storage and media query.
const changeTheme = useChangeTheme();
const [mode, setMode] = React.useState<string | null>(null);
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const [mode, setMode] = useLocalStorageState('mui-mode', 'system');
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)', { noSsr: true });
const systemMode = prefersDarkMode ? 'dark' : 'light';
const calculatedMode = mode === 'system' ? systemMode : mode;

// TODO remove, the module where "changeTheme() is imported from" should use useColorScheme() to set the mode directly.
// Delegating this to the UI component is wrong.
React.useEffect(() => {
let initialMode = 'system';
try {
initialMode = localStorage.getItem('mui-mode') || initialMode;
} catch (error) {
// do nothing
}
setMode(initialMode);
}, []);

const handleChangeThemeMode = (checked: boolean) => {
const paletteMode = checked ? 'dark' : 'light';
setMode(paletteMode);
changeTheme({ paletteMode: calculatedMode });
}, [changeTheme, calculatedMode]);

try {
localStorage.setItem('mui-mode', paletteMode); // syncing with homepage, can be removed once all pages are migrated to CSS variables
} catch (error) {
// do nothing
}
changeTheme({ paletteMode });
};
const theme = useTheme();

// Server-side hydration
if (mode === null) {
return <IconButton color="primary" disableTouchRipple />;
}

if (theme.vars) {
// Temporarily renders conditionally because `useColorScheme` could not be used in the pages that haven't migrated to CSS theme variables.
return <CssVarsModeToggle onChange={handleChangeThemeMode} />;
// TODO remove this code branch, all pages should be migrated to use CssVarsProvider
if (!theme.vars) {
return (
<Tooltip title={calculatedMode === 'dark' ? 'Turn on the light' : 'Turn off the light'}>
<IconButton
color="primary"
disableTouchRipple
onClick={() => {
setMode(calculatedMode === 'dark' ? 'light' : 'dark');
}}
>
{calculatedMode === 'dark' ? (
<LightModeOutlined fontSize="small" />
) : (
<DarkModeOutlined fontSize="small" />
)}
</IconButton>
</Tooltip>
);
}

const checked = mode === 'system' ? prefersDarkMode : mode === 'dark';

return (
<Tooltip title={checked ? 'Turn on the light' : 'Turn off the light'}>
<IconButton
color="primary"
disableTouchRipple
onClick={() => {
handleChangeThemeMode(!checked);
}}
>
{checked ? <LightModeOutlined fontSize="small" /> : <DarkModeOutlined fontSize="small" />}
</IconButton>
</Tooltip>
);
return <CssVarsModeToggle onChange={setMode} />;
}
1 change: 0 additions & 1 deletion docs/src/layouts/AppHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ interface AppHeaderProps {

export default function AppHeader(props: AppHeaderProps) {
const { gitHubRepository = 'https://github.com/mui' } = props;

const t = useTranslate();

return (
Expand Down
46 changes: 13 additions & 33 deletions docs/src/modules/components/AppSettingsDrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import FormatTextdirectionLToRIcon from '@mui/icons-material/FormatTextdirection
import FormatTextdirectionRToLIcon from '@mui/icons-material/FormatTextdirectionRToL';
import { useChangeTheme } from 'docs/src/modules/components/ThemeContext';
import { useTranslate } from '@mui/docs/i18n';
import useLocalStorageState from '@mui/utils/useLocalStorageState';

const Heading = styled(Typography)(({ theme }) => ({
margin: '20px 0 10px',
Expand All @@ -37,55 +38,36 @@ const IconToggleButton = styled(ToggleButton)({
},
});

function AppSettingsDrawer(props) {
export default function AppSettingsDrawer(props) {
const { onClose, open = false, ...other } = props;
const t = useTranslate();
const upperTheme = useTheme();
const changeTheme = useChangeTheme();
const [mode, setMode] = React.useState(null);
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const preferredMode = prefersDarkMode ? 'dark' : 'light';

// TODO implement the dark mode toggle with useColorScheme().
// This already take cares of locale storage and media query.
const [mode, setMode] = useLocalStorageState('mui-mode', 'system');
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)', { noSsr: true });
const systemMode = prefersDarkMode ? 'dark' : 'light';
const calculatedMode = mode === 'system' ? systemMode : mode;

// TODO remove, where changeTheme() is imported from should use useColorScheme().
// Delegating this to the UI component is wrong.
React.useEffect(() => {
// syncing with homepage, can be removed once all pages are migrated to CSS variables
let initialMode = 'system';
try {
initialMode = localStorage.getItem('mui-mode') || initialMode;
} catch (error) {
// do nothing
}
setMode(initialMode);
}, [preferredMode]);
changeTheme({ paletteMode: calculatedMode });
}, [changeTheme, calculatedMode]);

const handleChangeThemeMode = (event, paletteMode) => {
if (paletteMode === null) {
return;
}

setMode(paletteMode);

if (paletteMode === 'system') {
try {
localStorage.setItem('mui-mode', 'system'); // syncing with homepage, can be removed once all pages are migrated to CSS variables
} catch (error) {
// thrown when cookies are disabled.
}
changeTheme({ paletteMode: preferredMode });
} else {
try {
localStorage.setItem('mui-mode', paletteMode); // syncing with homepage, can be removed once all pages are migrated to CSS variables
} catch (error) {
// thrown when cookies are disabled.
}
changeTheme({ paletteMode });
}
};

const handleChangeDirection = (event, direction) => {
if (direction === null) {
direction = upperTheme.direction;
}

changeTheme({ direction });
};

Expand Down Expand Up @@ -200,5 +182,3 @@ AppSettingsDrawer.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool,
};

export default AppSettingsDrawer;
40 changes: 11 additions & 29 deletions docs/src/modules/components/HighlightedCodeWithTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Tabs, TabsOwnProps } from '@mui/base/Tabs';
import { TabsList as TabsListBase } from '@mui/base/TabsList';
import { TabPanel as TabPanelBase } from '@mui/base/TabPanel';
import { Tab as TabBase } from '@mui/base/Tab';
import useLocalStorageState from '@mui/utils/useLocalStorageState';
import HighlightedCode from './HighlightedCode';

const TabList = styled(TabsListBase)(({ theme }) => ({
Expand Down Expand Up @@ -77,44 +78,25 @@ type TabsConfig = {
language: string;
tab: string;
};
export default function HighlightedCodeWithTabs({
tabs,
storageKey,
}: {
tabs: Array<TabsConfig>;
storageKey?: string;
} & Record<string, any>) {

export default function HighlightedCodeWithTabs(
props: {
tabs: Array<TabsConfig>;
storageKey?: string;
} & Record<string, any>,
) {
const { tabs, storageKey } = props;
const availableTabs = React.useMemo(() => tabs.map(({ tab }) => tab), [tabs]);
const [activeTab, setActiveTab] = React.useState(availableTabs[0]);
const [activeTab, setActiveTab] = useLocalStorageState(storageKey ?? null, availableTabs[0]);

const [mounted, setMounted] = React.useState(false);

React.useEffect(() => {
try {
setActiveTab((prev) => {
if (storageKey === undefined) {
return prev;
}
const storedValues = localStorage.getItem(storageKey);

return storedValues && availableTabs.includes(storedValues) ? storedValues : prev;
});
} catch (error) {
// ignore error
}
setMounted(true);
}, [availableTabs, storageKey]);
}, []);

const handleChange: TabsOwnProps['onChange'] = (event, newValue) => {
setActiveTab(newValue as string);
if (storageKey === undefined) {
return;
}
try {
localStorage.setItem(storageKey, newValue as string);
} catch (error) {
// ignore error
}
};

const ownerState = { mounted };
Expand Down
15 changes: 1 addition & 14 deletions docs/src/modules/components/MaterialYouUsageDemo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { useTheme as md2UseTheme, alpha } from '@mui/material/styles';
import { alpha } from '@mui/material/styles';
import ReplayRoundedIcon from '@mui/icons-material/ReplayRounded';
import Box from '@mui/material/Box';
import Divider from '@mui/material/Divider';
Expand All @@ -13,13 +13,10 @@ import Typography from '@mui/material/Typography';
import {
extendTheme,
CssVarsProvider as MaterialYouCssVarsProvider,
useColorScheme,
} from '@mui/material-next/styles';
import BrandingProvider from 'docs/src/BrandingProvider';
import HighlightedCode from 'docs/src/modules/components/HighlightedCode';

type Mode = 'light' | 'dark' | 'system';

const materialYouTheme = extendTheme();
const shallowEqual = (item1: { [k: string]: any }, item2: { [k: string]: any }) => {
let equal = true;
Expand Down Expand Up @@ -93,14 +90,6 @@ export const prependLinesSpace = (code: string, size: number = 2) => {
return newCode.join('\n');
};

function ModeSwitcher({ md2Mode }: { md2Mode: Mode }) {
const { setMode } = useColorScheme();
React.useEffect(() => {
setMode(md2Mode);
}, [md2Mode, setMode]);
return null;
}

Comment on lines -96 to -103
Copy link
Member

@oliviertassinari oliviertassinari Feb 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either we use different modeStorageKey and attribute keys between Material UI and Material UI Next or we remove this.

interface MaterialYouUsageDemoProps<ComponentProps> {
/**
* Name of the component to show in the code block.
Expand Down Expand Up @@ -195,7 +184,6 @@ export default function MaterialYouUsageDemo<T extends { [k: string]: any } = {}
}
});

const md2Theme = md2UseTheme();
return (
<Box
sx={{
Expand All @@ -221,7 +209,6 @@ export default function MaterialYouUsageDemo<T extends { [k: string]: any } = {}
}}
>
<MaterialYouCssVarsProvider theme={materialYouTheme}>
<ModeSwitcher md2Mode={md2Theme.palette.mode} />
{renderDemo(demoProps)}
</MaterialYouCssVarsProvider>
</Box>
Expand Down
Loading
Loading