From 45d2188a7f4690c2325b9d2ef29cc9fd30b585e7 Mon Sep 17 00:00:00 2001 From: Bharat Kashyap Date: Sat, 19 Oct 2024 01:13:50 +0530 Subject: [PATCH] [account] New slots and sub-components (#4181) --- .../core/components/account/AccountCustom.js | 127 ------ .../core/components/account/AccountCustom.tsx | 133 ------- .../account/AccountCustom.tsx.preview | 22 -- .../account/AccountCustomLocaleText.js | 47 +++ .../account/AccountCustomLocaleText.tsx | 51 +++ .../AccountCustomLocaleText.tsx.preview | 6 + .../account/AccountCustomSlotProps.js | 67 ++++ .../account/AccountCustomSlotProps.tsx | 71 ++++ .../AccountCustomSlotProps.tsx.preview | 25 ++ .../components/account/AccountDemoSignedIn.js | 2 + .../account/AccountDemoSignedIn.tsx | 2 + .../account/AccountDemoSignedIn.tsx.preview | 6 +- .../account/AccountSlots.tsx.preview | 9 - ...lots.js => AccountSlotsAccountSwitcher.js} | 8 +- ...ts.tsx => AccountSlotsAccountSwitcher.tsx} | 8 +- .../AccountSlotsAccountSwitcher.tsx.preview | 5 + .../core/components/account/CustomMenu.js | 214 +++++----- .../core/components/account/CustomMenu.tsx | 217 +++++----- .../core/components/account/account.md | 39 +- docs/data/toolpad/core/pagesApi.js | 5 + .../core/api/account-popover-footer.js | 23 ++ .../core/api/account-popover-footer.json | 11 + .../core/api/account-popover-header.js | 23 ++ .../core/api/account-popover-header.json | 11 + .../pages/toolpad/core/api/account-preview.js | 23 ++ .../toolpad/core/api/account-preview.json | 50 +++ docs/pages/toolpad/core/api/account.json | 36 +- .../toolpad/core/api/dashboard-layout.json | 2 +- docs/pages/toolpad/core/api/sign-in-button.js | 23 ++ .../toolpad/core/api/sign-in-button.json | 369 ++++++++++++++++++ .../pages/toolpad/core/api/sign-out-button.js | 23 ++ .../toolpad/core/api/sign-out-button.json | 369 ++++++++++++++++++ .../account-popover-footer.json | 1 + .../account-popover-header.json | 1 + .../account-preview/account-preview.json | 16 + .../api-docs/account/account.json | 4 +- .../sign-in-button/sign-in-button.json | 301 ++++++++++++++ .../sign-out-button/sign-out-button.json | 301 ++++++++++++++ packages/toolpad-core/src/Account/Account.tsx | 277 ++++++------- .../src/Account/AccountPopoverFooter.tsx | 27 ++ .../src/Account/AccountPopoverHeader.tsx | 20 + .../src/Account/AccountPreview.test.tsx | 82 ++++ .../src/Account/AccountPreview.tsx | 181 +++++++++ .../src/Account/SessionAvatar.tsx | 22 -- .../toolpad-core/src/Account/SignInButton.tsx | 38 ++ .../src/Account/SignOutButton.tsx | 44 +++ packages/toolpad-core/src/Account/index.ts | 5 + .../src/DashboardLayout/DashboardLayout.tsx | 13 +- .../src/shared/locales/LocaleContext.tsx | 39 ++ .../toolpad-core/src/shared/locales/en.tsx | 2 + .../app/(dashboard)/SidebarFooterAccount.tsx | 104 +++++ pnpm-lock.yaml | 6 +- 52 files changed, 2761 insertions(+), 750 deletions(-) delete mode 100644 docs/data/toolpad/core/components/account/AccountCustom.js delete mode 100644 docs/data/toolpad/core/components/account/AccountCustom.tsx delete mode 100644 docs/data/toolpad/core/components/account/AccountCustom.tsx.preview create mode 100644 docs/data/toolpad/core/components/account/AccountCustomLocaleText.js create mode 100644 docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx create mode 100644 docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx.preview create mode 100644 docs/data/toolpad/core/components/account/AccountCustomSlotProps.js create mode 100644 docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx create mode 100644 docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx.preview delete mode 100644 docs/data/toolpad/core/components/account/AccountSlots.tsx.preview rename docs/data/toolpad/core/components/account/{AccountSlots.js => AccountSlotsAccountSwitcher.js} (81%) rename docs/data/toolpad/core/components/account/{AccountSlots.tsx => AccountSlotsAccountSwitcher.tsx} (82%) create mode 100644 docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx.preview create mode 100644 docs/pages/toolpad/core/api/account-popover-footer.js create mode 100644 docs/pages/toolpad/core/api/account-popover-footer.json create mode 100644 docs/pages/toolpad/core/api/account-popover-header.js create mode 100644 docs/pages/toolpad/core/api/account-popover-header.json create mode 100644 docs/pages/toolpad/core/api/account-preview.js create mode 100644 docs/pages/toolpad/core/api/account-preview.json create mode 100644 docs/pages/toolpad/core/api/sign-in-button.js create mode 100644 docs/pages/toolpad/core/api/sign-in-button.json create mode 100644 docs/pages/toolpad/core/api/sign-out-button.js create mode 100644 docs/pages/toolpad/core/api/sign-out-button.json create mode 100644 docs/translations/api-docs/account-popover-footer/account-popover-footer.json create mode 100644 docs/translations/api-docs/account-popover-header/account-popover-header.json create mode 100644 docs/translations/api-docs/account-preview/account-preview.json create mode 100644 docs/translations/api-docs/sign-in-button/sign-in-button.json create mode 100644 docs/translations/api-docs/sign-out-button/sign-out-button.json create mode 100644 packages/toolpad-core/src/Account/AccountPopoverFooter.tsx create mode 100644 packages/toolpad-core/src/Account/AccountPopoverHeader.tsx create mode 100644 packages/toolpad-core/src/Account/AccountPreview.test.tsx create mode 100644 packages/toolpad-core/src/Account/AccountPreview.tsx delete mode 100644 packages/toolpad-core/src/Account/SessionAvatar.tsx create mode 100644 packages/toolpad-core/src/Account/SignInButton.tsx create mode 100644 packages/toolpad-core/src/Account/SignOutButton.tsx create mode 100644 packages/toolpad-core/src/shared/locales/LocaleContext.tsx create mode 100644 playground/nextjs/src/app/(dashboard)/SidebarFooterAccount.tsx diff --git a/docs/data/toolpad/core/components/account/AccountCustom.js b/docs/data/toolpad/core/components/account/AccountCustom.js deleted file mode 100644 index 526cde58938..00000000000 --- a/docs/data/toolpad/core/components/account/AccountCustom.js +++ /dev/null @@ -1,127 +0,0 @@ -import * as React from 'react'; -import Typography from '@mui/material/Typography'; -import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider'; -import { Account } from '@toolpad/core/Account'; - -const demoSession = { - user: { - name: 'Bharat Kashyap', - email: 'bharatkashyap@outlook.com', - image: 'https://avatars.githubusercontent.com/u/19550456', - }, -}; - -export default function AccountCustom() { - const [session, setSession] = React.useState(demoSession); - const [signedOutSession, setSignedOutSession] = React.useState(null); - - const authenticationSignedIn = React.useMemo(() => { - return { - signIn: () => { - setSession({ - user: { - name: 'Bharat Kashyap', - email: 'bharatkashyap@outlook.com', - image: 'https://avatars.githubusercontent.com/u/19550456', - }, - }); - }, - signOut: () => { - setSession(null); - setSignedOutSession(null); - }, - }; - }, []); - - const authenticationSignedOut = React.useMemo(() => { - return { - signIn: () => { - setSignedOutSession(demoSession); - }, - signOut: () => { - setSignedOutSession(null); - }, - }; - }, []); - - return ( -
- - - Signed in - - - - - - - Signed out - - - - - {/* preview-start */} - - {/* preview-end */} - - -
- ); -} diff --git a/docs/data/toolpad/core/components/account/AccountCustom.tsx b/docs/data/toolpad/core/components/account/AccountCustom.tsx deleted file mode 100644 index e2ac4f7fe64..00000000000 --- a/docs/data/toolpad/core/components/account/AccountCustom.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import * as React from 'react'; -import Typography from '@mui/material/Typography'; -import { - AuthenticationContext, - SessionContext, - type Session, -} from '@toolpad/core/AppProvider'; -import { Account } from '@toolpad/core/Account'; - -const demoSession = { - user: { - name: 'Bharat Kashyap', - email: 'bharatkashyap@outlook.com', - image: 'https://avatars.githubusercontent.com/u/19550456', - }, -}; - -export default function AccountCustom() { - const [session, setSession] = React.useState(demoSession); - const [signedOutSession, setSignedOutSession] = React.useState( - null, - ); - - const authenticationSignedIn = React.useMemo(() => { - return { - signIn: () => { - setSession({ - user: { - name: 'Bharat Kashyap', - email: 'bharatkashyap@outlook.com', - image: 'https://avatars.githubusercontent.com/u/19550456', - }, - }); - }, - signOut: () => { - setSession(null); - setSignedOutSession(null); - }, - }; - }, []); - - const authenticationSignedOut = React.useMemo(() => { - return { - signIn: () => { - setSignedOutSession(demoSession); - }, - signOut: () => { - setSignedOutSession(null); - }, - }; - }, []); - - return ( -
- - - Signed in - - - - - - - Signed out - - - - - {/* preview-start */} - - {/* preview-end */} - - -
- ); -} diff --git a/docs/data/toolpad/core/components/account/AccountCustom.tsx.preview b/docs/data/toolpad/core/components/account/AccountCustom.tsx.preview deleted file mode 100644 index 1143a6ea501..00000000000 --- a/docs/data/toolpad/core/components/account/AccountCustom.tsx.preview +++ /dev/null @@ -1,22 +0,0 @@ - \ No newline at end of file diff --git a/docs/data/toolpad/core/components/account/AccountCustomLocaleText.js b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.js new file mode 100644 index 00000000000..5ed35873c32 --- /dev/null +++ b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.js @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider'; +import { Account } from '@toolpad/core/Account'; + +const demoSession = { + user: { + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, +}; + +export default function AccountCustomLocaleText() { + const [session, setSession] = React.useState(demoSession); + + const authentication = React.useMemo(() => { + return { + signIn: () => { + setSession({ + user: { + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, + }); + }, + signOut: () => { + setSession(null); + }, + }; + }, []); + + return ( + + + {/* preview-start */} + + {/* preview-end */} + + + ); +} diff --git a/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx new file mode 100644 index 00000000000..c74db6789c7 --- /dev/null +++ b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import { + AuthenticationContext, + Session, + SessionContext, +} from '@toolpad/core/AppProvider'; +import { Account } from '@toolpad/core/Account'; + +const demoSession = { + user: { + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, +}; + +export default function AccountCustomLocaleText() { + const [session, setSession] = React.useState(demoSession); + + const authentication = React.useMemo(() => { + return { + signIn: () => { + setSession({ + user: { + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, + }); + }, + signOut: () => { + setSession(null); + }, + }; + }, []); + + return ( + + + {/* preview-start */} + + {/* preview-end */} + + + ); +} diff --git a/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx.preview b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx.preview new file mode 100644 index 00000000000..882b98486be --- /dev/null +++ b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/toolpad/core/components/account/AccountCustomSlotProps.js b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.js new file mode 100644 index 00000000000..3995c7e1345 --- /dev/null +++ b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.js @@ -0,0 +1,67 @@ +import * as React from 'react'; +import Logout from '@mui/icons-material/Logout'; +import { Account } from '@toolpad/core/Account'; +import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider'; + +const demoSession = { + user: { + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, +}; + +export default function AccountCustomSlotProps() { + const [session, setSession] = React.useState(demoSession); + + const authentication = React.useMemo(() => { + return { + signIn: () => { + setSession({ + user: { + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, + }); + }, + signOut: () => { + setSession(null); + }, + }; + }, []); + + return ( + + + {/* preview-start */} + , + }, + preview: { + variant: 'expanded', + slotProps: { + avatarIconButton: { + sx: { + width: 'fit-content', + margin: 'auto', + }, + }, + avatar: { + variant: 'rounded', + }, + }, + }, + }} + /> + {/* preview-end */} + + + ); +} diff --git a/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx new file mode 100644 index 00000000000..fcfa8bd028c --- /dev/null +++ b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import Logout from '@mui/icons-material/Logout'; +import { Account } from '@toolpad/core/Account'; +import { + AuthenticationContext, + SessionContext, + Session, +} from '@toolpad/core/AppProvider'; + +const demoSession = { + user: { + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, +}; + +export default function AccountCustomSlotProps() { + const [session, setSession] = React.useState(demoSession); + + const authentication = React.useMemo(() => { + return { + signIn: () => { + setSession({ + user: { + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, + }); + }, + signOut: () => { + setSession(null); + }, + }; + }, []); + + return ( + + + {/* preview-start */} + , + }, + preview: { + variant: 'expanded', + slotProps: { + avatarIconButton: { + sx: { + width: 'fit-content', + margin: 'auto', + }, + }, + avatar: { + variant: 'rounded', + }, + }, + }, + }} + /> + {/* preview-end */} + + + ); +} diff --git a/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx.preview b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx.preview new file mode 100644 index 00000000000..2730a8c4c9b --- /dev/null +++ b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx.preview @@ -0,0 +1,25 @@ +, + }, + preview: { + variant: 'expanded', + slotProps: { + avatarIconButton: { + sx: { + width: 'fit-content', + margin: 'auto', + }, + }, + avatar: { + variant: 'rounded', + }, + }, + }, + }} +/> \ No newline at end of file diff --git a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.js b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.js index c8b9b8a9c0c..5f8ea177353 100644 --- a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.js +++ b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.js @@ -26,7 +26,9 @@ export default function AccountDemoSignedIn() { return ( + {/* preview-start */} + {/* preview-end */} ); diff --git a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx index 7cd00c266e1..718b4ed60fa 100644 --- a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx +++ b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx @@ -30,7 +30,9 @@ export default function AccountDemoSignedIn() { return ( + {/* preview-start */} + {/* preview-end */} ); diff --git a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx.preview b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx.preview index b0489ce20ca..e6abc8692e0 100644 --- a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx.preview +++ b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx.preview @@ -1,5 +1 @@ - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/docs/data/toolpad/core/components/account/AccountSlots.tsx.preview b/docs/data/toolpad/core/components/account/AccountSlots.tsx.preview deleted file mode 100644 index 5e8ecca9f06..00000000000 --- a/docs/data/toolpad/core/components/account/AccountSlots.tsx.preview +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/docs/data/toolpad/core/components/account/AccountSlots.js b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.js similarity index 81% rename from docs/data/toolpad/core/components/account/AccountSlots.js rename to docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.js index d825aaba8f0..c938fa78e17 100644 --- a/docs/data/toolpad/core/components/account/AccountSlots.js +++ b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.js @@ -1,7 +1,7 @@ import * as React from 'react'; import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider'; import { Account } from '@toolpad/core/Account'; -import CustomMenuItems from './CustomMenu'; +import CustomMenu from './CustomMenu'; const demoSession = { user: { @@ -11,7 +11,7 @@ const demoSession = { }, }; -export default function AccountSlots() { +export default function AccountSlotsAccountSwitcher() { const [session, setSession] = React.useState(demoSession); const authentication = React.useMemo(() => { return { @@ -27,11 +27,13 @@ export default function AccountSlots() { return ( + {/* preview-start */} + {/* preview-end */} ); diff --git a/docs/data/toolpad/core/components/account/AccountSlots.tsx b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx similarity index 82% rename from docs/data/toolpad/core/components/account/AccountSlots.tsx rename to docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx index 52eb96ce395..7e89f9663a3 100644 --- a/docs/data/toolpad/core/components/account/AccountSlots.tsx +++ b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx @@ -5,7 +5,7 @@ import { type Session, } from '@toolpad/core/AppProvider'; import { Account } from '@toolpad/core/Account'; -import CustomMenuItems from './CustomMenu'; +import CustomMenu from './CustomMenu'; const demoSession = { user: { @@ -15,7 +15,7 @@ const demoSession = { }, }; -export default function AccountSlots() { +export default function AccountSlotsAccountSwitcher() { const [session, setSession] = React.useState(demoSession); const authentication = React.useMemo(() => { return { @@ -31,11 +31,13 @@ export default function AccountSlots() { return ( + {/* preview-start */} + {/* preview-end */} ); diff --git a/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx.preview b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx.preview new file mode 100644 index 00000000000..504347a380e --- /dev/null +++ b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/toolpad/core/components/account/CustomMenu.js b/docs/data/toolpad/core/components/account/CustomMenu.js index 2bbee81379a..a06670c3be1 100644 --- a/docs/data/toolpad/core/components/account/CustomMenu.js +++ b/docs/data/toolpad/core/components/account/CustomMenu.js @@ -1,133 +1,99 @@ import * as React from 'react'; -import PropTypes from 'prop-types'; -import { Menu, MenuItem, MenuList, Divider, ListItemIcon } from '@mui/material'; -import SettingsIcon from '@mui/icons-material/Settings'; +import { + MenuItem, + MenuList, + Button, + Divider, + ListItemIcon, + ListItemText, + Typography, + Avatar, + Stack, +} from '@mui/material'; +import { + AccountPreview, + SignOutButton, + AccountPopoverFooter, +} from '@toolpad/core/Account'; import AddIcon from '@mui/icons-material/Add'; -function CustomSettingsMenu(props) { - const { open, anchorEl, handleMenuClose, handleEnter, handleLeave } = props; - - return ( - - { - handleEnter(); - }} - onMouseLeave={handleLeave} - > - Profile - My account - - - ); -} - -CustomSettingsMenu.propTypes = { - anchorEl: PropTypes.object, - handleEnter: PropTypes.func.isRequired, - handleLeave: PropTypes.func.isRequired, - handleMenuClose: PropTypes.func.isRequired, - open: PropTypes.bool.isRequired, -}; +const accounts = [ + { + id: 1, + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, + { + id: 2, + name: 'Bharat MUI', + email: 'bharat@mui.com', + color: '#8B4513', // Brown color + }, +]; export default function CustomMenu() { - const handleMenuNavigation = (route) => () => { - console.log( - 'Toolpad Core Account Demo --- CustomMenuItems --- handleMenuNavigation --- route: ', - route, - ); - }; - - const mouseOnSubMenu = React.useRef(false); - - const [subMenuAnchorEl, setSubMenuAnchorEl] = React.useState(null); - const subMenuOpen = Boolean(subMenuAnchorEl); - - const handleTriggerEnter = React.useCallback((event) => { - setSubMenuAnchorEl(event.currentTarget); - }, []); - - const handleTriggerLeave = React.useCallback(() => { - // Wait for 300ms to see if the mouse has moved to the sub menu - setTimeout(() => { - if (mouseOnSubMenu.current) { - return; - } - setSubMenuAnchorEl(null); - }, 300); - }, []); - - const handleSubMenuEnter = React.useCallback(() => { - mouseOnSubMenu.current = true; - }, []); - - const handleSubMenuLeave = (event) => { - mouseOnSubMenu.current = false; - if (subMenuAnchorEl?.contains(event.relatedTarget)) { - return; - } - setSubMenuAnchorEl(null); - }; - - const handleSubMenuClose = React.useCallback(() => { - setSubMenuAnchorEl(null); - }, []); - return ( - - - - - - Settings - - - - - - Add another account - - + + + + + Accounts + + + {accounts.map((account) => ( + + + + {account.name[0]} + + + + + ))} + + + - - + + + + ); } diff --git a/docs/data/toolpad/core/components/account/CustomMenu.tsx b/docs/data/toolpad/core/components/account/CustomMenu.tsx index bdf09af5c13..a06670c3be1 100644 --- a/docs/data/toolpad/core/components/account/CustomMenu.tsx +++ b/docs/data/toolpad/core/components/account/CustomMenu.tsx @@ -1,136 +1,99 @@ import * as React from 'react'; -import { Menu, MenuItem, MenuList, Divider, ListItemIcon } from '@mui/material'; -import SettingsIcon from '@mui/icons-material/Settings'; +import { + MenuItem, + MenuList, + Button, + Divider, + ListItemIcon, + ListItemText, + Typography, + Avatar, + Stack, +} from '@mui/material'; +import { + AccountPreview, + SignOutButton, + AccountPopoverFooter, +} from '@toolpad/core/Account'; import AddIcon from '@mui/icons-material/Add'; -interface CustomSettingsMenuProps { - open: boolean; - anchorEl: HTMLButtonElement | null; - handleMenuClose: () => void; - handleLeave: (event: React.MouseEvent) => void; - handleEnter: () => void; -} - -function CustomSettingsMenu(props: CustomSettingsMenuProps) { - const { open, anchorEl, handleMenuClose, handleEnter, handleLeave } = props; - - return ( - - { - handleEnter(); - }} - onMouseLeave={handleLeave} - > - Profile - My account - - - ); -} +const accounts = [ + { + id: 1, + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + }, + { + id: 2, + name: 'Bharat MUI', + email: 'bharat@mui.com', + color: '#8B4513', // Brown color + }, +]; export default function CustomMenu() { - const handleMenuNavigation = (route: string) => () => { - console.log( - 'Toolpad Core Account Demo --- CustomMenuItems --- handleMenuNavigation --- route: ', - route, - ); - }; - - const mouseOnSubMenu = React.useRef(false); - - const [subMenuAnchorEl, setSubMenuAnchorEl] = - React.useState(null); - const subMenuOpen = Boolean(subMenuAnchorEl); - - const handleTriggerEnter = React.useCallback( - (event: React.MouseEvent) => { - setSubMenuAnchorEl(event.currentTarget); - }, - [], - ); - - const handleTriggerLeave = React.useCallback(() => { - // Wait for 300ms to see if the mouse has moved to the sub menu - setTimeout(() => { - if (mouseOnSubMenu.current) { - return; - } - setSubMenuAnchorEl(null); - }, 300); - }, []); - - const handleSubMenuEnter = React.useCallback(() => { - mouseOnSubMenu.current = true; - }, []); - - const handleSubMenuLeave = (event: React.MouseEvent) => { - mouseOnSubMenu.current = false; - if (subMenuAnchorEl?.contains(event.relatedTarget as Node)) { - return; - } - setSubMenuAnchorEl(null); - }; - - const handleSubMenuClose = React.useCallback(() => { - setSubMenuAnchorEl(null); - }, []); - return ( - - - - - - Settings - - - - - - Add another account - - + + + + + Accounts + + + {accounts.map((account) => ( + + + + {account.name[0]} + + + + + ))} + + + - - + + + + ); } diff --git a/docs/data/toolpad/core/components/account/account.md b/docs/data/toolpad/core/components/account/account.md index 7b5121fb8d3..a706c176a50 100644 --- a/docs/data/toolpad/core/components/account/account.md +++ b/docs/data/toolpad/core/components/account/account.md @@ -1,7 +1,7 @@ --- productId: toolpad-core title: Account -components: Account +components: Account, AccountPreview, AccountPopoverHeader, AccountPopoverFooter, SignInButton, SignOutButton --- # Account @@ -26,20 +26,43 @@ If a `session` object is present, the component is rendered as a dropdown contai When signed out, the component renders as an inline sign in button within the dashboard layout. -{{"demo": "AccountDemoSignedOut.js", "bg": "outlined" }} +{{"demo": "AccountDemoSignedOut.js", "bg": "outlined", "defaultCodeOpen": false }} ## Customization -### Slot Props +The `` component is composed of multiple sub-components: + +- `` +- `` +- `` +- `` + +You can pass extra props to them through the `slotProps` prop on the `` component. You may also completely override them by passing your own custom components to `` through the `slots` prop. -The underlying `signInButton`, `signOutButton` and `iconButton` components can be customized by passing in `slotProps` to the `Account` component. +The labels can be customized through the `localeText` prop. -Labels for the sign in and sign out buttons can be customized through the `localeText` prop. +### Slot Props + +The `AccountPreview` component has two variants, `condensed` (the default) and `expanded`. You can change the variant used inside `` by passing in custom props through `slotProps`: -{{"demo": "AccountCustom.js", "bg": "outlined" }} +{{"demo": "AccountCustomSlotProps.js", "bg": "outlined" }} ### Slots -You can pass in your own items to the `Account` menu through the `menuItems` slot to add additional menu items in the space between the user's account details and the sign out button, to create larger, more complex menus: +You can pass in your own components inside the `Account` popover through the `slots` prop. + +#### Popover Content + +You can wrap the default sub-components – such as `SignOutButton` and `AccountPreview` – and wrap them in `AccountPopoverHeader` and `AccountPopoverFooter` components to create custom account popovers, as shown in the following demos: + +##### Account Switcher + +You can build advanced menus – such as a tenant switcher – by passing in a component that wraps `AccountPreview` and `SignOutButton` with a custom menu: + +{{"demo": "AccountSlotsAccountSwitcher.js", "bg": "outlined"}} + +### Labels + +You can pass in custom labels – including of different languages – using the `localeText` prop. -{{"demo": "AccountSlots.js", "bg": "gradient"}} +{{"demo": "AccountCustomLocaleText.js", "bg": "outlined" }} diff --git a/docs/data/toolpad/core/pagesApi.js b/docs/data/toolpad/core/pagesApi.js index 346ba5b4a23..ea749c0aff6 100644 --- a/docs/data/toolpad/core/pagesApi.js +++ b/docs/data/toolpad/core/pagesApi.js @@ -1,10 +1,15 @@ module.exports = [ { pathname: '/toolpad/core/api/account' }, + { pathname: '/toolpad/core/api/account-popover-footer' }, + { pathname: '/toolpad/core/api/account-popover-header' }, + { pathname: '/toolpad/core/api/account-preview' }, { pathname: '/toolpad/core/api/app-provider' }, { pathname: '/toolpad/core/api/dashboard-layout' }, { pathname: '/toolpad/core/api/dialogs-provider' }, { pathname: '/toolpad/core/api/notifications-provider' }, { pathname: '/toolpad/core/api/page-container' }, { pathname: '/toolpad/core/api/page-container-toolbar' }, + { pathname: '/toolpad/core/api/sign-in-button' }, { pathname: '/toolpad/core/api/sign-in-page' }, + { pathname: '/toolpad/core/api/sign-out-button' }, ]; diff --git a/docs/pages/toolpad/core/api/account-popover-footer.js b/docs/pages/toolpad/core/api/account-popover-footer.js new file mode 100644 index 00000000000..c3052dec403 --- /dev/null +++ b/docs/pages/toolpad/core/api/account-popover-footer.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './account-popover-footer.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docs-toolpad/translations/api-docs/account-popover-footer', + false, + /\.\/account-popover-footer.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/toolpad/core/api/account-popover-footer.json b/docs/pages/toolpad/core/api/account-popover-footer.json new file mode 100644 index 00000000000..4d4b4d99d00 --- /dev/null +++ b/docs/pages/toolpad/core/api/account-popover-footer.json @@ -0,0 +1,11 @@ +{ + "props": {}, + "name": "AccountPopoverFooter", + "imports": ["import { AccountPopoverFooter } from '@toolpad/core/Account';"], + "classes": [], + "muiName": "AccountPopoverFooter", + "filename": "/packages/toolpad-core/src/Account/AccountPopoverFooter.tsx", + "inheritance": null, + "demos": "", + "cssComponent": false +} diff --git a/docs/pages/toolpad/core/api/account-popover-header.js b/docs/pages/toolpad/core/api/account-popover-header.js new file mode 100644 index 00000000000..4206c8ca54a --- /dev/null +++ b/docs/pages/toolpad/core/api/account-popover-header.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './account-popover-header.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docs-toolpad/translations/api-docs/account-popover-header', + false, + /\.\/account-popover-header.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/toolpad/core/api/account-popover-header.json b/docs/pages/toolpad/core/api/account-popover-header.json new file mode 100644 index 00000000000..e2ec37ec671 --- /dev/null +++ b/docs/pages/toolpad/core/api/account-popover-header.json @@ -0,0 +1,11 @@ +{ + "props": {}, + "name": "AccountPopoverHeader", + "imports": ["import { AccountPopoverHeader } from '@toolpad/core/Account';"], + "classes": [], + "muiName": "AccountPopoverHeader", + "filename": "/packages/toolpad-core/src/Account/AccountPopoverHeader.tsx", + "inheritance": null, + "demos": "", + "cssComponent": false +} diff --git a/docs/pages/toolpad/core/api/account-preview.js b/docs/pages/toolpad/core/api/account-preview.js new file mode 100644 index 00000000000..44cb65528d1 --- /dev/null +++ b/docs/pages/toolpad/core/api/account-preview.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './account-preview.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docs-toolpad/translations/api-docs/account-preview', + false, + /\.\/account-preview.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/toolpad/core/api/account-preview.json b/docs/pages/toolpad/core/api/account-preview.json new file mode 100644 index 00000000000..8608aa72476 --- /dev/null +++ b/docs/pages/toolpad/core/api/account-preview.json @@ -0,0 +1,50 @@ +{ + "props": { + "handleClick": { "type": { "name": "func" } }, + "open": { "type": { "name": "bool" }, "default": "false" }, + "slotProps": { + "type": { + "name": "shape", + "description": "{ avatar?: object, avatarIconButton?: object, moreIconButton?: object }" + } + }, + "slots": { + "type": { "name": "shape", "description": "{ avatar?: elementType }" }, + "additionalInfo": { "slotsApi": true } + }, + "variant": { + "type": { "name": "enum", "description": "'condensed'
| 'expanded'" }, + "default": "'condensed'" + } + }, + "name": "AccountPreview", + "imports": ["import { AccountPreview } from '@toolpad/core/Account';"], + "slots": [ + { + "name": "avatar", + "description": "The component used for the Avatar", + "default": "Avatar", + "class": null + }, + { + "name": "moreIconButton", + "description": "The component used for the overflow icon button in the expanded variant", + "default": "IconButton", + "class": null + }, + { + "name": "avatarIconButton", + "description": "The component used for the avatar icon button in the condensed variant", + "default": "IconButton", + "class": null + } + ], + "classes": [], + "spread": true, + "themeDefaultProps": null, + "muiName": "AccountPreview", + "filename": "/packages/toolpad-core/src/Account/AccountPreview.tsx", + "inheritance": null, + "demos": "", + "cssComponent": false +} diff --git a/docs/pages/toolpad/core/api/account.json b/docs/pages/toolpad/core/api/account.json index 16f12682220..87dc15cbe0e 100644 --- a/docs/pages/toolpad/core/api/account.json +++ b/docs/pages/toolpad/core/api/account.json @@ -1,19 +1,21 @@ { "props": { "localeText": { - "type": { "name": "shape", "description": "{ signInLabel: string, signOutLabel: string }" }, - "default": "DEFAULT_LOCALE_TEXT" + "type": { + "name": "shape", + "description": "{ iconButtonAriaLabel?: string, signInLabel?: string, signOutLabel?: string }" + } }, "slotProps": { "type": { "name": "shape", - "description": "{ iconButton?: object, signInButton?: object, signOutButton?: object }" + "description": "{ popover?: object, popoverContent?: object, preview?: { handleClick?: func, open?: bool, slotProps?: { avatar?: object, avatarIconButton?: object, moreIconButton?: object }, slots?: { avatar?: elementType, avatarIconButton?: elementType, moreIconButton?: elementType }, variant?: 'condensed'
| 'expanded' }, signInButton?: object, signOutButton?: object }" } }, "slots": { "type": { "name": "shape", - "description": "{ menuItems?: elementType, signInButton?: elementType, signOutButton?: elementType }" + "description": "{ popover?: elementType, popoverContent?: elementType, preview?: elementType, signInButton?: elementType, signOutButton?: elementType }" }, "additionalInfo": { "slotsApi": true } } @@ -24,6 +26,24 @@ "import { Account } from '@toolpad/core';" ], "slots": [ + { + "name": "preview", + "description": "The component used for the account preview", + "default": "AccountPreview", + "class": null + }, + { + "name": "popover", + "description": "The component used for the account popover menu", + "default": "Popover", + "class": null + }, + { + "name": "popoverContent", + "description": "The component used for the content of account popover", + "default": "Stack", + "class": null + }, { "name": "signInButton", "description": "The component used for the sign in button.", @@ -33,13 +53,7 @@ { "name": "signOutButton", "description": "The component used for the sign out button.", - "default": "MenuItem", - "class": null - }, - { - "name": "menuItems", - "description": "The component used for the custom menu items.", - "default": "null", + "default": "Button", "class": null } ], diff --git a/docs/pages/toolpad/core/api/dashboard-layout.json b/docs/pages/toolpad/core/api/dashboard-layout.json index fdb76804d9b..e8c7e9260b4 100644 --- a/docs/pages/toolpad/core/api/dashboard-layout.json +++ b/docs/pages/toolpad/core/api/dashboard-layout.json @@ -7,7 +7,7 @@ "slotProps": { "type": { "name": "shape", - "description": "{ sidebarFooter?: { mini: bool }, toolbarAccount?: { localeText?: { signInLabel: string, signOutLabel: string }, slotProps?: { iconButton?: object, signInButton?: object, signOutButton?: object }, slots?: { menuItems?: elementType, signInButton?: elementType, signOutButton?: elementType } }, toolbarActions?: object }" + "description": "{ sidebarFooter?: { mini: bool }, toolbarAccount?: { localeText?: { iconButtonAriaLabel?: string, signInLabel?: string, signOutLabel?: string }, slotProps?: { popover?: object, popoverContent?: object, preview?: object, signInButton?: object, signOutButton?: object }, slots?: { popover?: elementType, popoverContent?: elementType, preview?: elementType, signInButton?: elementType, signOutButton?: elementType } }, toolbarActions?: object }" }, "default": "{}" }, diff --git a/docs/pages/toolpad/core/api/sign-in-button.js b/docs/pages/toolpad/core/api/sign-in-button.js new file mode 100644 index 00000000000..aab4d5111ca --- /dev/null +++ b/docs/pages/toolpad/core/api/sign-in-button.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './sign-in-button.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docs-toolpad/translations/api-docs/sign-in-button', + false, + /\.\/sign-in-button.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/toolpad/core/api/sign-in-button.json b/docs/pages/toolpad/core/api/sign-in-button.json new file mode 100644 index 00000000000..72ccc67fc4a --- /dev/null +++ b/docs/pages/toolpad/core/api/sign-in-button.json @@ -0,0 +1,369 @@ +{ + "props": {}, + "name": "SignInButton", + "imports": ["import { SignInButton } from '@toolpad/core/Account';"], + "classes": [ + { + "key": "colorError", + "className": "", + "description": "Styles applied to the root element if `color=\"error\"`.", + "isGlobal": false + }, + { + "key": "colorInfo", + "className": "", + "description": "Styles applied to the root element if `color=\"info\"`.", + "isGlobal": false + }, + { + "key": "colorInherit", + "className": "", + "description": "Styles applied to the root element if `color=\"inherit\"`.", + "isGlobal": false + }, + { + "key": "colorPrimary", + "className": "", + "description": "Styles applied to the root element if `color=\"primary\"`.", + "isGlobal": false + }, + { + "key": "colorSecondary", + "className": "", + "description": "Styles applied to the root element if `color=\"secondary\"`.", + "isGlobal": false + }, + { + "key": "colorSuccess", + "className": "", + "description": "Styles applied to the root element if `color=\"success\"`.", + "isGlobal": false + }, + { + "key": "colorWarning", + "className": "", + "description": "Styles applied to the root element if `color=\"warning\"`.", + "isGlobal": false + }, + { + "key": "contained", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"`.", + "isGlobal": false + }, + { + "key": "containedError", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"error\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedInfo", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"info\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedInherit", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"inherit\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedPrimary", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"primary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSecondary", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"secondary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSizeLarge", + "className": "", + "description": "Styles applied to the root element if `size=\"large\"` and `variant=\"contained\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSizeMedium", + "className": "", + "description": "Styles applied to the root element if `size=\"medium\"` and `variant=\"contained\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSizeSmall", + "className": "", + "description": "Styles applied to the root element if `size=\"small\"` and `variant=\"contained\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSuccess", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"success\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedWarning", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"warning\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "disabled", + "className": "", + "description": "State class applied to the root element if `disabled={true}`.", + "isGlobal": false + }, + { + "key": "disableElevation", + "className": "", + "description": "Styles applied to the root element if `disableElevation={true}`.", + "isGlobal": false + }, + { + "key": "endIcon", + "className": "", + "description": "Styles applied to the endIcon element if supplied.", + "isGlobal": false + }, + { + "key": "focusVisible", + "className": "", + "description": "State class applied to the ButtonBase root element if the button is keyboard focused.", + "isGlobal": false + }, + { + "key": "fullWidth", + "className": "", + "description": "Styles applied to the root element if `fullWidth={true}`.", + "isGlobal": false + }, + { + "key": "icon", + "className": "", + "description": "Styles applied to the icon element if supplied", + "isGlobal": false + }, + { + "key": "iconSizeLarge", + "className": "", + "description": "Styles applied to the icon element if supplied and `size=\"large\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "iconSizeMedium", + "className": "", + "description": "Styles applied to the icon element if supplied and `size=\"medium\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "iconSizeSmall", + "className": "", + "description": "Styles applied to the icon element if supplied and `size=\"small\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlined", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"`.", + "isGlobal": false + }, + { + "key": "outlinedError", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"error\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedInfo", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"info\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedInherit", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"inherit\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedPrimary", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"primary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSecondary", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"secondary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSizeLarge", + "className": "", + "description": "Styles applied to the root element if `size=\"large\"` and `variant=\"outlined\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSizeMedium", + "className": "", + "description": "Styles applied to the root element if `size=\"medium\"` and `variant=\"outlined\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSizeSmall", + "className": "", + "description": "Styles applied to the root element if `size=\"small\"` and `variant=\"outlined\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSuccess", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"success\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedWarning", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"warning\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "root", + "className": "", + "description": "Styles applied to the root element.", + "isGlobal": false + }, + { + "key": "sizeLarge", + "className": "", + "description": "Styles applied to the root element if `size=\"large\"`.", + "isGlobal": false + }, + { + "key": "sizeMedium", + "className": "", + "description": "Styles applied to the root element if `size=\"medium\"`.", + "isGlobal": false + }, + { + "key": "sizeSmall", + "className": "", + "description": "Styles applied to the root element if `size=\"small\"`.", + "isGlobal": false + }, + { + "key": "startIcon", + "className": "", + "description": "Styles applied to the startIcon element if supplied.", + "isGlobal": false + }, + { + "key": "text", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"`.", + "isGlobal": false + }, + { + "key": "textError", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"error\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textInfo", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"info\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textInherit", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"inherit\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textPrimary", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"primary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSecondary", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"secondary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSizeLarge", + "className": "", + "description": "Styles applied to the root element if `size=\"large\"` and `variant=\"text\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSizeMedium", + "className": "", + "description": "Styles applied to the root element if `size=\"medium\"` and `variant=\"text\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSizeSmall", + "className": "", + "description": "Styles applied to the root element if `size=\"small\"` and `variant=\"text\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSuccess", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"success\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textWarning", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"warning\"`.", + "isGlobal": false, + "isDeprecated": true + } + ], + "muiName": "SignInButton", + "filename": "/packages/toolpad-core/src/Account/SignInButton.tsx", + "inheritance": null, + "demos": "", + "cssComponent": false +} diff --git a/docs/pages/toolpad/core/api/sign-out-button.js b/docs/pages/toolpad/core/api/sign-out-button.js new file mode 100644 index 00000000000..8f0e9e7e8fe --- /dev/null +++ b/docs/pages/toolpad/core/api/sign-out-button.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './sign-out-button.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docs-toolpad/translations/api-docs/sign-out-button', + false, + /\.\/sign-out-button.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/toolpad/core/api/sign-out-button.json b/docs/pages/toolpad/core/api/sign-out-button.json new file mode 100644 index 00000000000..9305dc4de49 --- /dev/null +++ b/docs/pages/toolpad/core/api/sign-out-button.json @@ -0,0 +1,369 @@ +{ + "props": {}, + "name": "SignOutButton", + "imports": ["import { SignOutButton } from '@toolpad/core/Account';"], + "classes": [ + { + "key": "colorError", + "className": "", + "description": "Styles applied to the root element if `color=\"error\"`.", + "isGlobal": false + }, + { + "key": "colorInfo", + "className": "", + "description": "Styles applied to the root element if `color=\"info\"`.", + "isGlobal": false + }, + { + "key": "colorInherit", + "className": "", + "description": "Styles applied to the root element if `color=\"inherit\"`.", + "isGlobal": false + }, + { + "key": "colorPrimary", + "className": "", + "description": "Styles applied to the root element if `color=\"primary\"`.", + "isGlobal": false + }, + { + "key": "colorSecondary", + "className": "", + "description": "Styles applied to the root element if `color=\"secondary\"`.", + "isGlobal": false + }, + { + "key": "colorSuccess", + "className": "", + "description": "Styles applied to the root element if `color=\"success\"`.", + "isGlobal": false + }, + { + "key": "colorWarning", + "className": "", + "description": "Styles applied to the root element if `color=\"warning\"`.", + "isGlobal": false + }, + { + "key": "contained", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"`.", + "isGlobal": false + }, + { + "key": "containedError", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"error\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedInfo", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"info\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedInherit", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"inherit\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedPrimary", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"primary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSecondary", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"secondary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSizeLarge", + "className": "", + "description": "Styles applied to the root element if `size=\"large\"` and `variant=\"contained\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSizeMedium", + "className": "", + "description": "Styles applied to the root element if `size=\"medium\"` and `variant=\"contained\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSizeSmall", + "className": "", + "description": "Styles applied to the root element if `size=\"small\"` and `variant=\"contained\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedSuccess", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"success\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "containedWarning", + "className": "", + "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"warning\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "disabled", + "className": "", + "description": "State class applied to the root element if `disabled={true}`.", + "isGlobal": false + }, + { + "key": "disableElevation", + "className": "", + "description": "Styles applied to the root element if `disableElevation={true}`.", + "isGlobal": false + }, + { + "key": "endIcon", + "className": "", + "description": "Styles applied to the endIcon element if supplied.", + "isGlobal": false + }, + { + "key": "focusVisible", + "className": "", + "description": "State class applied to the ButtonBase root element if the button is keyboard focused.", + "isGlobal": false + }, + { + "key": "fullWidth", + "className": "", + "description": "Styles applied to the root element if `fullWidth={true}`.", + "isGlobal": false + }, + { + "key": "icon", + "className": "", + "description": "Styles applied to the icon element if supplied", + "isGlobal": false + }, + { + "key": "iconSizeLarge", + "className": "", + "description": "Styles applied to the icon element if supplied and `size=\"large\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "iconSizeMedium", + "className": "", + "description": "Styles applied to the icon element if supplied and `size=\"medium\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "iconSizeSmall", + "className": "", + "description": "Styles applied to the icon element if supplied and `size=\"small\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlined", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"`.", + "isGlobal": false + }, + { + "key": "outlinedError", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"error\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedInfo", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"info\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedInherit", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"inherit\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedPrimary", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"primary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSecondary", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"secondary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSizeLarge", + "className": "", + "description": "Styles applied to the root element if `size=\"large\"` and `variant=\"outlined\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSizeMedium", + "className": "", + "description": "Styles applied to the root element if `size=\"medium\"` and `variant=\"outlined\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSizeSmall", + "className": "", + "description": "Styles applied to the root element if `size=\"small\"` and `variant=\"outlined\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedSuccess", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"success\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "outlinedWarning", + "className": "", + "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"warning\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "root", + "className": "", + "description": "Styles applied to the root element.", + "isGlobal": false + }, + { + "key": "sizeLarge", + "className": "", + "description": "Styles applied to the root element if `size=\"large\"`.", + "isGlobal": false + }, + { + "key": "sizeMedium", + "className": "", + "description": "Styles applied to the root element if `size=\"medium\"`.", + "isGlobal": false + }, + { + "key": "sizeSmall", + "className": "", + "description": "Styles applied to the root element if `size=\"small\"`.", + "isGlobal": false + }, + { + "key": "startIcon", + "className": "", + "description": "Styles applied to the startIcon element if supplied.", + "isGlobal": false + }, + { + "key": "text", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"`.", + "isGlobal": false + }, + { + "key": "textError", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"error\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textInfo", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"info\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textInherit", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"inherit\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textPrimary", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"primary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSecondary", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"secondary\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSizeLarge", + "className": "", + "description": "Styles applied to the root element if `size=\"large\"` and `variant=\"text\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSizeMedium", + "className": "", + "description": "Styles applied to the root element if `size=\"medium\"` and `variant=\"text\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSizeSmall", + "className": "", + "description": "Styles applied to the root element if `size=\"small\"` and `variant=\"text\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textSuccess", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"success\"`.", + "isGlobal": false, + "isDeprecated": true + }, + { + "key": "textWarning", + "className": "", + "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"warning\"`.", + "isGlobal": false, + "isDeprecated": true + } + ], + "muiName": "SignOutButton", + "filename": "/packages/toolpad-core/src/Account/SignOutButton.tsx", + "inheritance": null, + "demos": "", + "cssComponent": false +} diff --git a/docs/translations/api-docs/account-popover-footer/account-popover-footer.json b/docs/translations/api-docs/account-popover-footer/account-popover-footer.json new file mode 100644 index 00000000000..f93d4cbd8c7 --- /dev/null +++ b/docs/translations/api-docs/account-popover-footer/account-popover-footer.json @@ -0,0 +1 @@ +{ "componentDescription": "", "propDescriptions": {}, "classDescriptions": {} } diff --git a/docs/translations/api-docs/account-popover-header/account-popover-header.json b/docs/translations/api-docs/account-popover-header/account-popover-header.json new file mode 100644 index 00000000000..f93d4cbd8c7 --- /dev/null +++ b/docs/translations/api-docs/account-popover-header/account-popover-header.json @@ -0,0 +1 @@ +{ "componentDescription": "", "propDescriptions": {}, "classDescriptions": {} } diff --git a/docs/translations/api-docs/account-preview/account-preview.json b/docs/translations/api-docs/account-preview/account-preview.json new file mode 100644 index 00000000000..8724209a3a9 --- /dev/null +++ b/docs/translations/api-docs/account-preview/account-preview.json @@ -0,0 +1,16 @@ +{ + "componentDescription": "The AccountPreview component displays user account information.", + "propDescriptions": { + "handleClick": { "description": "The handler used when the preview is expanded" }, + "open": { "description": "The state of the Account popover" }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, + "variant": { "description": "The type of account details to display." } + }, + "classDescriptions": {}, + "slotDescriptions": { + "avatar": "The component used for the Avatar", + "avatarIconButton": "The component used for the avatar icon button in the condensed variant", + "moreIconButton": "The component used for the overflow icon button in the expanded variant" + } +} diff --git a/docs/translations/api-docs/account/account.json b/docs/translations/api-docs/account/account.json index 58620d755a2..de7187c1fcc 100644 --- a/docs/translations/api-docs/account/account.json +++ b/docs/translations/api-docs/account/account.json @@ -7,7 +7,9 @@ }, "classDescriptions": {}, "slotDescriptions": { - "menuItems": "The component used for the custom menu items.", + "popover": "The component used for the account popover menu", + "popoverContent": "The component used for the content of account popover", + "preview": "The component used for the account preview", "signInButton": "The component used for the sign in button.", "signOutButton": "The component used for the sign out button." } diff --git a/docs/translations/api-docs/sign-in-button/sign-in-button.json b/docs/translations/api-docs/sign-in-button/sign-in-button.json new file mode 100644 index 00000000000..18077dbb50a --- /dev/null +++ b/docs/translations/api-docs/sign-in-button/sign-in-button.json @@ -0,0 +1,301 @@ +{ + "componentDescription": "", + "propDescriptions": {}, + "classDescriptions": { + "colorError": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"error\"" + }, + "colorInfo": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"info\"" + }, + "colorInherit": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"inherit\"" + }, + "colorPrimary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"primary\"" + }, + "colorSecondary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"secondary\"" + }, + "colorSuccess": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"success\"" + }, + "colorWarning": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"warning\"" + }, + "contained": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\"" + }, + "containedError": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"error\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorError classes instead. See Migrating from deprecated APIs for more details." + }, + "containedInfo": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"info\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorInfo classes instead. See Migrating from deprecated APIs for more details." + }, + "containedInherit": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"inherit\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorInherit classes instead. See Migrating from deprecated APIs for more details." + }, + "containedPrimary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"primary\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorPrimary classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSecondary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"secondary\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorSecondary classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"large\" and variant=\"contained\"", + "deprecationInfo": "Combine the .MuiButton-sizeLarge and .MuiButton-contained classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"medium\" and variant=\"contained\"", + "deprecationInfo": "Combine the .MuiButton-sizeMedium and .MuiButton-contained classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"small\" and variant=\"contained\"", + "deprecationInfo": "Combine the .MuiButton-sizeSmall and .MuiButton-contained classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSuccess": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"success\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorSuccess classes instead. See Migrating from deprecated APIs for more details." + }, + "containedWarning": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"warning\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorWarning classes instead. See Migrating from deprecated APIs for more details." + }, + "disabled": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "disabled={true}" + }, + "disableElevation": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "disableElevation={true}" + }, + "endIcon": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the endIcon element", + "conditions": "supplied" + }, + "focusVisible": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the ButtonBase root element", + "conditions": "the button is keyboard focused" + }, + "fullWidth": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "fullWidth={true}" + }, + "icon": { "description": "Styles applied to the icon element if supplied" }, + "iconSizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the icon element", + "conditions": "supplied and size=\"large\"", + "deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeLarge classes instead. See Migrating from deprecated APIs for more details." + }, + "iconSizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the icon element", + "conditions": "supplied and size=\"medium\"", + "deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeMedium classes instead. See Migrating from deprecated APIs for more details." + }, + "iconSizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the icon element", + "conditions": "supplied and size=\"small\"", + "deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeSmall classes instead. See Migrating from deprecated APIs for more details." + }, + "outlined": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\"" + }, + "outlinedError": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"error\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorError classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedInfo": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"info\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorInfo classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedInherit": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"inherit\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorInherit classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedPrimary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"primary\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorPrimary classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSecondary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"secondary\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorSecondary classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"large\" and variant=\"outlined\"", + "deprecationInfo": "Combine the .MuiButton-sizeLarge and .MuiButton-outlined classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"medium\" and variant=\"outlined\"", + "deprecationInfo": "Combine the .MuiButton-sizeMedium and .MuiButton-outlined classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"small\" and variant=\"outlined\"", + "deprecationInfo": "Combine the .MuiButton-sizeSmall and .MuiButton-outlined classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSuccess": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"success\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorSuccess classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedWarning": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"warning\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorWarning classes instead. See Migrating from deprecated APIs for more details." + }, + "root": { "description": "Styles applied to the root element." }, + "sizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"large\"" + }, + "sizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"medium\"" + }, + "sizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"small\"" + }, + "startIcon": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the startIcon element", + "conditions": "supplied" + }, + "text": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\"" + }, + "textError": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"error\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorError classes instead. See Migrating from deprecated APIs for more details." + }, + "textInfo": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"info\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorInfo classes instead. See Migrating from deprecated APIs for more details." + }, + "textInherit": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"inherit\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorInherit classes instead. See Migrating from deprecated APIs for more details." + }, + "textPrimary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"primary\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorPrimary classes instead. See Migrating from deprecated APIs for more details." + }, + "textSecondary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"secondary\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorSecondary classes instead. See Migrating from deprecated APIs for more details." + }, + "textSizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"large\" and variant=\"text\"", + "deprecationInfo": "Combine the .MuiButton-sizeLarge and .MuiButton-text classes instead. See Migrating from deprecated APIs for more details." + }, + "textSizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"medium\" and variant=\"text\"", + "deprecationInfo": "Combine the .MuiButton-sizeMedium and .MuiButton-text classes instead. See Migrating from deprecated APIs for more details." + }, + "textSizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"small\" and variant=\"text\"", + "deprecationInfo": "Combine the .MuiButton-sizeSmall and .MuiButton-text classes instead. See Migrating from deprecated APIs for more details." + }, + "textSuccess": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"success\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorSuccess classes instead. See Migrating from deprecated APIs for more details." + }, + "textWarning": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"warning\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorWarning classes instead. See Migrating from deprecated APIs for more details." + } + } +} diff --git a/docs/translations/api-docs/sign-out-button/sign-out-button.json b/docs/translations/api-docs/sign-out-button/sign-out-button.json new file mode 100644 index 00000000000..18077dbb50a --- /dev/null +++ b/docs/translations/api-docs/sign-out-button/sign-out-button.json @@ -0,0 +1,301 @@ +{ + "componentDescription": "", + "propDescriptions": {}, + "classDescriptions": { + "colorError": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"error\"" + }, + "colorInfo": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"info\"" + }, + "colorInherit": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"inherit\"" + }, + "colorPrimary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"primary\"" + }, + "colorSecondary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"secondary\"" + }, + "colorSuccess": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"success\"" + }, + "colorWarning": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "color=\"warning\"" + }, + "contained": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\"" + }, + "containedError": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"error\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorError classes instead. See Migrating from deprecated APIs for more details." + }, + "containedInfo": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"info\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorInfo classes instead. See Migrating from deprecated APIs for more details." + }, + "containedInherit": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"inherit\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorInherit classes instead. See Migrating from deprecated APIs for more details." + }, + "containedPrimary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"primary\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorPrimary classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSecondary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"secondary\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorSecondary classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"large\" and variant=\"contained\"", + "deprecationInfo": "Combine the .MuiButton-sizeLarge and .MuiButton-contained classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"medium\" and variant=\"contained\"", + "deprecationInfo": "Combine the .MuiButton-sizeMedium and .MuiButton-contained classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"small\" and variant=\"contained\"", + "deprecationInfo": "Combine the .MuiButton-sizeSmall and .MuiButton-contained classes instead. See Migrating from deprecated APIs for more details." + }, + "containedSuccess": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"success\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorSuccess classes instead. See Migrating from deprecated APIs for more details." + }, + "containedWarning": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"contained\" and color=\"warning\"", + "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorWarning classes instead. See Migrating from deprecated APIs for more details." + }, + "disabled": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "disabled={true}" + }, + "disableElevation": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "disableElevation={true}" + }, + "endIcon": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the endIcon element", + "conditions": "supplied" + }, + "focusVisible": { + "description": "State class applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the ButtonBase root element", + "conditions": "the button is keyboard focused" + }, + "fullWidth": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "fullWidth={true}" + }, + "icon": { "description": "Styles applied to the icon element if supplied" }, + "iconSizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the icon element", + "conditions": "supplied and size=\"large\"", + "deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeLarge classes instead. See Migrating from deprecated APIs for more details." + }, + "iconSizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the icon element", + "conditions": "supplied and size=\"medium\"", + "deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeMedium classes instead. See Migrating from deprecated APIs for more details." + }, + "iconSizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the icon element", + "conditions": "supplied and size=\"small\"", + "deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeSmall classes instead. See Migrating from deprecated APIs for more details." + }, + "outlined": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\"" + }, + "outlinedError": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"error\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorError classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedInfo": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"info\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorInfo classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedInherit": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"inherit\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorInherit classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedPrimary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"primary\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorPrimary classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSecondary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"secondary\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorSecondary classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"large\" and variant=\"outlined\"", + "deprecationInfo": "Combine the .MuiButton-sizeLarge and .MuiButton-outlined classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"medium\" and variant=\"outlined\"", + "deprecationInfo": "Combine the .MuiButton-sizeMedium and .MuiButton-outlined classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"small\" and variant=\"outlined\"", + "deprecationInfo": "Combine the .MuiButton-sizeSmall and .MuiButton-outlined classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedSuccess": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"success\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorSuccess classes instead. See Migrating from deprecated APIs for more details." + }, + "outlinedWarning": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"outlined\" and color=\"warning\"", + "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorWarning classes instead. See Migrating from deprecated APIs for more details." + }, + "root": { "description": "Styles applied to the root element." }, + "sizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"large\"" + }, + "sizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"medium\"" + }, + "sizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"small\"" + }, + "startIcon": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the startIcon element", + "conditions": "supplied" + }, + "text": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\"" + }, + "textError": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"error\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorError classes instead. See Migrating from deprecated APIs for more details." + }, + "textInfo": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"info\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorInfo classes instead. See Migrating from deprecated APIs for more details." + }, + "textInherit": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"inherit\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorInherit classes instead. See Migrating from deprecated APIs for more details." + }, + "textPrimary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"primary\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorPrimary classes instead. See Migrating from deprecated APIs for more details." + }, + "textSecondary": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"secondary\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorSecondary classes instead. See Migrating from deprecated APIs for more details." + }, + "textSizeLarge": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"large\" and variant=\"text\"", + "deprecationInfo": "Combine the .MuiButton-sizeLarge and .MuiButton-text classes instead. See Migrating from deprecated APIs for more details." + }, + "textSizeMedium": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"medium\" and variant=\"text\"", + "deprecationInfo": "Combine the .MuiButton-sizeMedium and .MuiButton-text classes instead. See Migrating from deprecated APIs for more details." + }, + "textSizeSmall": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "size=\"small\" and variant=\"text\"", + "deprecationInfo": "Combine the .MuiButton-sizeSmall and .MuiButton-text classes instead. See Migrating from deprecated APIs for more details." + }, + "textSuccess": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"success\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorSuccess classes instead. See Migrating from deprecated APIs for more details." + }, + "textWarning": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "variant=\"text\" and color=\"warning\"", + "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorWarning classes instead. See Migrating from deprecated APIs for more details." + } + } +} diff --git a/packages/toolpad-core/src/Account/Account.tsx b/packages/toolpad-core/src/Account/Account.tsx index 7ea40743f0e..b4aace2f797 100644 --- a/packages/toolpad-core/src/Account/Account.tsx +++ b/packages/toolpad-core/src/Account/Account.tsx @@ -1,33 +1,33 @@ import * as React from 'react'; -import { styled } from '@mui/material/styles'; import PropTypes from 'prop-types'; +import Button from '@mui/material/Button'; import Popover from '@mui/material/Popover'; import Divider from '@mui/material/Divider'; -import Button, { ButtonProps } from '@mui/material/Button'; -import IconButton, { IconButtonProps } from '@mui/material/IconButton'; -import Tooltip from '@mui/material/Tooltip'; -import Logout from '@mui/icons-material/Logout'; -import { Typography } from '@mui/material'; -import { SessionAvatar } from './SessionAvatar'; +import Stack from '@mui/material/Stack'; +import { SignInButton } from './SignInButton'; +import { SignOutButton } from './SignOutButton'; +import { AccountPreview, AccountPreviewProps } from './AccountPreview'; +import { AccountPopoverHeader } from './AccountPopoverHeader'; +import { AccountPopoverFooter } from './AccountPopoverFooter'; import { SessionContext, AuthenticationContext } from '../AppProvider/AppProvider'; -import DEFAULT_LOCALE_TEXT from '../shared/locales/en'; - -const AccountInfoContainer = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - padding: theme.spacing(2), - gap: theme.spacing(2), -})); - -const SignOutContainer = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'row', - padding: theme.spacing(1), - justifyContent: 'flex-end', -})); +import { LocaleProvider, useLocaleText } from '../shared/locales/LocaleContext'; export interface AccountSlots { + /** + * The component used for the account preview + * @default AccountPreview + */ + preview?: React.ElementType; + /** + * The component used for the account popover menu + * @default Popover + */ + popover?: React.ElementType; + /** + * The component used for the content of account popover + * @default Stack + */ + popoverContent?: React.ElementType; /** * The component used for the sign in button. * @default Button @@ -35,14 +35,9 @@ export interface AccountSlots { signInButton?: React.ElementType; /** * The component used for the sign out button. - * @default MenuItem + * @default Button */ signOutButton?: React.ElementType; - /** - * The component used for the custom menu items. - * @default null - */ - menuItems?: React.ElementType; } export interface AccountProps { @@ -54,15 +49,16 @@ export interface AccountProps { * The props used for each slot inside. */ slotProps?: { - signInButton?: ButtonProps; - signOutButton?: ButtonProps; - iconButton?: IconButtonProps; + preview?: AccountPreviewProps; + popover?: React.ComponentProps; + popoverContent?: React.ComponentProps; + signInButton?: React.ComponentProps; + signOutButton?: React.ComponentProps; }; /** * The labels for the account component. - * @default DEFAULT_LOCALE_TEXT */ - localeText?: typeof DEFAULT_LOCALE_TEXT; + localeText?: Partial>; } /** * @@ -77,132 +73,101 @@ export interface AccountProps { * - [Account API](https://mui.com/toolpad/core/api/account) */ function Account(props: AccountProps) { - const { slots, slotProps, localeText = DEFAULT_LOCALE_TEXT } = props; - const [anchorEl, setAnchorEl] = React.useState(null); + const { localeText } = props; + const { slots, slotProps } = props; + const [anchorEl, setAnchorEl] = React.useState(null); const session = React.useContext(SessionContext); const authentication = React.useContext(AuthenticationContext); const open = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { + + const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; + const handleClose = () => { setAnchorEl(null); }; + if (!authentication) { return null; } if (!session?.user) { - return slots?.signInButton ? ( - - ) : ( - + return ( + + {slots?.signInButton ? ( + + ) : ( + + )} + ); } + return ( - -
- - - - - -
- + {slots?.preview ? ( + + ) : ( + + )} + {slots?.popover ? ( + + ) : ( + - - -
- {session.user.name} - {session.user.email} -
-
- - {slots?.menuItems ? : null} - {slots?.signOutButton ? ( - - ) : ( - - - - )} -
-
+ ...slotProps?.popover?.slotProps, + }} + > + {slots?.popoverContent ? ( + + ) : ( + + + + + + + + + + )} +
+ )} + ); } @@ -213,17 +178,33 @@ Account.propTypes /* remove-proptypes */ = { // └─────────────────────────────────────────────────────────────────────┘ /** * The labels for the account component. - * @default DEFAULT_LOCALE_TEXT */ localeText: PropTypes.shape({ - signInLabel: PropTypes.string.isRequired, - signOutLabel: PropTypes.string.isRequired, + iconButtonAriaLabel: PropTypes.string, + signInLabel: PropTypes.string, + signOutLabel: PropTypes.string, }), /** * The props used for each slot inside. */ slotProps: PropTypes.shape({ - iconButton: PropTypes.object, + popover: PropTypes.object, + popoverContent: PropTypes.object, + preview: PropTypes.shape({ + handleClick: PropTypes.func, + open: PropTypes.bool, + slotProps: PropTypes.shape({ + avatar: PropTypes.object, + avatarIconButton: PropTypes.object, + moreIconButton: PropTypes.object, + }), + slots: PropTypes.shape({ + avatar: PropTypes.elementType, + avatarIconButton: PropTypes.elementType, + moreIconButton: PropTypes.elementType, + }), + variant: PropTypes.oneOf(['condensed', 'expanded']), + }), signInButton: PropTypes.object, signOutButton: PropTypes.object, }), @@ -231,7 +212,9 @@ Account.propTypes /* remove-proptypes */ = { * The components used for each slot inside. */ slots: PropTypes.shape({ - menuItems: PropTypes.elementType, + popover: PropTypes.elementType, + popoverContent: PropTypes.elementType, + preview: PropTypes.elementType, signInButton: PropTypes.elementType, signOutButton: PropTypes.elementType, }), diff --git a/packages/toolpad-core/src/Account/AccountPopoverFooter.tsx b/packages/toolpad-core/src/Account/AccountPopoverFooter.tsx new file mode 100644 index 00000000000..62696b0b449 --- /dev/null +++ b/packages/toolpad-core/src/Account/AccountPopoverFooter.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import Box, { BoxProps } from '@mui/material/Box'; + +export interface AccountPopoverFooterProps extends BoxProps { + children?: React.ReactNode; +} + +export /** + * + * Demos: + * + * - [Account](https://mui.com/toolpad/core/react-account/) + * + * API: + * + * - [AccountPopoverFooter API](https://mui.com/toolpad/core/api/account-popover-footer) + */ function AccountPopoverFooter(props: AccountPopoverFooterProps) { + const { children, ...rest } = props; + return ( + + {children} + + ); +} diff --git a/packages/toolpad-core/src/Account/AccountPopoverHeader.tsx b/packages/toolpad-core/src/Account/AccountPopoverHeader.tsx new file mode 100644 index 00000000000..f22591550d3 --- /dev/null +++ b/packages/toolpad-core/src/Account/AccountPopoverHeader.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import Stack, { StackProps } from '@mui/material/Stack'; + +export interface AccountPopoverHeaderProps extends StackProps { + children?: React.ReactNode; +} + +export /** + * + * Demos: + * + * - [Account](https://mui.com/toolpad/core/react-account/) + * + * API: + * + * - [AccountPopoverHeader API](https://mui.com/toolpad/core/api/account-popover-header) + */ function AccountPopoverHeader(props: AccountPopoverHeaderProps) { + const { children, ...rest } = props; + return {children}; +} diff --git a/packages/toolpad-core/src/Account/AccountPreview.test.tsx b/packages/toolpad-core/src/Account/AccountPreview.test.tsx new file mode 100644 index 00000000000..9024d9cef3a --- /dev/null +++ b/packages/toolpad-core/src/Account/AccountPreview.test.tsx @@ -0,0 +1,82 @@ +/// + +/** + * @vitest-environment jsdom + */ + +import * as React from 'react'; +import { describe, test, expect, vi } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { AccountPreview } from './AccountPreview'; +import { AppProvider } from '../AppProvider'; + +describe('AccountPreview', () => { + const auth = { signIn: vi.fn(), signOut: vi.fn() }; + const session = { + user: { name: 'John Doe', email: 'john@example.com', image: 'https://example.com/avatar.jpg' }, + }; + + test('renders nothing when no session is provided', () => { + render( + + + , + ); + + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + test('displays condensed variant by default', () => { + render( + + + , + ); + + const avatar = screen.getByRole('img', { name: 'John Doe' }); + expect(avatar).toBeInTheDocument(); + expect(screen.queryByText('John Doe')).not.toBeInTheDocument(); + expect(avatar).toHaveAttribute('src', 'https://example.com/avatar.jpg'); + }); + + test('displays user name, email, and avatar in expanded variant', () => { + render( + + + , + ); + + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.getByText('john@example.com')).toBeInTheDocument(); + expect(screen.getByRole('img', { name: 'John Doe' })).toBeInTheDocument(); + }); + + test('calls handleClick when more icon button is clicked in expanded variant', async () => { + const handleClick = vi.fn(); + render( + + + , + ); + + const moreButton = screen.getByRole('button'); + await userEvent.click(moreButton); + + expect(handleClick).toHaveBeenCalled(); + }); + + test('calls handleClick when avatar is clicked in condensed variant', async () => { + const handleClick = vi.fn(); + render( + + + , + ); + + const avatarButton = screen.getByRole('button', { name: 'Current User' }); + await userEvent.click(avatarButton); + + expect(handleClick).toHaveBeenCalled(); + }); +}); diff --git a/packages/toolpad-core/src/Account/AccountPreview.tsx b/packages/toolpad-core/src/Account/AccountPreview.tsx new file mode 100644 index 00000000000..dc4455269c5 --- /dev/null +++ b/packages/toolpad-core/src/Account/AccountPreview.tsx @@ -0,0 +1,181 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Avatar, { AvatarProps } from '@mui/material/Avatar'; +import Typography from '@mui/material/Typography'; +import Tooltip from '@mui/material/Tooltip'; +import Stack from '@mui/material/Stack'; +import IconButton, { IconButtonProps } from '@mui/material/IconButton'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import { SessionContext } from '../AppProvider'; +import { useLocaleText } from '../shared/locales/LocaleContext'; + +export type AccountPreviewVariant = 'condensed' | 'expanded'; + +export interface AccountPreviewSlots { + /** + * The component used for the Avatar + * @default Avatar + */ + avatar?: React.ElementType; + /** + * The component used for the overflow icon button in the expanded variant + * @default IconButton + */ + moreIconButton?: React.ElementType; + /** + * The component used for the avatar icon button in the condensed variant + * @default IconButton + */ + avatarIconButton?: React.ElementType; +} + +export interface AccountPreviewProps { + /** + * The components used for each slot inside. + */ + slots?: AccountPreviewSlots; + /** + * The props used for each slot inside. + */ + slotProps?: { + avatar?: AvatarProps; + moreIconButton?: IconButtonProps; + avatarIconButton?: IconButtonProps; + }; + /** + * The type of account details to display. + * @property {'condensed'} condensed - Shows only the user's avatar. + * @property {'expanded'} expanded - Displays the user's avatar, name, and email if available. + * @default 'condensed' + */ + variant?: AccountPreviewVariant; + /** + * The handler used when the preview is expanded + */ + handleClick?: React.MouseEventHandler; + /** + * The state of the Account popover + * @default false + */ + open?: boolean; +} + +/** + * The AccountPreview component displays user account information. + * + * Demos: + * + * - [Account](https://mui.com/toolpad/core/react-account/) + * + * API: + * + * - [AccountPreview API](https://mui.com/toolpad/core/api/account-preview) + */ +function AccountPreview(props: AccountPreviewProps) { + const { slots, variant = 'condensed', slotProps, open, handleClick } = props; + const session = React.useContext(SessionContext); + const localeText = useLocaleText(); + + if (!session || !session.user) { + return null; + } + + const avatarContent = slots?.avatar ? ( + + ) : ( + + ); + + if (variant === 'expanded') { + return ( + + {avatarContent} + + + {session.user?.name} + + + {session.user?.email} + + + {handleClick && + (slots?.moreIconButton ? ( + + ) : ( + + + + ))} + + ); + } + + return ( + + {slots?.avatarIconButton ? ( + + ) : ( + + {avatarContent} + + )} + + ); +} + +AccountPreview.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The handler used when the preview is expanded + */ + handleClick: PropTypes.func, + /** + * The state of the Account popover + * @default false + */ + open: PropTypes.bool, + /** + * The props used for each slot inside. + */ + slotProps: PropTypes.shape({ + avatar: PropTypes.object, + avatarIconButton: PropTypes.object, + moreIconButton: PropTypes.object, + }), + /** + * The components used for each slot inside. + */ + slots: PropTypes.shape({ + avatar: PropTypes.elementType, + }), + /** + * The type of account details to display. + * @property {'condensed'} condensed - Shows only the user's avatar. + * @property {'expanded'} expanded - Displays the user's avatar, name, and email if available. + * @default 'condensed' + */ + variant: PropTypes.oneOf(['condensed', 'expanded']), +} as any; + +export { AccountPreview }; diff --git a/packages/toolpad-core/src/Account/SessionAvatar.tsx b/packages/toolpad-core/src/Account/SessionAvatar.tsx deleted file mode 100644 index b69247e8a5b..00000000000 --- a/packages/toolpad-core/src/Account/SessionAvatar.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import Avatar, { AvatarProps } from '@mui/material/Avatar'; -import { Session } from '../AppProvider/AppProvider'; - -/** - * @ignore - internal component. - */ - -export interface SessionAvatarProps extends AvatarProps { - session: Session; -} - -export function SessionAvatar(props: SessionAvatarProps) { - const { session, ...rest } = props; - return ( - - ); -} diff --git a/packages/toolpad-core/src/Account/SignInButton.tsx b/packages/toolpad-core/src/Account/SignInButton.tsx new file mode 100644 index 00000000000..9c67a964f05 --- /dev/null +++ b/packages/toolpad-core/src/Account/SignInButton.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import Button, { ButtonProps } from '@mui/material/Button'; +import { AuthenticationContext } from '../AppProvider/AppProvider'; +import { useLocaleText } from '../shared/locales/LocaleContext'; + +export /** + * + * Demos: + * + * - [Account](https://mui.com/toolpad/core/react-account/) + * + * API: + * + * - [SignInButton API](https://mui.com/toolpad/core/api/sign-in-button) + */ function SignInButton(props: ButtonProps) { + const authentication = React.useContext(AuthenticationContext); + const localeText = useLocaleText(); + + return ( + + ); +} diff --git a/packages/toolpad-core/src/Account/SignOutButton.tsx b/packages/toolpad-core/src/Account/SignOutButton.tsx new file mode 100644 index 00000000000..03d87b1fcaf --- /dev/null +++ b/packages/toolpad-core/src/Account/SignOutButton.tsx @@ -0,0 +1,44 @@ +import * as React from 'react'; +import Button, { ButtonProps } from '@mui/material/Button'; +import LogoutIcon from '@mui/icons-material/Logout'; +import { AuthenticationContext } from '../AppProvider/AppProvider'; +import { useLocaleText } from '../shared/locales/LocaleContext'; + +export type SignOutButtonProps = ButtonProps; + +export /** + * + * Demos: + * + * - [Account](https://mui.com/toolpad/core/react-account/) + * + * API: + * + * - [SignOutButton API](https://mui.com/toolpad/core/api/sign-out-button) + */ function SignOutButton(props: SignOutButtonProps) { + const authentication = React.useContext(AuthenticationContext); + const localeText = useLocaleText(); + + return ( + + ); +} diff --git a/packages/toolpad-core/src/Account/index.ts b/packages/toolpad-core/src/Account/index.ts index 4c71833ffeb..cb01e597927 100644 --- a/packages/toolpad-core/src/Account/index.ts +++ b/packages/toolpad-core/src/Account/index.ts @@ -1 +1,6 @@ export * from './Account'; +export * from './AccountPreview'; +export * from './AccountPopoverHeader'; +export * from './AccountPopoverFooter'; +export * from './SignOutButton'; +export * from './SignInButton'; diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx index 606e7a0d227..d286dc0b477 100644 --- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx +++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx @@ -479,16 +479,21 @@ DashboardLayout.propTypes /* remove-proptypes */ = { }), toolbarAccount: PropTypes.shape({ localeText: PropTypes.shape({ - signInLabel: PropTypes.string.isRequired, - signOutLabel: PropTypes.string.isRequired, + iconButtonAriaLabel: PropTypes.string, + signInLabel: PropTypes.string, + signOutLabel: PropTypes.string, }), slotProps: PropTypes.shape({ - iconButton: PropTypes.object, + popover: PropTypes.object, + popoverContent: PropTypes.object, + preview: PropTypes.object, signInButton: PropTypes.object, signOutButton: PropTypes.object, }), slots: PropTypes.shape({ - menuItems: PropTypes.elementType, + popover: PropTypes.elementType, + popoverContent: PropTypes.elementType, + preview: PropTypes.elementType, signInButton: PropTypes.elementType, signOutButton: PropTypes.elementType, }), diff --git a/packages/toolpad-core/src/shared/locales/LocaleContext.tsx b/packages/toolpad-core/src/shared/locales/LocaleContext.tsx new file mode 100644 index 00000000000..abfb38d6d5d --- /dev/null +++ b/packages/toolpad-core/src/shared/locales/LocaleContext.tsx @@ -0,0 +1,39 @@ +'use client'; + +import * as React from 'react'; +import DEFAULT_LOCALE_TEXT from './en'; + +export type LocaleContextType = { + // Account + signInLabel?: string; + signOutLabel?: string; + // Account Preview + iconButtonAriaLabel?: string; +}; + +export const LocaleContext = React.createContext(DEFAULT_LOCALE_TEXT); + +export interface LocaleProviderProps { + localeText?: Partial; + children: React.ReactNode; +} + +/** + * @ignore - internal component. + */ +export function LocaleProvider({ localeText, children }: LocaleProviderProps) { + const mergedLocaleText = React.useMemo( + () => ({ ...DEFAULT_LOCALE_TEXT, ...localeText }), + [localeText], + ); + + return {children}; +} + +/** + * @ignore - internal hook. + */ + +export function useLocaleText() { + return React.useContext(LocaleContext); +} diff --git a/packages/toolpad-core/src/shared/locales/en.tsx b/packages/toolpad-core/src/shared/locales/en.tsx index a3f075cdbf8..9717c8b3a3c 100644 --- a/packages/toolpad-core/src/shared/locales/en.tsx +++ b/packages/toolpad-core/src/shared/locales/en.tsx @@ -2,6 +2,8 @@ const TOOLPAD_CORE_DEFAULT_LOCALE_TEXT = { // Account signInLabel: 'Sign In', signOutLabel: 'Sign Out', + // Account Preview + iconButtonAriaLabel: 'Current User', }; export default TOOLPAD_CORE_DEFAULT_LOCALE_TEXT; diff --git a/playground/nextjs/src/app/(dashboard)/SidebarFooterAccount.tsx b/playground/nextjs/src/app/(dashboard)/SidebarFooterAccount.tsx new file mode 100644 index 00000000000..aceb55524f4 --- /dev/null +++ b/playground/nextjs/src/app/(dashboard)/SidebarFooterAccount.tsx @@ -0,0 +1,104 @@ +'use client'; + +import * as React from 'react'; + +import Typography from '@mui/material/Typography'; +import Stack from '@mui/material/Stack'; +import MenuList from '@mui/material/MenuList'; +import MenuItem from '@mui/material/MenuItem'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import Avatar from '@mui/material/Avatar'; +import Divider from '@mui/material/Divider'; + +import { + AccountPreview, + AccountPopoverFooter, + SignOutButton, + AccountPreviewProps, +} from '@toolpad/core/Account'; + +export function AccountSidebarPreview(props: AccountPreviewProps) { + const { handleClick, open } = props; + return ( + + + + + ); +} + +const accounts = [ + { + id: 1, + name: 'Bharat Kashyap', + email: 'bharatkashyap@outlook.com', + image: 'https://avatars.githubusercontent.com/u/19550456', + projects: [ + { + id: 3, + title: 'Project X', + }, + ], + }, + { + id: 2, + name: 'Bharat MUI', + email: 'bharat@mui.com', + color: '#8B4513', // Brown color + projects: [{ id: 4, title: 'Project A' }], + }, +]; + +export function SidebarFooterAccountPopover() { + return ( + + + Accounts + + + {accounts.map((account) => ( + + + + {account.name[0]} + + + + + ))} + + + + + + ); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2412c6a7cf9..000eb4bc7ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2052,7 +2052,7 @@ packages: resolution: {integrity: sha512-GjV0/mUEEXpi1U5ZgDprMRRgajGMRW3G5FjMr5KLKD8nT2fTG8+h/klV3+6Dm5739QE+K5+2e91qFKAYI3pmRg==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': ^7.25.8 '@babel/preset-typescript@7.25.7': resolution: {integrity: sha512-rkkpaXJZOFN45Fb+Gki0c+KMIglk4+zZXOoMJuyEK8y8Kkc8Jd3BDmP7qPsz0zQMJj+UD7EprF+AqAXcILnexw==} @@ -2122,7 +2122,7 @@ packages: '@docsearch/react@3.6.2': resolution: {integrity: sha512-rtZce46OOkVflCQH71IdbXSFK+S8iJZlUF56XBW5rIgx/eG5qoomC7Ag3anZson1bBac/JFQn7XOBfved/IMRA==} peerDependencies: - '@types/react': '>= 16.8.0 < 19.0.0' + '@types/react': ^18.3.11 react: '>= 16.8.0 < 19.0.0' react-dom: '>= 16.8.0 < 19.0.0' search-insights: '>= 1 < 3' @@ -2938,7 +2938,7 @@ packages: resolution: {integrity: sha512-LQZ2907rPMut/2Lq6qSnyP+nqOHLO3buMv91m7SdLpqp/lXU5+8vUXcf5oOwTNis6hfSvYGSQJ493Q00OzxDmQ==} engines: {node: '>=14.0.0'} peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 + '@types/react': ^18.3.11 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 peerDependenciesMeta: