mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 12:51:55 +00:00
web: moved Storybook config into a new package (#19569)
* web: moved Storybook config into a new package * web: added storybook package to client/README * web: checkin @types/terser-webpack-plugin * web: yarn deduplicate * web: prettier * web: yarn.lock carriage-return * web: yarn deduplicate * web: removed redundant eslint command * web: restored manager-head.html * web: removed @storybook/core-common dependency for now * web: moved cover-storybook back to root package.json * web: updated stories glob * web: fixed babel-plugin-istanbul cwd * web: added storybook workspace to exclude list in nyc config * web: removed redundant change
This commit is contained in:
parent
c43dc2e1fa
commit
7cf15290a2
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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'
|
||||
},
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
managerEntries: (entry = []) => {
|
||||
return [...entry, require.resolve('./register')]
|
||||
},
|
||||
}
|
||||
@ -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 (
|
||||
<IconButton
|
||||
key="redesign-toolbar"
|
||||
active={isRedesignEnabled}
|
||||
title={isRedesignEnabled ? 'Disable redesign theme' : 'Enable redesign theme'}
|
||||
onClick={handleRedesignToggle}
|
||||
>
|
||||
<Icons icon="beaker" />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
})
|
||||
})
|
||||
@ -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<import('@storybook/theming').ThemeVars>} */
|
||||
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',
|
||||
}
|
||||
@ -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',
|
||||
{
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
1
client/storybook/.eslintignore
Normal file
1
client/storybook/.eslintignore
Normal file
@ -0,0 +1 @@
|
||||
out/
|
||||
13
client/storybook/.eslintrc.js
Normal file
13
client/storybook/.eslintrc.js
Normal file
@ -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,
|
||||
}
|
||||
12
client/storybook/README.md
Normal file
12
client/storybook/README.md
Normal file
@ -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
|
||||
```
|
||||
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
5
client/storybook/babel.config.js
Normal file
5
client/storybook/babel.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
// @ts-check
|
||||
|
||||
module.exports = {
|
||||
extends: '../../babel.config.js',
|
||||
}
|
||||
9
client/storybook/globals.d.ts
vendored
Normal file
9
client/storybook/globals.d.ts
vendored
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
// @ts-check
|
||||
|
||||
const config = require('../jest.config.base')
|
||||
const config = require('../../jest.config.base')
|
||||
|
||||
const exportedConfig = {
|
||||
...config,
|
||||
14
client/storybook/package.json
Normal file
14
client/storybook/package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
151
client/storybook/src/main.ts
Normal file
151
client/storybook/src/main.ts
Normal file
@ -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
|
||||
54
client/storybook/src/preview.ts
Normal file
54
client/storybook/src/preview.ts
Normal file
@ -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<ReactElement> = (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'
|
||||
},
|
||||
}
|
||||
@ -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 (
|
||||
<IconButton
|
||||
key="redesign-toolbar"
|
||||
active={isRedesignEnabled}
|
||||
title={isRedesignEnabled ? 'Disable redesign theme' : 'Enable redesign theme'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={handleRedesignToggle}
|
||||
>
|
||||
<Icons icon="beaker" />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
14
client/storybook/src/redesign-toggle-toolbar/register.ts
Normal file
14
client/storybook/src/redesign-toggle-toolbar/register.ts
Normal file
@ -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,
|
||||
})
|
||||
})
|
||||
35
client/storybook/src/themes.ts
Normal file
35
client/storybook/src/themes.ts
Normal file
@ -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<ThemeVars, 'base'> = {
|
||||
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',
|
||||
}
|
||||
13
client/storybook/tsconfig.json
Normal file
13
client/storybook/tsconfig.json
Normal file
@ -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"],
|
||||
}
|
||||
@ -19,6 +19,7 @@ DIRS=(
|
||||
client/extension-api
|
||||
client/eslint-plugin-sourcegraph
|
||||
client/extension-api-types
|
||||
client/storybook
|
||||
dev/release
|
||||
dev/ts-morph
|
||||
)
|
||||
|
||||
@ -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',
|
||||
],
|
||||
}
|
||||
|
||||
10
package.json
10
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",
|
||||
|
||||
@ -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" },
|
||||
],
|
||||
|
||||
10
yarn.lock
10
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==
|
||||
|
||||
Loading…
Reference in New Issue
Block a user