diff --git a/docs/src/modules/components/ApiPage.js b/docs/src/modules/components/ApiPage.js index 568496a14bf8ce..abc2cfa1781d6d 100644 --- a/docs/src/modules/components/ApiPage.js +++ b/docs/src/modules/components/ApiPage.js @@ -21,6 +21,7 @@ import ClassesSection, { getClassesToC, } from 'docs/src/modules/components/ApiPage/sections/ClassesSection'; import SlotsSection from 'docs/src/modules/components/ApiPage/sections/SlotsSection'; +import { DEFAULT_API_LAYOUT_STORAGE_KEYS } from 'docs/src/modules/components/ApiPage/sections/ToggleDisplayOption'; export function getTranslatedHeader(t, header) { const translations = { @@ -68,7 +69,13 @@ Heading.propTypes = { }; export default function ApiPage(props) { - const { descriptions, disableAd = false, pageContent } = props; + const { + descriptions, + disableAd = false, + pageContent, + defaultLayout = 'table', + layoutStorageKey = DEFAULT_API_LAYOUT_STORAGE_KEYS, + } = props; const t = useTranslate(); const userLanguage = useUserLanguage(); @@ -257,6 +264,8 @@ export default function ApiPage(props) { propertiesDescriptions={propDescriptions} componentName={pageContent.name} spreadHint={spreadHint} + defaultLayout={defaultLayout} + layoutStorageKey={layoutStorageKey.props} /> {cssComponent && ( @@ -314,6 +323,8 @@ export default function ApiPage(props) { slotGuideLink && t('api-docs.slotDescription').replace(/{{slotGuideLink}}/, slotGuideLink) } + defaultLayout={defaultLayout} + layoutStorageKey={layoutStorageKey.slots} /> @@ -334,8 +347,14 @@ export default function ApiPage(props) { } ApiPage.propTypes = { + defaultLayout: PropTypes.oneOf(['collapsed', 'expanded', 'table']), descriptions: PropTypes.object.isRequired, disableAd: PropTypes.bool, + layoutStorageKey: PropTypes.shape({ + classes: PropTypes.string, + props: PropTypes.string, + slots: PropTypes.string, + }), pageContent: PropTypes.object.isRequired, }; diff --git a/docs/src/modules/components/ApiPage/sections/ClassesSection.tsx b/docs/src/modules/components/ApiPage/sections/ClassesSection.tsx index 8c1a714b58d022..1436bcdaac2b2a 100644 --- a/docs/src/modules/components/ApiPage/sections/ClassesSection.tsx +++ b/docs/src/modules/components/ApiPage/sections/ClassesSection.tsx @@ -4,7 +4,7 @@ import { useTranslate } from 'docs/src/modules/utils/i18n'; import { ComponentClassDefinition } from '@mui-internal/docs-utils'; import Box from '@mui/material/Box'; import ToggleDisplayOption, { - API_LAYOUT_STORAGE_KEYS, + ApiDisplayOptions, useApiPageOption, } from 'docs/src/modules/components/ApiPage/sections/ToggleDisplayOption'; import ClassesList, { getHash } from 'docs/src/modules/components/ApiPage/list/ClassesList'; @@ -49,6 +49,8 @@ export type ClassesSectionProps = { title: string; titleHash: string; level?: 'h2' | 'h3' | 'h4'; + defaultLayout: ApiDisplayOptions; + layoutStorageKey: string; displayClassKeys: boolean; styleOverridesLink: string; }; @@ -64,10 +66,12 @@ export default function ClassesSection(props: ClassesSectionProps) { level: Level = 'h2', displayClassKeys, styleOverridesLink, + defaultLayout, + layoutStorageKey, } = props; const t = useTranslate(); - const [displayOption, setDisplayOption] = useApiPageOption(API_LAYOUT_STORAGE_KEYS.classes); + const [displayOption, setDisplayOption] = useApiPageOption(layoutStorageKey, defaultLayout); if (!componentClasses || componentClasses.length === 0) { return null; @@ -101,7 +105,11 @@ export default function ClassesSection(props: ClassesSectionProps) { - + {spreadHint &&

} {displayOption === 'table' ? ( diff --git a/docs/src/modules/components/ApiPage/sections/PropertiesSection.js b/docs/src/modules/components/ApiPage/sections/PropertiesSection.js index a19e6ebe8c1eca..034fc15e8c4162 100644 --- a/docs/src/modules/components/ApiPage/sections/PropertiesSection.js +++ b/docs/src/modules/components/ApiPage/sections/PropertiesSection.js @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import Box from '@mui/material/Box'; import { useTranslate } from 'docs/src/modules/utils/i18n'; import ToggleDisplayOption, { - API_LAYOUT_STORAGE_KEYS, useApiPageOption, } from 'docs/src/modules/components/ApiPage/sections/ToggleDisplayOption'; import PropertiesList, { getHash } from 'docs/src/modules/components/ApiPage/list/PropertiesList'; @@ -51,10 +50,12 @@ export default function PropertiesSection(props) { spreadHint, hooksParameters = false, hooksReturnValue = false, + defaultLayout, + layoutStorageKey, } = props; const t = useTranslate(); - const [displayOption, setDisplayOption] = useApiPageOption(API_LAYOUT_STORAGE_KEYS.props); + const [displayOption, setDisplayOption] = useApiPageOption(layoutStorageKey, defaultLayout); const formatedProperties = Object.entries(properties) .filter(([, propData]) => propData.description !== '@ignore') .map(([propName, propData]) => { @@ -131,7 +132,11 @@ export default function PropertiesSection(props) { - + {spreadHint &&

} {displayOption === 'table' ? ( @@ -145,8 +150,10 @@ export default function PropertiesSection(props) { PropertiesSection.propTypes = { componentName: PropTypes.string, + defaultLayout: PropTypes.oneOf(['collapsed', 'expanded', 'table']).isRequired, hooksParameters: PropTypes.bool, hooksReturnValue: PropTypes.bool, + layoutStorageKey: PropTypes.string.isRequired, level: PropTypes.string, properties: PropTypes.object.isRequired, propertiesDescriptions: PropTypes.object.isRequired, diff --git a/docs/src/modules/components/ApiPage/sections/SlotsSection.tsx b/docs/src/modules/components/ApiPage/sections/SlotsSection.tsx index 966f578855f484..c93869bb90f81f 100644 --- a/docs/src/modules/components/ApiPage/sections/SlotsSection.tsx +++ b/docs/src/modules/components/ApiPage/sections/SlotsSection.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import Box from '@mui/material/Box'; import { useTranslate } from 'docs/src/modules/utils/i18n'; import ToggleDisplayOption, { - API_LAYOUT_STORAGE_KEYS, + ApiDisplayOptions, useApiPageOption, } from 'docs/src/modules/components/ApiPage/sections/ToggleDisplayOption'; import SlotsList from 'docs/src/modules/components/ApiPage/list/SlotsList'; @@ -16,6 +16,8 @@ export type SlotsSectionProps = { title?: string; titleHash?: string; level?: 'h2' | 'h3' | 'h4'; + defaultLayout: ApiDisplayOptions; + layoutStorageKey: string; spreadHint?: string; }; @@ -28,10 +30,12 @@ export default function SlotsSection(props: SlotsSectionProps) { titleHash = 'slots', level: Level = 'h2', spreadHint, + defaultLayout, + layoutStorageKey, } = props; const t = useTranslate(); - const [displayOption, setDisplayOption] = useApiPageOption(API_LAYOUT_STORAGE_KEYS.slots); + const [displayOption, setDisplayOption] = useApiPageOption(layoutStorageKey, defaultLayout); if (!componentSlots || componentSlots.length === 0) { return null; @@ -63,7 +67,11 @@ export default function SlotsSection(props: SlotsSectionProps) { - + {spreadHint &&

} {displayOption === 'table' ? ( diff --git a/docs/src/modules/components/ApiPage/sections/ToggleDisplayOption.tsx b/docs/src/modules/components/ApiPage/sections/ToggleDisplayOption.tsx index 0c6449b7efe7f3..753c0a5758c565 100644 --- a/docs/src/modules/components/ApiPage/sections/ToggleDisplayOption.tsx +++ b/docs/src/modules/components/ApiPage/sections/ToggleDisplayOption.tsx @@ -7,52 +7,21 @@ import TableChartRoundedIcon from '@mui/icons-material/TableChartRounded'; import ReorderRoundedIcon from '@mui/icons-material/ReorderRounded'; import useEnhancedEffect from '@mui/utils/useEnhancedEffect'; -type ApiDisplayOptions = 'collapsed' | 'expanded' | 'table'; +export type ApiDisplayOptions = 'collapsed' | 'expanded' | 'table'; const options: ApiDisplayOptions[] = ['collapsed', 'expanded', 'table']; -const DEFAULT_LAYOUT: ApiDisplayOptions = 'expanded'; -export const API_LAYOUT_STORAGE_KEYS = { - default: 'apiPage_default', +export const DEFAULT_API_LAYOUT_STORAGE_KEYS = { slots: 'apiPage_slots', props: 'apiPage_props', - css: 'apiPage_css', classes: 'apiPage_classes', } as const; -// https://stackoverflow.com/a/20084661 -function isBot() { - return /bot|googlebot|crawler|spider|robot|crawling/i.test(navigator.userAgent); -} - -function getRandomOption() { - if (isBot()) { - // When crawlers visit the page, they should not have to expand items - return DEFAULT_LAYOUT; - } - // A default layout is saved in localstorage at first render to make sure all section start with the same layout. - const savedDefaultOption = localStorage.getItem( - API_LAYOUT_STORAGE_KEYS.default, - ) as null | ApiDisplayOptions; - - if (savedDefaultOption !== null && options.includes(savedDefaultOption)) { - return savedDefaultOption; - } - - const randomOption = options[Math.floor(options.length * Math.random())]; - try { - localStorage.setItem(API_LAYOUT_STORAGE_KEYS.default, randomOption); - } catch (error) { - // Do nothing - } - return randomOption; -} - let neverHydrated = true; -function getOption(storageKey: string) { +function getOption(storageKey: string, defaultValue: ApiDisplayOptions): ApiDisplayOptions { if (neverHydrated) { - return DEFAULT_LAYOUT; + return defaultValue; } try { const savedOption = localStorage.getItem(storageKey); @@ -60,34 +29,32 @@ function getOption(storageKey: string) { if (savedOption !== null && options.includes(savedOption as ApiDisplayOptions)) { return savedOption as ApiDisplayOptions; } - - const randomOption = getRandomOption(); - - return randomOption; } catch (error) { - return DEFAULT_LAYOUT; + return defaultValue; } + return defaultValue; } export function useApiPageOption( storageKey: string, + defaultValue: ApiDisplayOptions, ): [ApiDisplayOptions, (newOption: ApiDisplayOptions) => void] { - const [option, setOption] = React.useState(getOption(storageKey)); + const [option, setOption] = React.useState(getOption(storageKey, defaultValue)); useEnhancedEffect(() => { neverHydrated = false; - const newOption = getOption(storageKey); + const newOption = getOption(storageKey, defaultValue); setOption(newOption); - }, [storageKey]); + }, [storageKey, defaultValue]); React.useEffect(() => { - if (option !== DEFAULT_LAYOUT) { + if (option !== defaultValue) { const id = document.location.hash.slice(1); const element = document.getElementById(id); element?.scrollIntoView(); } return undefined; - }, [option]); + }, [option, defaultValue]); const updateOption = React.useCallback( (newOption: ApiDisplayOptions) => { @@ -104,20 +71,6 @@ export function useApiPageOption( return [option, updateOption]; } -export function getApiPageLayout() { - const rep: { [key: string]: string } = {}; - - Object.values(API_LAYOUT_STORAGE_KEYS).forEach((localStorageKey) => { - try { - const savedOption = localStorage.getItem(localStorageKey); - rep[localStorageKey] = savedOption || 'none'; - } catch { - rep[localStorageKey] = 'none'; - } - }); - return rep; -} - // Fix Toggle buton highlight (taken from https://github.com/mui/material-ui/issues/18091) type TooltipToggleButtonProps = ToggleButtonProps & { /** @@ -141,10 +94,14 @@ const TooltipToggleButton = React.forwardRef void; + /** + * The type of section. This value is used to send correct event to Google Analytics. + */ + sectionType: 'classes' | 'props' | 'slots'; } export default function ToggleDisplayOption(props: ToggleDisplayOptionProps) { - const { displayOption, setDisplayOption } = props; + const { displayOption, setDisplayOption, sectionType } = props; const handleAlignment = ( event: React.MouseEvent, @@ -182,13 +139,34 @@ export default function ToggleDisplayOption(props: ToggleDisplayOptionProps) { }, }} > - + - + - + diff --git a/docs/src/modules/components/ComponentsApiContent.js b/docs/src/modules/components/ComponentsApiContent.js index d76390a243fd48..a49959898ec535 100644 --- a/docs/src/modules/components/ComponentsApiContent.js +++ b/docs/src/modules/components/ComponentsApiContent.js @@ -10,6 +10,7 @@ import MarkdownElement from 'docs/src/modules/components/MarkdownElement'; import PropertiesSection from 'docs/src/modules/components/ApiPage/sections/PropertiesSection'; import ClassesSection from 'docs/src/modules/components/ApiPage/sections/ClassesSection'; import SlotsSection from 'docs/src/modules/components/ApiPage/sections/SlotsSection'; +import { DEFAULT_API_LAYOUT_STORAGE_KEYS } from 'docs/src/modules/components/ApiPage/sections/ToggleDisplayOption'; function getTranslatedHeader(t, header, text) { const translations = { @@ -49,7 +50,12 @@ Heading.propTypes = { }; export default function ComponentsApiContent(props) { - const { descriptions, pageContents } = props; + const { + descriptions, + pageContents, + defaultLayout = 'table', + layoutStorageKey = DEFAULT_API_LAYOUT_STORAGE_KEYS, + } = props; const t = useTranslate(); const userLanguage = useUserLanguage(); const router = useRouter(); @@ -150,6 +156,8 @@ export default function ComponentsApiContent(props) { spreadHint={spreadHint} level="h3" titleHash={`${componentNameKebabCase}-props`} + defaultLayout={defaultLayout} + layoutStorageKey={layoutStorageKey.props} />
{cssComponent && ( @@ -216,6 +224,8 @@ export default function ComponentsApiContent(props) { slotGuideLink && t('api-docs.slotDescription').replace(/{{slotGuideLink}}/, slotGuideLink) } + defaultLayout={defaultLayout} + layoutStorageKey={layoutStorageKey.slots} /> @@ -237,7 +249,13 @@ export default function ComponentsApiContent(props) { } ComponentsApiContent.propTypes = { + defaultLayout: PropTypes.oneOf(['collapsed', 'expanded', 'table']), descriptions: PropTypes.object.isRequired, + layoutStorageKey: PropTypes.shape({ + classes: PropTypes.string, + props: PropTypes.string, + slots: PropTypes.string, + }), pageContents: PropTypes.object.isRequired, }; diff --git a/docs/src/modules/components/GoogleAnalytics.js b/docs/src/modules/components/GoogleAnalytics.js index 0931581890bab3..fb4c511f87c2d6 100644 --- a/docs/src/modules/components/GoogleAnalytics.js +++ b/docs/src/modules/components/GoogleAnalytics.js @@ -6,7 +6,6 @@ import { useNoSsrCodeVariant } from 'docs/src/modules/utils/codeVariant'; import { useNoSsrCodeStyling } from 'docs/src/modules/utils/codeStylingSolution'; import { useUserLanguage } from 'docs/src/modules/utils/i18n'; import { pathnameToLanguage } from 'docs/src/modules/utils/helpers'; -import { getApiPageLayout } from 'docs/src/modules/components/ApiPage/sections/ToggleDisplayOption'; // So we can write code like: // @@ -139,12 +138,6 @@ function GoogleAnalytics() { }); }, [codeStylingVariant]); - React.useEffect(() => { - window.gtag('set', 'user_properties', { - ...getApiPageLayout(), - }); - }, []); - return null; } diff --git a/docs/src/modules/components/HooksApiContent.js b/docs/src/modules/components/HooksApiContent.js index 105364787953ee..5ae5ebee298608 100644 --- a/docs/src/modules/components/HooksApiContent.js +++ b/docs/src/modules/components/HooksApiContent.js @@ -7,6 +7,7 @@ import { useTranslate, useUserLanguage } from 'docs/src/modules/utils/i18n'; import PropertiesSection from 'docs/src/modules/components/ApiPage/sections/PropertiesSection'; import HighlightedCode from 'docs/src/modules/components/HighlightedCode'; import MarkdownElement from 'docs/src/modules/components/MarkdownElement'; +import { DEFAULT_API_LAYOUT_STORAGE_KEYS } from 'docs/src/modules/components/ApiPage/sections/ToggleDisplayOption'; function getTranslatedHeader(t, header, text) { const translations = { @@ -43,7 +44,12 @@ Heading.propTypes = { }; export default function HooksApiContent(props) { - const { descriptions, pagesContents } = props; + const { + descriptions, + pagesContents, + defaultLayout = 'table', + layoutStorageKey = DEFAULT_API_LAYOUT_STORAGE_KEYS, + } = props; const userLanguage = useUserLanguage(); const t = useTranslate(); @@ -76,6 +82,8 @@ export default function HooksApiContent(props) { level="h3" title="api-docs.parameters" titleHash={`${hookNameKebabCase}-parameters`} + defaultLayout={defaultLayout} + layoutStorageKey={layoutStorageKey} /> ) : ( {t('api-docs.hooksNoParameters')} @@ -89,6 +97,8 @@ export default function HooksApiContent(props) { level="h3" title="api-docs.returnValue" titleHash={`${hookNameKebabCase}-return-value`} + defaultLayout={defaultLayout} + layoutStorageKey={layoutStorageKey} />
@@ -103,7 +113,9 @@ export default function HooksApiContent(props) { } HooksApiContent.propTypes = { + defaultLayout: PropTypes.oneOf(['collapsed', 'expanded', 'table']), descriptions: PropTypes.object.isRequired, + layoutStorageKey: PropTypes.string, pagesContents: PropTypes.object.isRequired, };