-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UI: Add links to documentation and videos in UI (#25565)
- Add API function to get the versioned docs URL - Add versioned link to docs into the sidebar menu - Fix missing icons in sidebar menu - Attach Storybook renderer to docs links - Add education links to empty state of interaction tests panel - Match icons across empty states
- Loading branch information
Showing
14 changed files
with
431 additions
and
34 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import { Link } from '@storybook/components'; | ||
import { DocumentIcon, VideoIcon } from '@storybook/icons'; | ||
import { Consumer, useStorybookApi } from '@storybook/manager-api'; | ||
import { styled } from '@storybook/theming'; | ||
|
||
import { DOCUMENTATION_LINK, TUTORIAL_VIDEO_LINK } from '../constants'; | ||
|
||
const Wrapper = styled.div(({ theme }) => ({ | ||
height: '100%', | ||
display: 'flex', | ||
padding: 0, | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
flexDirection: 'column', | ||
gap: 15, | ||
background: theme.background.content, | ||
})); | ||
|
||
const Content = styled.div({ | ||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: 4, | ||
maxWidth: 415, | ||
}); | ||
|
||
const Title = styled.div(({ theme }) => ({ | ||
fontWeight: theme.typography.weight.bold, | ||
fontSize: theme.typography.size.s2 - 1, | ||
textAlign: 'center', | ||
color: theme.textColor, | ||
})); | ||
|
||
const Description = styled.div(({ theme }) => ({ | ||
fontWeight: theme.typography.weight.regular, | ||
fontSize: theme.typography.size.s2 - 1, | ||
textAlign: 'center', | ||
color: theme.textMutedColor, | ||
})); | ||
|
||
const Links = styled.div(({ theme }) => ({ | ||
display: 'flex', | ||
fontSize: theme.typography.size.s2 - 1, | ||
gap: 25, | ||
})); | ||
|
||
const Divider = styled.div(({ theme }) => ({ | ||
width: 1, | ||
height: 16, | ||
backgroundColor: theme.appBorderColor, | ||
})); | ||
|
||
export const Empty = () => { | ||
const [isLoading, setIsLoading] = useState(true); | ||
const api = useStorybookApi(); | ||
const docsUrl = api.getDocsUrl({ | ||
subpath: DOCUMENTATION_LINK, | ||
versioned: true, | ||
renderer: true, | ||
}); | ||
|
||
// We are adding a small delay to avoid flickering when the story is loading. | ||
// It takes a bit of time for the controls to appear, so we don't want | ||
// to show the empty state for a split second. | ||
useEffect(() => { | ||
const load = setTimeout(() => { | ||
setIsLoading(false); | ||
}, 100); | ||
|
||
return () => clearTimeout(load); | ||
}, []); | ||
|
||
if (isLoading) return null; | ||
|
||
return ( | ||
<Wrapper> | ||
<Content> | ||
<Title>Interaction testing</Title> | ||
<Description> | ||
Interaction tests allow you to verify the functional aspects of UIs. Write a play function | ||
for your story and you'll see it run here. | ||
</Description> | ||
</Content> | ||
<Links> | ||
<Link href={TUTORIAL_VIDEO_LINK} target="_blank" withArrow> | ||
<VideoIcon /> Watch 8m video | ||
</Link> | ||
<Divider /> | ||
<Consumer> | ||
{({ state }) => ( | ||
<Link href={docsUrl} target="_blank" withArrow> | ||
<DocumentIcon /> Read docs | ||
</Link> | ||
)} | ||
</Consumer> | ||
</Links> | ||
</Wrapper> | ||
); | ||
}; |
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
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 |
---|---|---|
@@ -1,2 +1,5 @@ | ||
export const ADDON_ID = 'storybook/interactions'; | ||
export const PANEL_ID = `${ADDON_ID}/panel`; | ||
|
||
export const TUTORIAL_VIDEO_LINK = 'https://youtu.be/Waht9qq7AoA'; | ||
export const DOCUMENTATION_LINK = 'writing-tests/interaction-testing'; |
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
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,48 @@ | ||
import path from 'node:path'; | ||
import { describe, it, expect } from 'vitest'; | ||
|
||
import { | ||
pluckNameFromConfigProperty, | ||
pluckStorybookPackageFromPath, | ||
pluckThirdPartyPackageFromPath, | ||
} from './framework'; | ||
|
||
describe('UTILITIES: Framework information', () => { | ||
describe('UTILITY: pluckNameFromConfigProperty', () => { | ||
it('should return undefined if the property is undefined', () => { | ||
expect(pluckNameFromConfigProperty(undefined)).toBe(undefined); | ||
}); | ||
|
||
it('should return the name if the property is a string', () => { | ||
expect(pluckNameFromConfigProperty('foo')).toBe('foo'); | ||
}); | ||
|
||
it('should return the name if the property is an object', () => { | ||
expect(pluckNameFromConfigProperty({ name: 'foo' })).toBe('foo'); | ||
}); | ||
}); | ||
|
||
describe('UTILITY: pluckStorybookPackageFromPath', () => { | ||
it('should return the package name if the path is a storybook package', () => { | ||
const packagePath = path.join(process.cwd(), 'node_modules', '@storybook', 'foo'); | ||
expect(pluckStorybookPackageFromPath(packagePath)).toBe('@storybook/foo'); | ||
}); | ||
|
||
it('should return undefined if the path is not a storybook package', () => { | ||
const packagePath = path.join(process.cwd(), 'foo'); | ||
expect(pluckStorybookPackageFromPath(packagePath)).toBe(undefined); | ||
}); | ||
}); | ||
|
||
describe('UTILITY: pluckThirdPartyPackageFromPath', () => { | ||
it('should return the package name if the path is a third party package', () => { | ||
const packagePath = path.join(process.cwd(), 'node_modules', 'bar'); | ||
expect(pluckThirdPartyPackageFromPath(packagePath)).toBe('bar'); | ||
}); | ||
|
||
it('should return the given path if the path is not a third party package', () => { | ||
const packagePath = path.join(process.cwd(), 'foo', 'bar', 'baz'); | ||
expect(pluckThirdPartyPackageFromPath(packagePath)).toBe(packagePath); | ||
}); | ||
}); | ||
}); |
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,51 @@ | ||
import path from 'path'; | ||
import type { Options } from '@storybook/types'; | ||
|
||
interface PropertyObject { | ||
name: string; | ||
options?: Record<string, any>; | ||
} | ||
|
||
type Property = string | PropertyObject | undefined; | ||
|
||
export const pluckNameFromConfigProperty = (property: Property) => { | ||
if (!property) { | ||
return undefined; | ||
} | ||
|
||
return typeof property === 'string' ? property : property.name; | ||
}; | ||
|
||
// For replacing Windows backslashes with forward slashes | ||
const normalizePath = (packagePath: string) => packagePath.replaceAll(path.sep, '/'); | ||
|
||
export const pluckStorybookPackageFromPath = (packagePath: string) => | ||
normalizePath(packagePath).match(/(@storybook\/.*)$/)?.[1]; | ||
|
||
export const pluckThirdPartyPackageFromPath = (packagePath: string) => | ||
normalizePath(packagePath).split('node_modules/')[1] ?? packagePath; | ||
|
||
export const buildFrameworkGlobalsFromOptions = async (options: Options) => { | ||
const globals: Record<string, any> = {}; | ||
|
||
const { renderer, builder } = await options.presets.apply('core'); | ||
|
||
const rendererName = pluckNameFromConfigProperty(renderer); | ||
if (rendererName) { | ||
globals.STORYBOOK_RENDERER = | ||
pluckStorybookPackageFromPath(rendererName) ?? pluckThirdPartyPackageFromPath(rendererName); | ||
} | ||
|
||
const builderName = pluckNameFromConfigProperty(builder); | ||
if (builderName) { | ||
globals.STORYBOOK_BUILDER = | ||
pluckStorybookPackageFromPath(builderName) ?? pluckThirdPartyPackageFromPath(builderName); | ||
} | ||
|
||
const framework = pluckNameFromConfigProperty(await options.presets.apply('framework')); | ||
if (framework) { | ||
globals.STORYBOOK_FRAMEWORK = framework; | ||
} | ||
|
||
return globals; | ||
}; |
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
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
Oops, something went wrong.