diff --git a/.storybook/docs/blocks/ImportInfo.js b/.storybook/docs/blocks/ImportInfo.js index c58cec75cdd6ff25dc7756efcd79f81b0ef23040..b099480c01a4bba10c561a0db31e81d522e0e611 100644 --- a/.storybook/docs/blocks/ImportInfo.js +++ b/.storybook/docs/blocks/ImportInfo.js @@ -2,7 +2,7 @@ import React, { useContext } from 'react'; import { DocsContext } from '@storybook/addon-docs'; import { SyntaxHighlighter } from '@storybook/components'; -export const ImportInfo = ({ children }) => { +export const ImportInfo = () => { const context = useContext(DocsContext); const componentName = context.parameters?.component?.name; if (!componentName) { diff --git a/.storybook/docs/blocks/LinkToSource.js b/.storybook/docs/blocks/LinkToSource.js new file mode 100644 index 0000000000000000000000000000000000000000..3ba4cafdc1dfed058f14ffc29dea97720d8b98f0 --- /dev/null +++ b/.storybook/docs/blocks/LinkToSource.js @@ -0,0 +1,57 @@ +import React, { useContext } from 'react'; +import { DocsContext } from '@storybook/addon-docs/blocks'; +import { Button } from '@storybook/components'; +import * as modules from '../../../index'; + +const BASE_URL = 'https://gitlab.com/gitlab-org/gitlab-ui/-/tree/main'; + +/** + * Returns a source path given the current story path, if it exists. This works + * by assuming the story's filename is of the form `.../{basename}.stories.js`, + * and returns the source path of `.../{basename}.vue` or `{basename}.js`, + * whichever exists. + * + * For example: + * + * sourcePathFromStoryPath('./src/components/base/alert/alert.stories.js') + * // => './src/components/base/alert/alert.vue' + * + * @param {string} Story file path, relative to the root directory of the repo. + * @returns {string} Source file path for component or directive, relative to + * the root of the repo. + */ +const sourcePathFromStoryPath = (storyPath) => { + const sourcePaths = require + .context( + '../../../src', + true, + /^(?!.*(?:(spec|documentation|stories).js$|examples|utils)).*\.(vue|js)$/ + ) + .keys() + .map((path) => path.replace(/^\.\//, './src/')); + + const sourcePathRegex = new RegExp(`${storyPath.replace(/\.stories\.js$/, '')}\.(vue|js)$`); + + return sourcePaths.find((path, i, arr) => sourcePathRegex.test(path)); +}; + +export const LinkToSource = (props) => { + const context = useContext(DocsContext); + // There is a context.parameters.component.__file property that points to the + // source file if it's a component, but it does not exist for directives and isn't available + // in the production bundle. + const storyFileName = context?.parameters?.fileName; + if (!storyFileName) { + return null; + } + const sourcePath = sourcePathFromStoryPath(storyFileName); + if (!sourcePath) { + return null; + } + const href = `${BASE_URL}/${sourcePath.replace(/^\.\//, '')}`; + return ( + + ); +}; diff --git a/.storybook/docs/blocks/index.js b/.storybook/docs/blocks/index.js new file mode 100644 index 0000000000000000000000000000000000000000..9a38373e8e1f5b6f0490e9c76d361356c7cbbead --- /dev/null +++ b/.storybook/docs/blocks/index.js @@ -0,0 +1,3 @@ +export { ImportInfo } from './ImportInfo'; +export { BootstrapComponent } from './BootstrapComponent'; +export { LinkToSource } from './LinkToSource'; diff --git a/.storybook/docs/page.js b/.storybook/docs/page.js index b36301134a0584daf689fc45c30f9cd861909e4e..515abd9600b8737bc3c2208ae4dea6b7d6cde465 100644 --- a/.storybook/docs/page.js +++ b/.storybook/docs/page.js @@ -8,12 +8,14 @@ import { Stories, PRIMARY_STORY, } from '@storybook/addon-docs'; -import { ImportInfo } from './blocks/ImportInfo'; -import { BootstrapComponent } from './blocks/BootstrapComponent'; +import { ImportInfo, BootstrapComponent, LinkToSource } from './blocks'; export const page = () => ( <> - + <div className="gl-display-flex gl-align-items-center"> + <Title /> + <LinkToSource className="gl-ml-auto!" /> + </div> <Subtitle /> <ImportInfo /> <Description />