Fix conflicting storybook styles (#10033)

This commit is contained in:
Felix Becker 2020-04-20 18:05:16 +02:00 committed by GitHub
parent a04a648e8b
commit a2378e5176
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 175 additions and 92 deletions

View File

@ -5,8 +5,9 @@ import { withInfo } from '@storybook/addon-info'
import { withKnobs } from '@storybook/addon-knobs'
import { addDecorator, addParameters, configure } from '@storybook/react'
import { themes } from '@storybook/theming'
import { setLinkComponent, AnchorLink } from '../shared/src/components/Link'
import './styles'
setLinkComponent(AnchorLink)
async function main(): Promise<void> {
// Webpack provides require.context. TODO: If this is run in Jest in the future, we'll need to

View File

@ -20,7 +20,7 @@ export default ({ config }: { config: webpack.Configuration }) => {
config.module.rules.unshift({
test: /\.(css|sass|scss)$/,
use: [
'style-loader',
'to-string-loader',
'css-loader',
{
loader: 'sass-loader',

View File

@ -55,3 +55,8 @@ declare module 'worker-loader?*' {
}
export default WebpackWorker
}
declare module '*.scss' {
const cssModule: string
export default cssModule
}

View File

@ -1,19 +1,23 @@
import * as React from 'react'
import { storiesOf } from '@storybook/react'
import '../../app.scss'
import { action } from '@storybook/addon-actions'
import { OptionsHeader } from './OptionsHeader'
import optionsStyles from '../../options.scss'
storiesOf('Options - OptionsHeader', module).add('Default', () => (
<div style={{ maxWidth: 400 }}>
<OptionsHeader
version="0.0.0"
isActivated={true}
onSettingsClick={action('Settings clicked')}
onToggleActivationClick={action('Toggle activation clicked')}
/>
</div>
))
storiesOf('Options - OptionsHeader', module)
.addDecorator(story => (
<>
<style>{optionsStyles}</style>
<div>{story()}</div>
</>
))
.add('Default', () => (
<div style={{ maxWidth: 400 }}>
<OptionsHeader
version="0.0.0"
isActivated={true}
onSettingsClick={action('Settings clicked')}
onToggleActivationClick={action('Toggle activation clicked')}
/>
</div>
))

View File

@ -1,47 +1,46 @@
import * as React from 'react'
import { action } from '@storybook/addon-actions'
import { storiesOf } from '@storybook/react'
import '../../app.scss'
import { OptionsMenu } from './OptionsMenu'
import optionsStyles from '../../options.scss'
storiesOf('Options - OptionsMenu', module)
.addDecorator(story => (
<>
<style>{optionsStyles}</style>
<div>{story()}</div>
</>
))
.add('Default', () => (
<div style={{ maxWidth: 400, marginLeft: 20, marginTop: 20, boxShadow: '0 0 12px 0 rgba(0, 0, 0, 0.15)' }}>
<OptionsMenu
version="0.0.0"
status="connected"
sourcegraphURL="https://sourcegraph.com"
onURLChange={action('Sourcegraph URL changed')}
onURLSubmit={action('New Sourcegraph URL submitted')}
onSettingsClick={action('Settings clicked')}
onToggleActivationClick={action('Toggle activation clicked')}
featureFlags={[]}
isSettingsOpen={false}
isActivated={true}
toggleFeatureFlag={action('Feature flag toggled')}
requestPermissions={() => undefined}
urlHasPermissions={true}
/>
</div>
<OptionsMenu
version="0.0.0"
status="connected"
sourcegraphURL="https://sourcegraph.com"
onURLChange={action('Sourcegraph URL changed')}
onURLSubmit={action('New Sourcegraph URL submitted')}
onSettingsClick={action('Settings clicked')}
onToggleActivationClick={action('Toggle activation clicked')}
featureFlags={[]}
isSettingsOpen={false}
isActivated={true}
toggleFeatureFlag={action('Feature flag toggled')}
requestPermissions={() => undefined}
urlHasPermissions={true}
/>
))
.add('Settings open', () => (
<div style={{ maxWidth: 400, marginLeft: 20, marginTop: 20, boxShadow: '0 0 12px 0 rgba(0, 0, 0, 0.15)' }}>
<OptionsMenu
version="0.0.0"
status="connected"
sourcegraphURL="https://sourcegraph.com"
onURLChange={action('Sourcegraph URL changed')}
onURLSubmit={action('New Sourcegraph URL submitted')}
onSettingsClick={action('Settings clicked')}
onToggleActivationClick={action('Toggle activation clicked')}
isSettingsOpen={true}
isActivated={true}
toggleFeatureFlag={action('Feature flag toggled')}
requestPermissions={() => undefined}
urlHasPermissions={true}
/>
</div>
<OptionsMenu
version="0.0.0"
status="connected"
sourcegraphURL="https://sourcegraph.com"
onURLChange={action('Sourcegraph URL changed')}
onURLSubmit={action('New Sourcegraph URL submitted')}
onSettingsClick={action('Settings clicked')}
onToggleActivationClick={action('Toggle activation clicked')}
isSettingsOpen={true}
isActivated={true}
toggleFeatureFlag={action('Feature flag toggled')}
requestPermissions={() => undefined}
urlHasPermissions={true}
/>
))

View File

@ -1,12 +1,9 @@
import * as React from 'react'
import { action } from '@storybook/addon-actions'
import { storiesOf } from '@storybook/react'
import '../../app.scss'
import { interval, Subscription } from 'rxjs'
import { ConnectionErrors, ServerURLForm, ServerURLFormProps } from './ServerURLForm'
import optionsStyles from '../../options.scss'
class Container extends React.Component<{}, { value: string; status: ServerURLFormProps['status'] }> {
public state = { value: 'https://sourcegraph.com', status: 'connected' as ServerURLFormProps['status'] }
@ -87,6 +84,12 @@ class CyclingStatus extends React.Component<{}, { step: number }> {
}
storiesOf('Options - ServerURLForm', module)
.addDecorator(story => (
<>
<style>{optionsStyles}</style>
<div>{story()}</div>
</>
))
.add('Interactive', () => <Container />)
.add('Cycling Status', () => <CyclingStatus />)
.add('Error Status', () => (

View File

@ -219,6 +219,7 @@
"term-size": "^2.2.0",
"terser-webpack-plugin": "^2.3.4",
"thread-loader": "^2.1.3",
"to-string-loader": "^1.1.6",
"ts-node": "^8.8.1",
"typescript": "^3.8.3",
"utc-version": "^2.0.1",
@ -284,6 +285,7 @@
"sourcegraph": "link:packages/sourcegraph-extension-api",
"string-score": "^1.0.1",
"symbol-observable": "^1.2.0",
"tagged-template-noop": "^2.1.1",
"textarea-caret": "^3.1.0",
"ts-key-enum": "^2.0.2",
"tslib": "^1.11.1",

View File

@ -4,8 +4,15 @@ import * as H from 'history'
import React from 'react'
import { NOOP_TELEMETRY_SERVICE } from '../telemetry/telemetryService'
import { ActionItem, ActionItemComponentProps } from './ActionItem'
import './ActionItem.scss'
import { NEVER } from 'rxjs'
import webStyles from '../../../web/src/SourcegraphWebApp.scss'
const { add } = storiesOf('ActionItem', module).addDecorator(story => (
<>
<style>{webStyles}</style>
<div className="p-4">{story()}</div>
</>
))
const EXTENSIONS_CONTROLLER: ActionItemComponentProps['extensionsController'] = {
executeCommand: () => new Promise(resolve => setTimeout(resolve, 750)),
@ -23,8 +30,6 @@ const ICON_URL =
const onDidExecute = action('onDidExecute')
const { add } = storiesOf('ActionItem', module)
add('noop action', () => (
<ActionItem
action={{ id: 'a', command: undefined, actionItem: { label: 'Hello' } }}

View File

@ -2,11 +2,25 @@ import { action } from '@storybook/addon-actions'
import { storiesOf } from '@storybook/react'
import React from 'react'
import { Toggle } from './Toggle'
import './Toggle.scss'
import toggleStyles from './Toggle.scss'
import css from 'tagged-template-noop'
const onToggle = action('onToggle')
const { add } = storiesOf('Toggle', module)
const cssVars = css`
:root {
--text-muted: grey;
--primary: blue;
}
`
const { add } = storiesOf('Toggle', module).addDecorator(story => (
<>
<style>{toggleStyles}</style>
<style>{cssVars}</style>
<div>{story()}</div>
</>
))
add('interactive', () => {
interface State {

View File

@ -1,13 +1,18 @@
import * as H from 'history'
import { storiesOf } from '@storybook/react'
import React from 'react'
import { ActivationDropdown } from './ActivationDropdown'
import { ActivationDropdown, ActivationDropdownProps } from './ActivationDropdown'
import { Activation } from './Activation'
import { boolean } from '@storybook/addon-knobs'
import { action } from '@storybook/addon-actions'
import webMainStyles from '../../../../web/src/SourcegraphWebApp.scss'
import { subTypeOf } from '../../util/types'
const { add } = storiesOf('ActivationDropdown', module).addDecorator(story => (
<div className="theme-light container">{story()}</div>
<>
<style>{webMainStyles}</style>
<div className="theme-light">{story()}</div>
</>
))
const baseActivation: Activation = {
@ -44,12 +49,17 @@ const baseActivation: Activation = {
completed: undefined,
}
const history = H.createMemoryHistory({ keyLength: 0 })
const commonProps = subTypeOf<Partial<ActivationDropdownProps>>()({
alwaysShow: true,
history,
// Make sure the dropdown is not rendered outside the theme-light container
portal: false,
})
add('Loading', () => <ActivationDropdown alwaysShow={true} history={history} activation={baseActivation} />)
add('Loading', () => <ActivationDropdown {...commonProps} activation={baseActivation} />)
add('0/4 completed', () => (
<ActivationDropdown
alwaysShow={true}
history={history}
{...commonProps}
activation={{
...baseActivation,
completed: {
@ -63,8 +73,7 @@ add('0/4 completed', () => (
))
add('1/4 completed', () => (
<ActivationDropdown
alwaysShow={true}
history={history}
{...commonProps}
activation={{
...baseActivation,
completed: {

View File

@ -6,10 +6,10 @@ import { concat, of, Subject, Subscription } from 'rxjs'
import { concatMap, delay, filter, map, pairwise, startWith, tap } from 'rxjs/operators'
import { Activation, percentageDone } from './Activation'
import { ActivationChecklist } from './ActivationChecklist'
import { Menu, MenuButton, MenuPopover } from '@reach/menu-button'
import { Menu, MenuButton, MenuPopover, MenuPopoverProps } from '@reach/menu-button'
import classNames from 'classnames'
interface Props {
export interface ActivationDropdownProps extends Pick<MenuPopoverProps, 'portal'> {
history: H.History
activation: Activation
/**
@ -28,9 +28,9 @@ const animationDurationMillis = 3260
* Renders the activation status navlink item, a dropdown button that shows activation
* status in the navbar.
*/
export class ActivationDropdown extends React.PureComponent<Props, State> {
export class ActivationDropdown extends React.PureComponent<ActivationDropdownProps, State> {
public state: State = { animate: false, displayEvenIfFullyCompleted: false }
private componentUpdates = new Subject<Props>()
private componentUpdates = new Subject<ActivationDropdownProps>()
private subscriptions = new Subscription()
public componentDidMount(): void {
@ -123,6 +123,7 @@ export class ActivationDropdown extends React.PureComponent<Props, State> {
</MenuButton>
<MenuPopover
className={classNames('activation-dropdown', 'dropdown-menu', { show: isExpanded })}
portal={this.props.portal}
>
<div className="dropdown-item-text activation-dropdown-header">
<h3 className="mb-0">

View File

@ -4,16 +4,23 @@ import { storiesOf } from '@storybook/react'
import React, { useState } from 'react'
import { CompletionList } from 'sourcegraph'
import { CompletionWidget, CompletionWidgetProps } from './CompletionWidget'
import 'bootstrap/scss/bootstrap.scss'
import './CompletionWidget.scss'
import bootstrapStyles from 'bootstrap/scss/bootstrap.scss'
import completionWidgetStyles from './CompletionWidget.scss'
const onSelectItem = action('onSelectItem')
// Disable keyboard shortcuts because in CompletionWidget the cursor is in a contenteditable element,
// which Storybook doesn't consider to be an input, so it intercepts keyboard shortcuts instead of
// propagating them to the CompletionWidget element.
const { add } = storiesOf('CompletionWidget', module).addParameters({ options: { enableShortcuts: false } })
const { add } = storiesOf('CompletionWidget', module)
.addParameters({ options: { enableShortcuts: false } })
.addDecorator(story => (
<>
<style>{bootstrapStyles}</style>
<style>{completionWidgetStyles}</style>
<div>{story()}</div>
</>
))
const completionWidgetListItemClassName = 'completion-widget-dropdown__item d-flex align-items-center p-2'
const StyledCompletionWidget: React.FunctionComponent<CompletionWidgetProps> = props => (

View File

@ -10,6 +10,11 @@ declare module 'worker-loader?*' {
export default WebpackWorker
}
declare module '*.scss' {
const cssModule: string
export default cssModule
}
/**
* Set by shared/dev/jest-environment.js
*/

View File

@ -7,8 +7,8 @@ import { interval } from 'rxjs'
import { map, startWith } from 'rxjs/operators'
import { NotificationType as NotificationTypeType } from 'sourcegraph'
import { NotificationItem } from './NotificationItem'
import './NotificationItem.scss'
import notificationItemStyles from './NotificationItem.scss'
import webStyles from '../../../web/src/SourcegraphWebApp.scss'
const notificationClassNames = {
[NotificationType.Log]: 'alert alert-secondary',
@ -21,7 +21,13 @@ const notificationClassNames = {
const onDismiss = action('onDismiss')
const { add } = storiesOf('NotificationItem', module).addDecorator(story => (
<div style={{ maxWidth: '20rem', margin: '2rem' }}>{story()}</div>
<>
<style>{webStyles}</style>
<style>{notificationItemStyles}</style>
<div className="theme-light" style={{ maxWidth: '20rem', margin: '2rem' }}>
{story()}
</div>
</>
))
for (const [name, type] of Object.entries(NotificationType)) {

View File

@ -1,11 +1,15 @@
import { storiesOf } from '@storybook/react'
import React, { useCallback } from 'react'
import { Tooltip } from './Tooltip'
import './Tooltip.scss'
import tooltipStyles from './Tooltip.scss'
import bootstrapStyles from 'bootstrap/scss/bootstrap.scss'
const { add } = storiesOf('Tooltip', module).addDecorator(story => (
<div style={{ maxWidth: '20rem', margin: '2rem' }}>{story()}</div>
<>
<style>{bootstrapStyles}</style>
<style>{tooltipStyles}</style>
<div style={{ maxWidth: '20rem', margin: '2rem' }}>{story()}</div>
</>
))
add('Hover', () => (

View File

@ -2,12 +2,15 @@ import { storiesOf } from '@storybook/react'
import React from 'react'
import { CampaignStatus } from './CampaignStatus'
import { BackgroundProcessState } from '../../../../../shared/src/graphql/schema'
import '../../../main.scss'
import { action } from '@storybook/addon-actions'
import { boolean } from '@storybook/addon-knobs'
import webStyles from '../../../SourcegraphWebApp.scss'
const { add } = storiesOf('CampaignStatus', module).addDecorator(story => (
<div className="theme-light container">{story()}</div>
<>
<style>{webStyles}</style>
<div className="theme-light container">{story()}</div>
</>
))
add('Errored', () => (

View File

@ -150,3 +150,8 @@ declare module 'worker-loader?*' {
}
export default WebpackWorker
}
declare module '*.scss' {
const cssModule: string
export default cssModule
}

View File

@ -6,15 +6,15 @@ import { MemoryRouter } from 'react-router'
import * as GQL from '../../../shared/src/graphql/schema'
import { ThemePreference } from '../theme'
import { UserNavItem } from './UserNavItem'
import './UserNavItem.scss'
import webStyles from '../SourcegraphWebApp.scss'
const onThemePreferenceChange = action('onThemePreferenceChange')
const { add } = storiesOf('UserNavItem', module).addDecorator(story => (
<div className="theme-light" style={{ display: 'inline-block', position: 'relative', margin: '2rem' }}>
{story()}
</div>
<>
<style>{webStyles}</style>
<div className="theme-light">{story()}</div>
</>
))
const OpenUserNavItem: React.FunctionComponent<UserNavItem['props']> = props => {

View File

@ -7,7 +7,6 @@ import { ThemePreference } from '../theme'
import { UserNavItem } from './UserNavItem'
describe('UserNavItem', () => {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const ORG_CONNECTION = {
__typename: 'OrgConnection',
nodes: [
@ -16,7 +15,6 @@ describe('UserNavItem', () => {
] as unknown,
totalCount: 2,
} as GQL.IOrgConnection
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const USER = { username: 'u', url: '/u', settingsURL: '/u/settings', organizations: ORG_CONNECTION } as GQL.IUser
const history = H.createMemoryHistory({ keyLength: 0 })

View File

@ -19548,6 +19548,11 @@ table@^5.2.3, table@^5.4.6:
slice-ansi "^2.1.0"
string-width "^3.0.0"
tagged-template-noop@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/tagged-template-noop/-/tagged-template-noop-2.1.1.tgz#0d215fd9eec557290f41838a22116a8d4ce606dd"
integrity sha512-diZ004cBHKVueqSr5p+/EPZhofCBRW7w7zZL71FcK8x+209BbMw77ICrP9AWXpVjPyyyIqRYYFAM4Wjk2HNWQg==
tapable@^1.0.0, tapable@^1.1.0, tapable@^1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
@ -19903,6 +19908,13 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
to-string-loader@^1.1.6:
version "1.1.6"
resolved "https://registry.npmjs.org/to-string-loader/-/to-string-loader-1.1.6.tgz#230529ccc63dd0ecca052a85e1fb82afe946b0ab"
integrity sha512-VNg62//PS1WfNwrK3n7t6wtK5Vdtx/qeYLLEioW46VMlYUwAYT6wnfB+OwS2FMTCalIHu0tk79D3RXX8ttmZTQ==
dependencies:
loader-utils "^1.0.0"
to-through@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6"