From a2378e51767b85a89b854366e3648106a0fd0be2 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Mon, 20 Apr 2020 18:05:16 +0200 Subject: [PATCH] Fix conflicting storybook styles (#10033) --- .storybook/config.ts | 3 +- .storybook/webpack.config.ts | 2 +- browser/src/globals.d.ts | 5 ++ .../src/libs/options/OptionsHeader.story.tsx | 32 ++++---- .../src/libs/options/OptionsMenu.story.tsx | 73 +++++++++---------- .../src/libs/options/ServerURLForm.story.tsx | 11 ++- package.json | 2 + shared/src/actions/ActionItem.story.tsx | 11 ++- shared/src/components/Toggle.story.tsx | 18 ++++- .../activation/ActivationDropdown.story.tsx | 23 ++++-- .../activation/ActivationDropdown.tsx | 9 ++- .../completion/CompletionWidget.story.tsx | 15 +++- shared/src/globals.d.ts | 5 ++ .../notifications/NotificationItem.story.tsx | 12 ++- web/src/components/tooltip/Tooltip.story.tsx | 10 ++- .../campaigns/detail/CampaignStatus.story.tsx | 7 +- web/src/globals.d.ts | 5 ++ web/src/nav/UserNavItem.story.tsx | 10 +-- web/src/nav/UserNavItem.test.tsx | 2 - yarn.lock | 12 +++ 20 files changed, 175 insertions(+), 92 deletions(-) diff --git a/.storybook/config.ts b/.storybook/config.ts index 81202329006..7ef7bdd7413 100644 --- a/.storybook/config.ts +++ b/.storybook/config.ts @@ -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 { // Webpack provides require.context. TODO: If this is run in Jest in the future, we'll need to diff --git a/.storybook/webpack.config.ts b/.storybook/webpack.config.ts index a7748c92025..64009e5cafe 100644 --- a/.storybook/webpack.config.ts +++ b/.storybook/webpack.config.ts @@ -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', diff --git a/browser/src/globals.d.ts b/browser/src/globals.d.ts index b5c3bba67ee..37b767346b9 100644 --- a/browser/src/globals.d.ts +++ b/browser/src/globals.d.ts @@ -55,3 +55,8 @@ declare module 'worker-loader?*' { } export default WebpackWorker } + +declare module '*.scss' { + const cssModule: string + export default cssModule +} diff --git a/browser/src/libs/options/OptionsHeader.story.tsx b/browser/src/libs/options/OptionsHeader.story.tsx index 37fa0c60a2e..f00f118fb19 100644 --- a/browser/src/libs/options/OptionsHeader.story.tsx +++ b/browser/src/libs/options/OptionsHeader.story.tsx @@ -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', () => ( -
- -
-)) +storiesOf('Options - OptionsHeader', module) + .addDecorator(story => ( + <> + +
{story()}
+ + )) + .add('Default', () => ( +
+ +
+ )) diff --git a/browser/src/libs/options/OptionsMenu.story.tsx b/browser/src/libs/options/OptionsMenu.story.tsx index 9c0a00f958d..50494eb2e87 100644 --- a/browser/src/libs/options/OptionsMenu.story.tsx +++ b/browser/src/libs/options/OptionsMenu.story.tsx @@ -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 => ( + <> + +
{story()}
+ + )) .add('Default', () => ( -
- undefined} - urlHasPermissions={true} - /> -
+ undefined} + urlHasPermissions={true} + /> )) .add('Settings open', () => ( -
- undefined} - urlHasPermissions={true} - /> -
+ undefined} + urlHasPermissions={true} + /> )) diff --git a/browser/src/libs/options/ServerURLForm.story.tsx b/browser/src/libs/options/ServerURLForm.story.tsx index 23268e72cae..8b8df899bf6 100644 --- a/browser/src/libs/options/ServerURLForm.story.tsx +++ b/browser/src/libs/options/ServerURLForm.story.tsx @@ -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 => ( + <> + +
{story()}
+ + )) .add('Interactive', () => ) .add('Cycling Status', () => ) .add('Error Status', () => ( diff --git a/package.json b/package.json index 248aedaadf4..79f4b227c80 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/shared/src/actions/ActionItem.story.tsx b/shared/src/actions/ActionItem.story.tsx index 46dcf17c952..9232b454711 100644 --- a/shared/src/actions/ActionItem.story.tsx +++ b/shared/src/actions/ActionItem.story.tsx @@ -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 => ( + <> + +
{story()}
+ +)) 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', () => ( ( + <> + + +
{story()}
+ +)) add('interactive', () => { interface State { diff --git a/shared/src/components/activation/ActivationDropdown.story.tsx b/shared/src/components/activation/ActivationDropdown.story.tsx index fb0a74e6430..a5ede66f9c5 100644 --- a/shared/src/components/activation/ActivationDropdown.story.tsx +++ b/shared/src/components/activation/ActivationDropdown.story.tsx @@ -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 => ( -
{story()}
+ <> + +
{story()}
+ )) const baseActivation: Activation = { @@ -44,12 +49,17 @@ const baseActivation: Activation = { completed: undefined, } const history = H.createMemoryHistory({ keyLength: 0 }) +const commonProps = subTypeOf>()({ + alwaysShow: true, + history, + // Make sure the dropdown is not rendered outside the theme-light container + portal: false, +}) -add('Loading', () => ) +add('Loading', () => ) add('0/4 completed', () => ( ( )) add('1/4 completed', () => ( { 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 { +export class ActivationDropdown extends React.PureComponent { public state: State = { animate: false, displayEvenIfFullyCompleted: false } - private componentUpdates = new Subject() + private componentUpdates = new Subject() private subscriptions = new Subscription() public componentDidMount(): void { @@ -123,6 +123,7 @@ export class ActivationDropdown extends React.PureComponent {

diff --git a/shared/src/components/completion/CompletionWidget.story.tsx b/shared/src/components/completion/CompletionWidget.story.tsx index fe11376935a..6f0cdf8b4ea 100644 --- a/shared/src/components/completion/CompletionWidget.story.tsx +++ b/shared/src/components/completion/CompletionWidget.story.tsx @@ -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 => ( + <> + + +
{story()}
+ + )) const completionWidgetListItemClassName = 'completion-widget-dropdown__item d-flex align-items-center p-2' const StyledCompletionWidget: React.FunctionComponent = props => ( diff --git a/shared/src/globals.d.ts b/shared/src/globals.d.ts index 71da3d18a3b..ce51b619566 100644 --- a/shared/src/globals.d.ts +++ b/shared/src/globals.d.ts @@ -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 */ diff --git a/shared/src/notifications/NotificationItem.story.tsx b/shared/src/notifications/NotificationItem.story.tsx index a2fe0ff7c1d..a78b56214e6 100644 --- a/shared/src/notifications/NotificationItem.story.tsx +++ b/shared/src/notifications/NotificationItem.story.tsx @@ -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 => ( -
{story()}
+ <> + + +
+ {story()} +
+ )) for (const [name, type] of Object.entries(NotificationType)) { diff --git a/web/src/components/tooltip/Tooltip.story.tsx b/web/src/components/tooltip/Tooltip.story.tsx index e67c46d9d64..8e61e020299 100644 --- a/web/src/components/tooltip/Tooltip.story.tsx +++ b/web/src/components/tooltip/Tooltip.story.tsx @@ -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 => ( -
{story()}
+ <> + + +
{story()}
+ )) add('Hover', () => ( diff --git a/web/src/enterprise/campaigns/detail/CampaignStatus.story.tsx b/web/src/enterprise/campaigns/detail/CampaignStatus.story.tsx index 62f9460c156..fbf77180ce7 100644 --- a/web/src/enterprise/campaigns/detail/CampaignStatus.story.tsx +++ b/web/src/enterprise/campaigns/detail/CampaignStatus.story.tsx @@ -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 => ( -
{story()}
+ <> + +
{story()}
+ )) add('Errored', () => ( diff --git a/web/src/globals.d.ts b/web/src/globals.d.ts index 7f0ee1cf6e8..91c3573c5fb 100644 --- a/web/src/globals.d.ts +++ b/web/src/globals.d.ts @@ -150,3 +150,8 @@ declare module 'worker-loader?*' { } export default WebpackWorker } + +declare module '*.scss' { + const cssModule: string + export default cssModule +} diff --git a/web/src/nav/UserNavItem.story.tsx b/web/src/nav/UserNavItem.story.tsx index 4e572be2d7d..de24fb732b3 100644 --- a/web/src/nav/UserNavItem.story.tsx +++ b/web/src/nav/UserNavItem.story.tsx @@ -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 => ( -
- {story()} -
+ <> + +
{story()}
+ )) const OpenUserNavItem: React.FunctionComponent = props => { diff --git a/web/src/nav/UserNavItem.test.tsx b/web/src/nav/UserNavItem.test.tsx index 0d1979ba4e2..3486b0b9bf4 100644 --- a/web/src/nav/UserNavItem.test.tsx +++ b/web/src/nav/UserNavItem.test.tsx @@ -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 }) diff --git a/yarn.lock b/yarn.lock index 8a73ef8a1ff..fa6098a99ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"