-
Notifications
You must be signed in to change notification settings - Fork 367
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
refactor: [M3-8623] - Introduce TanStack Router #10997
Changes from all commits
8bd0774
8b61d5b
373537c
e79a3ad
65b366c
6bb0d5e
2930229
a8820e7
99655ee
696df7c
257177a
dbf91d9
6219592
f4826fa
e44e781
3228ae1
cc7fae1
7846dc0
861fe76
0fb58fd
2167131
cd8f378
4120de5
5093af7
5fcb678
c0adce6
8604c52
f7e2089
41b77a9
eff58c7
f90191e
72cb808
4e3405e
4f32ff5
bb7191e
c83738a
4ecfed9
43b9a8c
1fbcf22
b808165
a9cc322
6cd3d82
526fedd
8f5f1a1
19971cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@linode/manager": Added | ||
--- | ||
|
||
Introduce TanStack Router ([#10997](https://github.com/linode/manager/pull/10997)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { makeStyles } from 'tss-react/mui'; | ||
|
||
import { SIDEBAR_WIDTH } from 'src/components/PrimaryNav/SideMenu'; | ||
|
||
import type { Theme } from '@mui/material/styles'; | ||
|
||
export const useStyles = makeStyles()((theme: Theme) => ({ | ||
activationWrapper: { | ||
padding: theme.spacing(4), | ||
[theme.breakpoints.up('xl')]: { | ||
margin: '0 auto', | ||
width: '50%', | ||
}, | ||
}, | ||
appFrame: { | ||
backgroundColor: theme.bg.app, | ||
display: 'flex', | ||
flexDirection: 'column', | ||
minHeight: '100vh', | ||
position: 'relative', | ||
zIndex: 1, | ||
}, | ||
bgStyling: { | ||
backgroundColor: theme.bg.main, | ||
display: 'flex', | ||
flexDirection: 'column', | ||
justifyContent: 'center', | ||
minHeight: '100vh', | ||
}, | ||
cmrWrapper: { | ||
maxWidth: `${theme.breakpoints.values.lg}px !important`, | ||
padding: `${theme.spacing(3)} 0`, | ||
paddingTop: 12, | ||
[theme.breakpoints.between('md', 'xl')]: { | ||
paddingLeft: theme.spacing(2), | ||
paddingRight: theme.spacing(2), | ||
}, | ||
[theme.breakpoints.down('md')]: { | ||
paddingLeft: 0, | ||
paddingRight: 0, | ||
paddingTop: theme.spacing(2), | ||
}, | ||
transition: theme.transitions.create('opacity'), | ||
}, | ||
content: { | ||
flex: 1, | ||
[theme.breakpoints.up('md')]: { | ||
marginLeft: SIDEBAR_WIDTH, | ||
}, | ||
transition: 'margin-left .1s linear', | ||
}, | ||
fullWidthContent: { | ||
marginLeft: 0, | ||
[theme.breakpoints.up('md')]: { | ||
marginLeft: 52, | ||
}, | ||
}, | ||
grid: { | ||
marginLeft: 0, | ||
marginRight: 0, | ||
[theme.breakpoints.up('lg')]: { | ||
height: '100%', | ||
}, | ||
width: '100%', | ||
}, | ||
logo: { | ||
'& > g': { | ||
fill: theme.color.black, | ||
}, | ||
}, | ||
switchWrapper: { | ||
'& > .MuiGrid-container': { | ||
maxWidth: theme.breakpoints.values.lg, | ||
width: '100%', | ||
}, | ||
'&.mlMain': { | ||
[theme.breakpoints.up('lg')]: { | ||
maxWidth: '78.8%', | ||
}, | ||
}, | ||
flex: 1, | ||
maxWidth: '100%', | ||
position: 'relative', | ||
}, | ||
})); | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import Grid from '@mui/material/Unstable_Grid2'; | ||
import { Outlet } from '@tanstack/react-router'; | ||
import React from 'react'; | ||
|
||
import Logo from 'src/assets/logo/akamai-logo.svg'; | ||
import { Box } from 'src/components/Box'; | ||
import { MainContentBanner } from 'src/components/MainContentBanner'; | ||
import { MaintenanceScreen } from 'src/components/MaintenanceScreen'; | ||
import { SideMenu } from 'src/components/PrimaryNav/SideMenu'; | ||
import { SuspenseLoader } from 'src/components/SuspenseLoader'; | ||
import { useDialogContext } from 'src/context/useDialogContext'; | ||
import { Footer } from 'src/features/Footer'; | ||
import { GlobalNotifications } from 'src/features/GlobalNotifications/GlobalNotifications'; | ||
import { | ||
notificationCenterContext, | ||
useNotificationContext, | ||
} from 'src/features/NotificationCenter/NotificationCenterContext'; | ||
import { TopMenu } from 'src/features/TopMenu/TopMenu'; | ||
import { | ||
useMutatePreferences, | ||
usePreferences, | ||
} from 'src/queries/profile/preferences'; | ||
|
||
import { ENABLE_MAINTENANCE_MODE } from './constants'; | ||
import { complianceUpdateContext } from './context/complianceUpdateContext'; | ||
import { sessionExpirationContext } from './context/sessionExpirationContext'; | ||
import { switchAccountSessionContext } from './context/switchAccountSessionContext'; | ||
import { useGlobalErrors } from './hooks/useGlobalErrors'; | ||
import { useProfile } from './queries/profile/profile'; | ||
import { useStyles } from './Root.styles'; | ||
|
||
export const Root = () => { | ||
const { classes, cx } = useStyles(); | ||
const { data: preferences } = usePreferences(); | ||
const { mutateAsync: updatePreferences } = useMutatePreferences(); | ||
|
||
const globalErrors = useGlobalErrors(); | ||
|
||
const NotificationProvider = notificationCenterContext.Provider; | ||
const contextValue = useNotificationContext(); | ||
|
||
const ComplianceUpdateProvider = complianceUpdateContext.Provider; | ||
const complianceUpdateContextValue = useDialogContext(); | ||
|
||
const SwitchAccountSessionProvider = switchAccountSessionContext.Provider; | ||
const switchAccountSessionContextValue = useDialogContext({ | ||
isOpen: false, | ||
}); | ||
|
||
const SessionExpirationProvider = sessionExpirationContext.Provider; | ||
const sessionExpirationContextValue = useDialogContext({ | ||
isOpen: false, | ||
}); | ||
|
||
const [menuIsOpen, toggleMenu] = React.useState<boolean>(false); | ||
|
||
const { data: profile } = useProfile(); | ||
const username = profile?.username || ''; | ||
|
||
const desktopMenuIsOpen = preferences?.desktop_sidebar_open ?? false; | ||
|
||
const desktopMenuToggle = () => { | ||
updatePreferences({ | ||
desktop_sidebar_open: !preferences?.desktop_sidebar_open, | ||
}); | ||
}; | ||
|
||
if (globalErrors.account_unactivated) { | ||
return ( | ||
<div className={classes.bgStyling}> | ||
<div className={classes.activationWrapper}> | ||
<Box style={{ display: 'flex', justifyContent: 'center' }}> | ||
<Logo className={classes.logo} width={215} /> | ||
</Box> | ||
<Outlet /> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
if (globalErrors.api_maintenance_mode || ENABLE_MAINTENANCE_MODE) { | ||
return <MaintenanceScreen />; | ||
} | ||
|
||
return ( | ||
<div className={classes.appFrame} data-testid="root"> | ||
<SessionExpirationProvider value={sessionExpirationContextValue}> | ||
<SwitchAccountSessionProvider value={switchAccountSessionContextValue}> | ||
<ComplianceUpdateProvider value={complianceUpdateContextValue}> | ||
<NotificationProvider value={contextValue}> | ||
<SideMenu | ||
closeMenu={() => toggleMenu(false)} | ||
collapse={desktopMenuIsOpen || false} | ||
open={menuIsOpen} | ||
/> | ||
<div | ||
className={cx(classes.content, { | ||
[classes.fullWidthContent]: desktopMenuIsOpen, | ||
})} | ||
> | ||
<MainContentBanner /> | ||
<TopMenu | ||
desktopMenuToggle={desktopMenuToggle} | ||
isSideMenuOpen={!desktopMenuIsOpen} | ||
openSideMenu={() => toggleMenu(true)} | ||
username={username} | ||
/> | ||
<main | ||
className={classes.cmrWrapper} | ||
id="main-content" | ||
role="main" | ||
> | ||
<Grid className={classes.grid} container spacing={0}> | ||
<Grid className={cx(classes.switchWrapper, 'p0')}> | ||
<GlobalNotifications /> | ||
<React.Suspense fallback={<SuspenseLoader />}> | ||
<Outlet /> | ||
</React.Suspense> | ||
</Grid> | ||
</Grid> | ||
</main> | ||
</div> | ||
</NotificationProvider> | ||
<Footer desktopMenuIsOpen={desktopMenuIsOpen} /> | ||
</ComplianceUpdateProvider> | ||
</SwitchAccountSessionProvider> | ||
</SessionExpirationProvider> | ||
</div> | ||
); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decoupled the root from the so the saparation of concern between routing and composition is more clear. This is not affecting the current experience (unless switching things in |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { RouterProvider } from '@tanstack/react-router'; | ||
import * as React from 'react'; | ||
|
||
import { useFlags } from 'src/hooks/useFlags'; | ||
import { useGlobalErrors } from 'src/hooks/useGlobalErrors'; | ||
|
||
import { useIsACLPEnabled } from './features/CloudPulse/Utils/utils'; | ||
import { useIsDatabasesEnabled } from './features/Databases/utilities'; | ||
import { useIsPlacementGroupsEnabled } from './features/PlacementGroups/utils'; | ||
import { useAccountSettings } from './queries/account/settings'; | ||
import { router } from './routes'; | ||
|
||
export const Router = () => { | ||
const { data: accountSettings } = useAccountSettings(); | ||
const { isDatabasesEnabled } = useIsDatabasesEnabled(); | ||
const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled(); | ||
const { isACLPEnabled } = useIsACLPEnabled(); | ||
const globalErrors = useGlobalErrors(); | ||
const flags = useFlags(); | ||
|
||
// Update the router's context | ||
router.update({ | ||
context: { | ||
accountSettings, | ||
globalErrors, | ||
isACLPEnabled, | ||
isDatabasesEnabled, | ||
isPlacementGroupsEnabled, | ||
selfServeBetas: flags.selfServeBetas, | ||
}, | ||
}); | ||
|
||
return <RouterProvider router={router} />; | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Our new entrypoint to load all routes in Cloud Manager (not active in this PR!) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
import Warning from '@mui/icons-material/CheckCircle'; | ||
import { createLazyRoute } from '@tanstack/react-router'; | ||
import * as React from 'react'; | ||
import { useHistory } from 'react-router-dom'; | ||
|
||
|
@@ -69,4 +70,10 @@ const AccountActivationLanding = () => { | |
); | ||
}; | ||
|
||
export const accountActivationLandingLazyRoute = createLazyRoute( | ||
'/account-activation' | ||
)({ | ||
component: AccountActivationLanding, | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All Those get imported in the /routes/* files via |
||
|
||
export default React.memo(AccountActivationLanding); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All existing styles from
<MainContent />
extracted to their own file - this also isn't affecting the current experience