-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement docsite for contrib packages (#244)
- Loading branch information
1 parent
184e510
commit 5f2bc3a
Showing
84 changed files
with
1,454 additions
and
566 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
.storybook/copied-from-fluentui-core/components/FluentDocsContainer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import * as React from 'react'; | ||
import { DocsContainer, type DocsContextProps } from '@storybook/addon-docs'; | ||
import { webLightTheme, FluentProvider } from '@fluentui/react-components'; | ||
|
||
interface FluentDocsContainerProps { | ||
children: React.ReactNode; | ||
context: DocsContextProps; | ||
} | ||
|
||
/** | ||
* A container that wraps storybook's native docs container to add extra components to the docs experience | ||
*/ | ||
export const FluentDocsContainer = ({ | ||
children, | ||
context, | ||
}: FluentDocsContainerProps) => { | ||
return ( | ||
<FluentProvider | ||
className="sb-unstyled" | ||
style={{ backgroundColor: 'transparent' }} | ||
theme={webLightTheme} | ||
> | ||
<DocsContainer context={context}>{children}</DocsContainer> | ||
</FluentProvider> | ||
); | ||
}; |
159 changes: 159 additions & 0 deletions
159
.storybook/copied-from-fluentui-core/components/FluentDocsPage.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import * as React from 'react'; | ||
import { | ||
DocsContext, | ||
ArgsTable, | ||
Description, | ||
Stories, | ||
type DocsContextProps, | ||
} from '@storybook/addon-docs'; | ||
import type { SBEnumType, PreparedStory, Renderer } from '@storybook/types'; | ||
import { makeStyles, shorthands, tokens } from '@fluentui/react-components'; | ||
import { InfoFilled } from '@fluentui/react-icons'; | ||
import { Toc } from './Toc'; | ||
|
||
type PrimaryStory = PreparedStory<Renderer>; | ||
|
||
const useStyles = makeStyles({ | ||
divider: { | ||
height: '1px', | ||
backgroundColor: '#e1dfdd', | ||
...shorthands.border('0px', 'none'), | ||
...shorthands.margin('48px', '0px'), | ||
}, | ||
wrapper: { | ||
display: 'flex', | ||
...shorthands.gap('16px'), | ||
}, | ||
toc: { | ||
flexBasis: '200px', | ||
flexShrink: 0, | ||
[`@media screen and (max-width: 1000px)`]: { | ||
display: 'none', | ||
}, | ||
}, | ||
container: { | ||
// without a width, this div grows wider than its parent | ||
width: '200px', | ||
flexGrow: 1, | ||
}, | ||
globalTogglesContainer: { | ||
columnGap: tokens.spacingHorizontalXXXL, | ||
display: 'flex', | ||
}, | ||
description: { | ||
display: 'grid', | ||
gridTemplateColumns: '1fr min-content', | ||
}, | ||
nativeProps: { | ||
display: 'flex', | ||
gap: tokens.spacingHorizontalM, | ||
|
||
border: `1px solid ${tokens.colorNeutralStroke1}`, | ||
borderRadius: tokens.borderRadiusMedium, | ||
padding: tokens.spacingHorizontalM, | ||
margin: `0 ${tokens.spacingHorizontalM}`, | ||
}, | ||
nativePropsIcon: { | ||
alignSelf: 'center', | ||
color: tokens.colorBrandForeground1, | ||
fontSize: '24px', | ||
}, | ||
nativePropsMessage: { | ||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: tokens.spacingVerticalXS, | ||
}, | ||
}); | ||
|
||
const getNativeElementsList = (elements: SBEnumType['value']): JSX.Element => { | ||
const elementsArr = elements.map((el, idx) => [ | ||
<code key={idx}>{`<${el}>`}</code>, | ||
idx !== elements.length - 1 ? ', ' : ' ', | ||
]); | ||
|
||
return ( | ||
<> | ||
{elementsArr} | ||
{elementsArr.length > 1 ? 'elements' : 'element'} | ||
</> | ||
); | ||
}; | ||
|
||
const RenderArgsTable = ({ | ||
hideArgsTable, | ||
primaryStory, | ||
}: { | ||
primaryStory: PrimaryStory; | ||
hideArgsTable: boolean; | ||
}) => { | ||
const styles = useStyles(); | ||
return hideArgsTable ? null : ( | ||
<> | ||
<ArgsTable of={primaryStory.component} /> | ||
{primaryStory.argTypes.as && | ||
primaryStory.argTypes.as?.type?.name === 'enum' && ( | ||
<div className={styles.nativeProps}> | ||
<InfoFilled className={styles.nativePropsIcon} /> | ||
<div className={styles.nativePropsMessage}> | ||
<b> | ||
Native props are supported <span role="presentation">🙌</span> | ||
</b> | ||
<span> | ||
All HTML attributes native to the{' '} | ||
{getNativeElementsList(primaryStory.argTypes.as.type.value)}, | ||
including all <code>aria-*</code> and <code>data-*</code>{' '} | ||
attributes, can be applied as native props on this component. | ||
</span> | ||
</div> | ||
</div> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export const FluentDocsPage = () => { | ||
const context = React.useContext(DocsContext); | ||
const stories = context.componentStories(); | ||
const primaryStory = stories[0]; | ||
const primaryStoryContext = context.getStoryContext(primaryStory); | ||
|
||
assertStoryMetaValues(primaryStory); | ||
|
||
const hideArgsTable = Boolean( | ||
primaryStoryContext.parameters?.docs?.hideArgsTable | ||
); | ||
|
||
const styles = useStyles(); | ||
|
||
return ( | ||
<div className="sb-unstyled"> | ||
<div className={styles.wrapper}> | ||
<div className={styles.container}> | ||
<Description /> | ||
<RenderArgsTable | ||
primaryStory={primaryStory} | ||
hideArgsTable={hideArgsTable} | ||
/> | ||
<Stories title="Examples" /> | ||
</div> | ||
<div className={styles.toc}> | ||
<Toc stories={stories} /> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
function assertStoryMetaValues( | ||
story: ReturnType<DocsContextProps['componentStories']>[number] | ||
) { | ||
if (story.component === null) { | ||
throw new Error( | ||
[ | ||
'🚨 Invalid Story Meta declaration:', | ||
`- primaryStory.component of componentId:${story.componentId} is "null"`, | ||
'- to resolve this error, please update "component" property value in your story definition to reference a React Component or remove it if it is not needed.', | ||
].join('\n') | ||
); | ||
} | ||
} |
142 changes: 142 additions & 0 deletions
142
.storybook/copied-from-fluentui-core/components/Toc.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import * as React from 'react'; | ||
import { addons } from '@storybook/preview-api'; | ||
import { NAVIGATE_URL } from '@storybook/core-events'; | ||
import { makeStyles, shorthands } from '@fluentui/react-components'; | ||
|
||
const useTocStyles = makeStyles({ | ||
root: { | ||
top: '64px', | ||
position: 'sticky', | ||
marginLeft: '40px', | ||
}, | ||
heading: { | ||
fontSize: '11px', | ||
fontWeight: 'bold', | ||
textTransform: 'uppercase', | ||
marginBottom: '20px', | ||
}, | ||
ol: { | ||
position: 'relative', | ||
listStyleType: 'none', | ||
marginLeft: 0, | ||
marginTop: 0, | ||
paddingInlineStart: '20px', | ||
'& li': { | ||
marginBottom: '15px', | ||
lineHeight: '16px', | ||
}, | ||
'& a': { | ||
textDecorationLine: 'none', | ||
color: '#201F1E', | ||
fontSize: '14px', | ||
':hover': { | ||
color: '#201F1E', | ||
}, | ||
}, | ||
'&:before': { | ||
content: '""', | ||
position: 'absolute', | ||
left: 0, | ||
height: '100%', | ||
width: '3px', | ||
backgroundColor: '#EDEBE9', | ||
...shorthands.borderRadius('4px'), | ||
}, | ||
}, | ||
selected: { | ||
position: 'relative', | ||
'&:after': { | ||
content: '""', | ||
position: 'absolute', | ||
left: '-20px', | ||
top: 0, | ||
bottom: 0, | ||
width: '3px', | ||
backgroundColor: '#436DCD', | ||
...shorthands.borderRadius('4px'), | ||
}, | ||
}, | ||
}); | ||
|
||
type TocItem = { name: string; id: string; selected?: boolean }; | ||
|
||
// // Alternative approach to navigate - rerenders the iframe | ||
// // Usage: selectStory({ story: s.name, kind: s.kind }); | ||
// const selectStory = (story: { kind: string; story: string }) => { | ||
// console.log('Select Story', story); | ||
// addons.getChannel().emit(SELECT_STORY, story); | ||
// }; | ||
|
||
const navigate = (url: string) => { | ||
addons.getChannel().emit(NAVIGATE_URL, url); | ||
}; | ||
|
||
export const nameToHash = (id: string): string => | ||
id.toLowerCase().replace(/[^a-z0-9]/gi, '-'); | ||
|
||
export const Toc = ({ stories }: { stories: TocItem[] }) => { | ||
const [selected, setSelected] = React.useState(''); | ||
const isNavigating = React.useRef<boolean>(false); | ||
|
||
React.useEffect(() => { | ||
// eslint-disable-next-line no-restricted-globals | ||
const observer = new IntersectionObserver( | ||
(entries: IntersectionObserverEntry[]) => { | ||
if (isNavigating.current) { | ||
isNavigating.current = false; | ||
return; | ||
} | ||
for (const entry of entries) { | ||
const { intersectionRatio, target } = entry; | ||
if (intersectionRatio > 0.5) { | ||
setSelected(target.id); | ||
return; | ||
} | ||
} | ||
}, | ||
{ | ||
threshold: [0.5], | ||
} | ||
); | ||
|
||
stories.forEach((link) => { | ||
// eslint-disable-next-line no-restricted-globals | ||
const element = document.getElementById(nameToHash(link.name)); | ||
if (element) { | ||
observer.observe(element); | ||
} | ||
}); | ||
|
||
return () => observer.disconnect(); | ||
}, [stories]); | ||
|
||
const tocItems = stories.map((item) => { | ||
return { ...item, selected: nameToHash(item.name) === selected }; | ||
}); | ||
const tocClasses = useTocStyles(); | ||
return ( | ||
<nav className={tocClasses.root}> | ||
<h3 className={tocClasses.heading}>On this page</h3> | ||
<ol className={tocClasses.ol}> | ||
{tocItems.map((s) => { | ||
const name = nameToHash(s.name); | ||
return ( | ||
<li className={s.selected ? tocClasses.selected : ''} key={s.id}> | ||
<a | ||
href={`#${name}`} | ||
target="_self" | ||
onClick={(e) => { | ||
isNavigating.current = true; | ||
navigate(`#${name}`); | ||
setSelected(name); | ||
}} | ||
> | ||
{s.name} | ||
</a> | ||
</li> | ||
); | ||
})} | ||
</ol> | ||
</nav> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* remove the docs wrapper bg to let page bg show through */ | ||
/* remove unnecessary padding now that theme switcher is not taking up space */ | ||
#storybook-docs .sbdocs-wrapper { | ||
background: transparent !important; | ||
padding: 0 48px; | ||
} | ||
|
||
/* sb-show-main is missing during page transitions causing a page shift */ | ||
/* todo: cleanup once we no longer inherit docs-root */ | ||
.sb-show-main.sb-main-fullscreen, | ||
.sb-main-fullscreen { | ||
margin: 0; | ||
padding: 0; | ||
display: block; | ||
} | ||
|
||
/* remove loading overlay */ | ||
.sb-preparing-story, | ||
.sb-preparing-docs, | ||
.sb-nopreview, | ||
.sb-errordisplay { | ||
display: none !important; | ||
} |
Oops, something went wrong.