diff --git a/.prettierignore b/.prettierignore index fe38de2e01a..2d761e81124 100644 --- a/.prettierignore +++ b/.prettierignore @@ -27,5 +27,4 @@ GH2SG.bookmarklet.js docker-images/grafana/config/provisioning/dashboards/sourcegraph/ storybook-static/ browser/code-intel-extensions/ -!/.storybook/** monitoring/monitoring/README.md diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index 05042ea825d..00000000000 --- a/.storybook/main.js +++ /dev/null @@ -1,147 +0,0 @@ -const path = require('path') -const { remove } = require('lodash') -const { DefinePlugin, ProgressPlugin } = require('webpack') -const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin') -const TerserPlugin = require('terser-webpack-plugin') - -const monacoEditorPaths = [path.resolve(__dirname, '..', 'node_modules', 'monaco-editor')] - -const shouldMinify = !!process.env.MINIFY - -const config = { - stories: ['../client/**/*.story.tsx'], - addons: [ - '@storybook/addon-knobs', - '@storybook/addon-actions', - 'storybook-addon-designs', - 'storybook-dark-mode', - '@storybook/addon-a11y', - '@storybook/addon-toolbars', - './redesign-toggle-toolbar/preset.js', - ], - - /** - * @param config {import('webpack').Configuration} - * @returns {import('webpack').Configuration} - */ - webpackFinal: config => { - // Include sourcemaps - config.mode = shouldMinify ? 'production' : 'development' - config.devtool = shouldMinify ? 'source-map' : 'cheap-module-eval-source-map' - const definePlugin = config.plugins.find(plugin => plugin instanceof DefinePlugin) - // @ts-ignore - definePlugin.definitions.NODE_ENV = JSON.stringify(config.mode) - // @ts-ignore - definePlugin.definitions['process.env'].NODE_ENV = JSON.stringify(config.mode) - - if (shouldMinify) { - config.optimization = { - minimize: true, - minimizer: [ - new TerserPlugin({ - sourceMap: true, - terserOptions: { - compress: { - // // Don't inline functions, which causes name collisions with uglify-es: - // https://github.com/mishoo/UglifyJS2/issues/2842 - inline: 1, - }, - }, - }), - ], - namedModules: false, - } - } - - // We don't use Storybook's default Babel config for our repo, it doesn't include everything we need. - config.module.rules.splice(0, 1) - - if (process.env.CI) { - remove(config.plugins, plugin => plugin instanceof ProgressPlugin) - } - - config.module.rules.push({ - test: /\.tsx?$/, - loader: require.resolve('babel-loader'), - options: { - configFile: path.resolve(__dirname, '..', 'babel.config.js'), - }, - }) - - config.plugins.push( - new MonacoWebpackPlugin({ - languages: ['json'], - features: [ - 'bracketMatching', - 'clipboard', - 'coreCommands', - 'cursorUndo', - 'find', - 'format', - 'hover', - 'inPlaceReplace', - 'iPadShowKeyboard', - 'links', - 'suggest', - ], - }) - ) - - const storybookDirectory = path.resolve(__dirname, '../node_modules/@storybook') - - // Put our style rules at the beginning so they're processed by the time it - // gets to storybook's style rules. - config.module.rules.unshift({ - test: /\.(sass|scss)$/, - use: [ - 'to-string-loader', - 'css-loader', - { - loader: 'postcss-loader', - }, - { - loader: 'sass-loader', - options: { - sassOptions: { - includePaths: [path.resolve(__dirname, '..', 'node_modules')], - }, - }, - }, - ], - // Make sure Storybook styles get handled by the Storybook config - exclude: storybookDirectory, - }) - - // Make sure Storybook style loaders are only evaluated for Storybook styles. - config.module.rules.find(rule => rule.test?.toString() === /\.css$/.toString()).include = storybookDirectory - - config.module.rules.unshift({ - // CSS rule for external plain CSS (skip SASS and PostCSS for build perf) - test: /\.css$/, - // Make sure Storybook styles get handled by the Storybook config - exclude: [storybookDirectory, ...monacoEditorPaths], - use: ['to-string-loader', 'css-loader'], - }) - - config.module.rules.unshift({ - // CSS rule for monaco-editor, it expects styles to be loaded with `style-loader`. - test: /\.css$/, - include: monacoEditorPaths, - // Make sure Storybook styles get handled by the Storybook config - exclude: [storybookDirectory], - use: ['style-loader', 'css-loader'], - }) - config.module.rules.unshift({ - test: /\.ya?ml$/, - use: ['raw-loader'], - }) - - Object.assign(config.entry, { - 'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js', - 'json.worker': 'monaco-editor/esm/vs/language/json/json.worker', - }) - - return config - }, -} -module.exports = config diff --git a/.storybook/preview.js b/.storybook/preview.js deleted file mode 100644 index e4528023c19..00000000000 --- a/.storybook/preview.js +++ /dev/null @@ -1,50 +0,0 @@ -import 'focus-visible' - -import { configureActions } from '@storybook/addon-actions' -import { withConsole } from '@storybook/addon-console' -import { setLinkComponent, AnchorLink } from '../client/shared/src/components/Link' -import { withDesign } from 'storybook-addon-designs' -import isChromatic from 'chromatic/isChromatic' -import * as themes from './themes' - -export const decorators = [withDesign, (storyFn, context) => withConsole()(storyFn)(context)] - -export const parameters = { - darkMode: { - stylePreview: true, - darkClass: 'theme-dark', - lightClass: 'theme-light', - light: themes.light, - dark: themes.dark, - }, -} - -configureActions({ depth: 100, limit: 20 }) - -setLinkComponent(AnchorLink) - -// Default to light theme for Chromatic and "Open canvas in new tab" button. -// addon-dark-mode will override this if it's running. -if (!document.body.classList.contains('theme-dark')) { - document.body.classList.add('theme-light') -} - -if (isChromatic()) { - const style = document.createElement('style') - style.innerHTML = ` - .monaco-editor .cursor { - visibility: hidden !important; - } - ` - document.head.append(style) -} - -// @ts-ignore -window.MonacoEnvironment = { - getWorkerUrl(_, label) { - if (label === 'json') { - return '/json.worker.bundle.js' - } - return '/editor.worker.bundle.js' - }, -} diff --git a/.storybook/redesign-toggle-toolbar/preset.js b/.storybook/redesign-toggle-toolbar/preset.js deleted file mode 100644 index 228842d65fa..00000000000 --- a/.storybook/redesign-toggle-toolbar/preset.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - managerEntries: (entry = []) => { - return [...entry, require.resolve('./register')] - }, -} diff --git a/.storybook/redesign-toggle-toolbar/register.js b/.storybook/redesign-toggle-toolbar/register.js deleted file mode 100644 index 73100ba4487..00000000000 --- a/.storybook/redesign-toggle-toolbar/register.js +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react' -import addons, { types } from '@storybook/addons' -import { Icons, IconButton } from '@storybook/components' -import { useRedesignToggle, REDESIGN_CLASS_NAME } from '../../client/shared/src/util/useRedesignToggle' - -const toggleRedesignClass = (element, isRedesignEnabled) => { - element.classList.toggle(REDESIGN_CLASS_NAME, !isRedesignEnabled) -} - -const updatePreview = isRedesignEnabled => { - const iframe = document.getElementById('storybook-preview-iframe') - - if (!iframe) { - return - } - - const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document - const body = iframeDocument?.body - - toggleRedesignClass(body, isRedesignEnabled) -} - -const updateManager = isRedesignEnabled => { - const manager = document.querySelector('body') - - if (!manager) { - return - } - - toggleRedesignClass(manager, isRedesignEnabled) -} - -const RedesignToggleStorybook = () => { - const { isRedesignEnabled, setIsRedesignEnabled } = useRedesignToggle() - - const handleRedesignToggle = () => { - setIsRedesignEnabled(!isRedesignEnabled) - updatePreview(isRedesignEnabled) - updateManager(isRedesignEnabled) - } - - return ( - - - - ) -} - -/** - * Custom toolbar which renders button to toggle redesign theme global CSS class. - */ -addons.register('sourcegraph/redesign-toggle-toolbar', () => { - addons.add('sourcegraph/redesign-toggle-toolbar', { - title: 'Redesign toggle toolbar', - type: types.TOOL, - match: ({ viewMode }) => viewMode === 'story' || viewMode === 'docs', - render: RedesignToggleStorybook, - }) -}) diff --git a/.storybook/themes.js b/.storybook/themes.js deleted file mode 100644 index 55666aeb9a3..00000000000 --- a/.storybook/themes.js +++ /dev/null @@ -1,40 +0,0 @@ -import { themes } from '@storybook/theming' -import openColor from 'open-color' -// @ts-ignore -import brandImage from '../ui/assets/img/wildcard-design-system.svg' - -// Themes use the colors from our webapp. - -/** @type {Partial} */ -const common = { - colorPrimary: openColor.blue[6], - colorSecondary: openColor.blue[6], - brandImage, - brandTitle: 'Sourcegraph Wildcard design system', - fontBase: - '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', - fontCode: 'sfmono-regular, consolas, menlo, dejavu sans mono, monospace', -} - -/** @type {import('@storybook/theming').ThemeVars} */ -export const dark = { - ...themes.dark, - ...common, - appBg: '#1c2736', - appContentBg: '#151c28', - appBorderColor: '#2b3750', - barBg: '#0e121b', - barTextColor: '#a2b0cd', - textColor: '#f2f4f8', - inputTextColor: '#ffffff', -} - -/** @type {import('@storybook/theming').ThemeVars} */ -export const light = { - ...themes.light, - ...common, - appBg: '#fbfdff', - textColor: '#2b3750', - barTextColor: '#566e9f', - inputTextColor: '#2b3750', -} diff --git a/babel.config.js b/babel.config.js index ec4fd579b71..e55a2f7d126 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,7 @@ // @ts-check const logger = require('gulplog') const semver = require('semver') +const path = require('path') /** @type {import('@babel/core').ConfigFunction} */ module.exports = api => { @@ -19,7 +20,7 @@ module.exports = api => { return { presets: [ // Can't put this in plugins because it needs to run as the last plugin. - ...(instrument ? [{ plugins: [['babel-plugin-istanbul', { exclude: ['node_modules/**'] }]] }] : []), + ...(instrument ? [{ plugins: [['babel-plugin-istanbul', { cwd: path.resolve(__dirname) }]] }] : []), [ '@babel/preset-env', { diff --git a/client/README.md b/client/README.md index 297e0cbcfd4..da611af749c 100644 --- a/client/README.md +++ b/client/README.md @@ -10,6 +10,7 @@ - **shared**: Contains common TypeScript/React/SCSS client code shared between the browser extension and the web app. Everything in this package is code-host agnostic. - **branded**: Contains React components and implements the visual design language we use across our web app and e.g. in the options menu of the browser extension. Over time, components from `shared` and `branded` packages should be moved into the `wildcard` package. - **wildcard**: Package that encapsulates storybook configuration and contains our Wildcard design system components. If we're using a component in two or more different areas (e.g. `web-app` and `browser-extension`) then it should live in the `wildcard` package. Otherwise the components should be better colocated with the code where they're actually used. +- **storybook**: Storybook configuration. ## Further migration plan diff --git a/client/storybook/.eslintignore b/client/storybook/.eslintignore new file mode 100644 index 00000000000..52f673c41d7 --- /dev/null +++ b/client/storybook/.eslintignore @@ -0,0 +1 @@ +out/ diff --git a/client/storybook/.eslintrc.js b/client/storybook/.eslintrc.js new file mode 100644 index 00000000000..3747f40c469 --- /dev/null +++ b/client/storybook/.eslintrc.js @@ -0,0 +1,13 @@ +// @ts-check + +const baseConfig = require('../../.eslintrc.js') + +module.exports = { + extends: '../../.eslintrc.js', + parserOptions: { + ...baseConfig.parserOptions, + project: [__dirname + '/tsconfig.json'], + }, + rules: {}, + overrides: baseConfig.overrides, +} diff --git a/client/storybook/README.md b/client/storybook/README.md new file mode 100644 index 00000000000..ab2258df5b8 --- /dev/null +++ b/client/storybook/README.md @@ -0,0 +1,12 @@ +# Storybook configuration + +## Usage + +Storybook configuration is setup as a `yarn workspace` symlink. + +Important commands are expose via root `package.json`: + +```sh +yarn storybook +yarn build-storybook +``` diff --git a/ui/assets/img/wildcard-design-system.svg b/client/storybook/assets/img/wildcard-design-system.svg similarity index 100% rename from ui/assets/img/wildcard-design-system.svg rename to client/storybook/assets/img/wildcard-design-system.svg diff --git a/client/storybook/babel.config.js b/client/storybook/babel.config.js new file mode 100644 index 00000000000..73a95d310cd --- /dev/null +++ b/client/storybook/babel.config.js @@ -0,0 +1,5 @@ +// @ts-check + +module.exports = { + extends: '../../babel.config.js', +} diff --git a/client/storybook/globals.d.ts b/client/storybook/globals.d.ts new file mode 100644 index 00000000000..0242f33e16d --- /dev/null +++ b/client/storybook/globals.d.ts @@ -0,0 +1,9 @@ +declare module '@storybook/addon-console' { + export declare const withConsole: () => (storyFn: any) => (context: StoryContext) => React.ReactElement +} + +declare interface Window { + MonacoEnvironment: { + getWorkerUrl(moduleId: string, label: string): string + } +} diff --git a/.storybook/jest.config.js b/client/storybook/jest.config.js similarity index 82% rename from .storybook/jest.config.js rename to client/storybook/jest.config.js index 51edb1bf556..7284291c2c1 100644 --- a/.storybook/jest.config.js +++ b/client/storybook/jest.config.js @@ -1,6 +1,6 @@ // @ts-check -const config = require('../jest.config.base') +const config = require('../../jest.config.base') const exportedConfig = { ...config, diff --git a/client/storybook/package.json b/client/storybook/package.json new file mode 100644 index 00000000000..a4e5ee5dcfe --- /dev/null +++ b/client/storybook/package.json @@ -0,0 +1,14 @@ +{ + "private": true, + "name": "@sourcegraph/storybook", + "version": "0.0.1", + "description": "Sourcegraph Storybook configuration", + "sideEffects": false, + "license": "Apache-2.0", + "scripts": { + "eslint": "eslint --cache 'src/**/*.[jt]s?(x)'", + "start": "start-storybook -p 9001 -c ./src -s ./assets", + "build": "build-storybook -c ./src -s ./assets", + "test": "jest" + } +} diff --git a/.storybook/coverage.test.ts b/client/storybook/src/coverage.test.ts similarity index 86% rename from .storybook/coverage.test.ts rename to client/storybook/src/coverage.test.ts index 5e8c5a9497e..11e8c9afaf6 100644 --- a/.storybook/coverage.test.ts +++ b/client/storybook/src/coverage.test.ts @@ -1,8 +1,8 @@ +import path from 'path' +import { pathToFileURL } from 'url' import initStoryshots from '@storybook/addon-storyshots' import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer' -import * as path from 'path' -import { pathToFileURL } from 'url' -import { recordCoverage } from '../client/shared/src/testing/coverage' +import { recordCoverage } from '@sourcegraph/shared/src/testing/coverage' // This test suite does not actually test anything. // It just loads up the storybook in Puppeteer and records its coverage, diff --git a/client/storybook/src/main.ts b/client/storybook/src/main.ts new file mode 100644 index 00000000000..5464e1fb288 --- /dev/null +++ b/client/storybook/src/main.ts @@ -0,0 +1,151 @@ +import path from 'path' +import { remove } from 'lodash' +import TerserPlugin from 'terser-webpack-plugin' +import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin' +import { Configuration, DefinePlugin, ProgressPlugin, RuleSetRule } from 'webpack' + +const rootPath = path.resolve(__dirname, '../../../') +const monacoEditorPaths = [path.resolve(rootPath, 'node_modules', 'monaco-editor')] +const storiesGlob = path.resolve(rootPath, 'client/**/*.story.tsx') + +const shouldMinify = !!process.env.MINIFY + +const config = { + stories: [storiesGlob], + addons: [ + '@storybook/addon-knobs', + '@storybook/addon-actions', + 'storybook-addon-designs', + 'storybook-dark-mode', + '@storybook/addon-a11y', + '@storybook/addon-toolbars', + './redesign-toggle-toolbar/register.ts', + ], + + webpackFinal: (config: Configuration) => { + // Include sourcemaps + config.mode = shouldMinify ? 'production' : 'development' + config.devtool = shouldMinify ? 'source-map' : 'cheap-module-eval-source-map' + + config.plugins?.push( + new DefinePlugin({ + NODE_ENV: JSON.stringify(config.mode), + 'process.env.NODE_ENV': JSON.stringify(config.mode), + }) + ) + + if (shouldMinify) { + config.optimization = { + namedModules: false, + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + sourceMap: true, + compress: { + // Don't inline functions, which causes name collisions with uglify-es: + // https://github.com/mishoo/UglifyJS2/issues/2842 + inline: 1, + }, + }, + }), + ], + } + } + + // We don't use Storybook's default Babel config for our repo, it doesn't include everything we need. + config.module?.rules.splice(0, 1) + + if (process.env.CI) { + remove(config.plugins || [], plugin => plugin instanceof ProgressPlugin) + } + + config.module?.rules.push({ + test: /\.tsx?$/, + loader: require.resolve('babel-loader'), + options: { + configFile: path.resolve(rootPath, 'babel.config.js'), + }, + }) + + config.plugins?.push( + new MonacoWebpackPlugin({ + languages: ['json'], + features: [ + 'bracketMatching', + 'clipboard', + 'coreCommands', + 'cursorUndo', + 'find', + 'format', + 'hover', + 'inPlaceReplace', + 'iPadShowKeyboard', + 'links', + 'suggest', + ], + }) + ) + + const storybookDirectory = path.resolve(rootPath, 'node_modules/@storybook') + config.resolve?.modules?.push('src') + + // Put our style rules at the beginning so they're processed by the time it + // gets to storybook's style rules. + config.module?.rules.unshift({ + test: /\.(sass|scss)$/, + use: [ + 'to-string-loader', + 'css-loader', + { + loader: 'postcss-loader', + }, + { + loader: 'sass-loader', + options: { + sassOptions: { + includePaths: [path.resolve(rootPath, 'node_modules')], + }, + }, + }, + ], + // Make sure Storybook styles get handled by the Storybook config + exclude: storybookDirectory, + }) + + // Make sure Storybook style loaders are only evaluated for Storybook styles. + const cssRule = config.module?.rules.find(rule => rule.test?.toString() === /\.css$/.toString()) as RuleSetRule + cssRule.include = storybookDirectory + + config.module?.rules.unshift({ + // CSS rule for external plain CSS (skip SASS and PostCSS for build perf) + test: /\.css$/, + // Make sure Storybook styles get handled by the Storybook config + exclude: [storybookDirectory, ...monacoEditorPaths], + use: ['to-string-loader', 'css-loader'], + }) + + config.module?.rules.unshift({ + // CSS rule for monaco-editor, it expects styles to be loaded with `style-loader`. + test: /\.css$/, + include: monacoEditorPaths, + // Make sure Storybook styles get handled by the Storybook config + exclude: [storybookDirectory], + use: ['style-loader', 'css-loader'], + }) + + config.module?.rules.unshift({ + test: /\.ya?ml$/, + use: ['raw-loader'], + }) + + Object.assign(config.entry, { + 'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js', + 'json.worker': 'monaco-editor/esm/vs/language/json/json.worker', + }) + + return config + }, +} + +module.exports = config diff --git a/.storybook/manager-head.html b/client/storybook/src/manager-head.html similarity index 100% rename from .storybook/manager-head.html rename to client/storybook/src/manager-head.html diff --git a/client/storybook/src/preview.ts b/client/storybook/src/preview.ts new file mode 100644 index 00000000000..3419f1f0f5c --- /dev/null +++ b/client/storybook/src/preview.ts @@ -0,0 +1,54 @@ +import 'focus-visible' +import { ReactElement } from 'react' +import isChromatic from 'chromatic/isChromatic' +import { withDesign } from 'storybook-addon-designs' +import { DecoratorFunction } from '@storybook/addons' +import { configureActions } from '@storybook/addon-actions' +import { withConsole } from '@storybook/addon-console' +import { setLinkComponent, AnchorLink } from '@sourcegraph/shared/src/components/Link' +import * as themes from './themes' + +const withConsoleDecorator: DecoratorFunction = (storyFn, context): ReactElement => + withConsole()(storyFn)(context) + +export const decorators = [withDesign, withConsoleDecorator] + +export const parameters = { + darkMode: { + stylePreview: true, + darkClass: 'theme-dark', + lightClass: 'theme-light', + light: themes.light, + dark: themes.dark, + }, +} + +configureActions({ depth: 100, limit: 20 }) + +setLinkComponent(AnchorLink) + +// Default to light theme for Chromatic and "Open canvas in new tab" button. +// addon-dark-mode will override this if it's running. +if (!document.body.classList.contains('theme-dark')) { + document.body.classList.add('theme-light') +} + +if (isChromatic()) { + const style = document.createElement('style') + style.innerHTML = ` + .monaco-editor .cursor { + visibility: hidden !important; + } + ` + document.head.append(style) +} + +window.MonacoEnvironment = { + getWorkerUrl(moduleId: string, label: string) { + if (label === 'json') { + return '/json.worker.bundle.js' + } + + return '/editor.worker.bundle.js' + }, +} diff --git a/client/storybook/src/redesign-toggle-toolbar/RedesignToggleStorybook.tsx b/client/storybook/src/redesign-toggle-toolbar/RedesignToggleStorybook.tsx new file mode 100644 index 00000000000..37628309a12 --- /dev/null +++ b/client/storybook/src/redesign-toggle-toolbar/RedesignToggleStorybook.tsx @@ -0,0 +1,48 @@ +import React, { ReactElement } from 'react' +import { Icons, IconButton } from '@storybook/components' +import { useRedesignToggle, REDESIGN_CLASS_NAME } from '@sourcegraph/shared/src/util/useRedesignToggle' + +const toggleRedesignClass = (element: HTMLElement, isRedesignEnabled: boolean): void => { + element.classList.toggle(REDESIGN_CLASS_NAME, !isRedesignEnabled) +} + +const updatePreview = (isRedesignEnabled: boolean): void => { + const iframe = document.querySelector('#storybook-preview-iframe') as HTMLIFrameElement | undefined + + const iframeDocument = iframe?.contentDocument || iframe?.contentWindow?.document + const body = iframeDocument?.body + + if (body) { + toggleRedesignClass(body, isRedesignEnabled) + } +} + +const updateManager = (isRedesignEnabled: boolean): void => { + const manager = document.querySelector('body') + + if (manager) { + toggleRedesignClass(manager, isRedesignEnabled) + } +} + +export const RedesignToggleStorybook = (): ReactElement => { + const { isRedesignEnabled, setIsRedesignEnabled } = useRedesignToggle() + + const handleRedesignToggle = (): void => { + setIsRedesignEnabled(!isRedesignEnabled) + updatePreview(isRedesignEnabled) + updateManager(isRedesignEnabled) + } + + return ( + + + + ) +} diff --git a/client/storybook/src/redesign-toggle-toolbar/register.ts b/client/storybook/src/redesign-toggle-toolbar/register.ts new file mode 100644 index 00000000000..9c458366f9a --- /dev/null +++ b/client/storybook/src/redesign-toggle-toolbar/register.ts @@ -0,0 +1,14 @@ +import addons, { types } from '@storybook/addons' +import { RedesignToggleStorybook } from './RedesignToggleStorybook' + +/** + * Custom toolbar which renders button to toggle redesign theme global CSS class. + */ +addons.register('sourcegraph/redesign-toggle-toolbar', () => { + addons.add('sourcegraph/redesign-toggle-toolbar', { + title: 'Redesign toggle toolbar', + type: types.TOOL, + match: ({ viewMode }) => viewMode === 'story' || viewMode === 'docs', + render: RedesignToggleStorybook, + }) +}) diff --git a/client/storybook/src/themes.ts b/client/storybook/src/themes.ts new file mode 100644 index 00000000000..d76e37be71d --- /dev/null +++ b/client/storybook/src/themes.ts @@ -0,0 +1,35 @@ +import { ThemeVars, themes } from '@storybook/theming' +import openColor from 'open-color' + +// Themes use the colors from our webapp. +const common: Omit = { + colorPrimary: openColor.blue[6], + colorSecondary: openColor.blue[6], + brandTitle: 'Sourcegraph Wildcard design system', + brandUrl: 'https://sourcegraph.com', + brandImage: '/img/wildcard-design-system.svg', + fontBase: + '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', + fontCode: 'sfmono-regular, consolas, menlo, dejavu sans mono, monospace', +} + +export const dark: ThemeVars = { + ...themes.dark, + ...common, + appBg: '#1c2736', + appContentBg: '#151c28', + appBorderColor: '#2b3750', + barBg: '#0e121b', + barTextColor: '#a2b0cd', + textColor: '#f2f4f8', + inputTextColor: '#ffffff', +} + +export const light: ThemeVars = { + ...themes.light, + ...common, + appBg: '#fbfdff', + textColor: '#2b3750', + barTextColor: '#566e9f', + inputTextColor: '#2b3750', +} diff --git a/client/storybook/tsconfig.json b/client/storybook/tsconfig.json new file mode 100644 index 00000000000..cdb52c96b58 --- /dev/null +++ b/client/storybook/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "sourceRoot": "src", + "rootDir": ".", + "outDir": "./out", + "baseUrl": "./src", + "jsx": "react", + }, + "references": [{ "path": "../shared" }], + "include": ["./src/**/*", "./*.ts"], +} diff --git a/dev/foreach-ts-project.sh b/dev/foreach-ts-project.sh index 47cdb8acf81..a3d45577b65 100755 --- a/dev/foreach-ts-project.sh +++ b/dev/foreach-ts-project.sh @@ -19,6 +19,7 @@ DIRS=( client/extension-api client/eslint-plugin-sourcegraph client/extension-api-types + client/storybook dev/release dev/ts-morph ) diff --git a/jest.config.js b/jest.config.js index dfc3937861b..ec2f3eb23da 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,6 +11,6 @@ module.exports = { 'client/branded/jest.config.js', 'client/web/jest.config.js', 'client/wildcard/jest.config.js', - '.storybook/jest.config.js', + 'client/storybook/jest.config.js', ], } diff --git a/package.json b/package.json index 11222ff0d38..069a974d4b2 100644 --- a/package.json +++ b/package.json @@ -27,9 +27,9 @@ "cover-integration": "nyc --hook-require=false yarn test-integration", "test-e2e": "TS_NODE_PROJECT=client/web/src/end-to-end/tsconfig.json mocha ./client/web/src/end-to-end/end-to-end.test.ts", "cover-e2e": "nyc --hook-require=false --silent=true yarn test-e2e", - "storybook": "start-storybook -p 9001 -c .storybook -s ui/assets", - "build-storybook": "build-storybook -c .storybook -s ui/assets", - "cover-storybook": "nyc --hook-require=false yarn jest .storybook/coverage", + "storybook": "yarn workspace @sourcegraph/storybook run start", + "build-storybook": "yarn workspace @sourcegraph/storybook run build", + "cover-storybook": "nyc --hook-require=false yarn jest client/storybook/src/coverage", "deduplicate": "yarn-deduplicate -s fewer", "release": "cd dev/release && yarn run release", "docsite:serve": "./dev/docsite.sh -config doc/docsite.json serve -http=localhost:5080", @@ -58,7 +58,8 @@ "exclude": [ "node_modules", "**/*.d.ts", - "**/*.@(test|story).ts?(x)" + "**/*.@(test|story).ts?(x)", + "client/storybook" ] }, "jscpd": { @@ -175,6 +176,7 @@ "@types/sinon": "9.0.4", "@types/socket.io": "2.1.10", "@types/socket.io-client": "1.4.33", + "@types/terser-webpack-plugin": "^4.2.0", "@types/testing-library__jest-dom": "^5.9.5", "@types/textarea-caret": "3.0.0", "@types/uuid": "8.0.1", diff --git a/tsconfig.all.json b/tsconfig.all.json index a7d583987e6..125744fdc08 100644 --- a/tsconfig.all.json +++ b/tsconfig.all.json @@ -13,6 +13,7 @@ { "path": "client/browser/src/end-to-end" }, { "path": "client/extension-api" }, { "path": "client/extension-api-types" }, + { "path": "client/storybook" }, { "path": "dev/release" }, { "path": "schema" }, ], diff --git a/yarn.lock b/yarn.lock index 14d1eb02d31..39d62fd84d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4373,6 +4373,14 @@ resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74" integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA== +"@types/terser-webpack-plugin@^4.2.0": + version "4.2.0" + resolved "https://registry.npmjs.org/@types/terser-webpack-plugin/-/terser-webpack-plugin-4.2.0.tgz#fe39917d334287c5cf25abcf370867a31ed59cd6" + integrity sha512-oGfGZzjwKY7s8gAYLZJuVuu9GXuc/ACo7bL/DQg7ROFkEMFQULB1W7qZjQrTXf2SkTfQx7/zcerfuLkUCVFGhg== + dependencies: + "@types/webpack" "*" + terser "^4.6.13" + "@types/testing-library__jest-dom@^5.9.1", "@types/testing-library__jest-dom@^5.9.5": version "5.9.5" resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz#5bf25c91ad2d7b38f264b12275e5c92a66d849b0" @@ -21129,7 +21137,7 @@ terser-webpack-plugin@^4.2.3: terser "^5.3.4" webpack-sources "^1.4.3" -terser@^4.1.2, terser@^4.6.3, terser@^4.8.0: +terser@^4.1.2, terser@^4.6.13, terser@^4.6.3, terser@^4.8.0: version "4.8.0" resolved "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==