From 72469d8ef975c0bb317bdb242bc52e02c6b705f7 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 27 May 2020 08:47:18 +0200 Subject: [PATCH] Overhaul tooltip close button & badge styles (#10956) --- .storybook/main.js | 7 +- CHANGELOG.md | 1 + .../src/libs/bitbucket/code_intelligence.tsx | 2 +- browser/src/libs/bitbucket/style.scss | 12 + browser/src/libs/github/code_intelligence.ts | 6 +- browser/src/libs/github/style.scss | 4 +- browser/src/libs/gitlab/code_intelligence.ts | 7 +- browser/src/libs/gitlab/style.scss | 30 +- .../src/libs/phabricator/code_intelligence.ts | 3 +- browser/src/libs/phabricator/style.scss | 14 +- browser/src/shared.scss | 1 - package.json | 1 + shared/src/components/BadgeAttachment.scss | 45 --- .../src/components/BadgeAttachment.story.tsx | 105 ------- .../src/components/BadgeAttachment.test.tsx | 21 +- shared/src/components/BadgeAttachment.tsx | 83 +++-- .../BadgeAttachment.test.tsx.snap | 43 +++ shared/src/components/linkClickHandler.ts | 3 +- shared/src/globals.d.ts | 4 + shared/src/hover/HoverOverlay.scss | 139 ++++----- shared/src/hover/HoverOverlay.story.tsx | 289 ++++++++++++++++++ shared/src/hover/HoverOverlay.test.tsx | 69 ----- shared/src/hover/HoverOverlay.tsx | 90 +++--- .../__snapshots__/HoverOverlay.test.tsx.snap | 125 ++++---- shared/src/index.scss | 1 - web/src/components/shared.tsx | 2 +- yarn.lock | 79 ++++- 27 files changed, 685 insertions(+), 501 deletions(-) delete mode 100644 shared/src/components/BadgeAttachment.scss delete mode 100644 shared/src/components/BadgeAttachment.story.tsx create mode 100644 shared/src/components/__snapshots__/BadgeAttachment.test.tsx.snap create mode 100644 shared/src/hover/HoverOverlay.story.tsx diff --git a/.storybook/main.js b/.storybook/main.js index 9c4cefa0a55..5230f78259b 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -35,6 +35,8 @@ const config = { config.resolve.extensions.push('.ts', '.tsx') + 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({ @@ -52,9 +54,12 @@ const config = { }, ], // Make sure Storybook styles get handled by the Storybook config - exclude: /node_modules\/@storybook\//, + 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 + return config }, } diff --git a/CHANGELOG.md b/CHANGELOG.md index 3416c2767cf..32f5b1c32c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All notable changes to Sourcegraph are documented in this file. - Repository search within a version context will link to the revision in the version context. [#10860](https://github.com/sourcegraph/sourcegraph/pull/10860) - Background permissions syncing becomes the default method to sync permissions from code hosts. Please [read our documentation for things to keep in mind before upgrading](https://docs.sourcegraph.com/admin/repo/permissions#background-permissions-syncing). [#10972](https://github.com/sourcegraph/sourcegraph/pull/10972) +- The styling of the hover overlay was overhauled to never have badges or the close button overlap content while also always indicating whether the overlay is currently pinned. The styling on code hosts was also improved. [#10956](https://github.com/sourcegraph/sourcegraph/pull/10956) ### Fixed diff --git a/browser/src/libs/bitbucket/code_intelligence.tsx b/browser/src/libs/bitbucket/code_intelligence.tsx index 71bf970539e..2c72b146325 100644 --- a/browser/src/libs/bitbucket/code_intelligence.tsx +++ b/browser/src/libs/bitbucket/code_intelligence.tsx @@ -232,7 +232,7 @@ export const bitbucketServerCodeHost: CodeHost = { hoverOverlayClassProps: { className: 'aui-dialog', actionItemClassName: 'aui-button hover-action-item--bitbucket-server', - closeButtonClassName: 'aui-button', + iconButtonClassName: 'aui-button btn-icon--bitbucket-server', infoAlertClassName: notificationClassNames[NotificationType.Info], errorAlertClassName: notificationClassNames[NotificationType.Error], iconClassName, diff --git a/browser/src/libs/bitbucket/style.scss b/browser/src/libs/bitbucket/style.scss index bb4d8872a82..7ecf1190997 100644 --- a/browser/src/libs/bitbucket/style.scss +++ b/browser/src/libs/bitbucket/style.scss @@ -77,6 +77,18 @@ } } +.btn-icon--bitbucket-server { + padding: 0 !important; + background: transparent !important; + border: none !important; + height: 16px !important; + line-height: 16px !important; + + &:hover { + opacity: 0.7; + } +} + // Bitbucket's style is copied here because adding the aui-dropdown2-trigger class // to the command palette causes exceptions in Atlassian's JS. .command-list-popover-button--bitbucket-server { diff --git a/browser/src/libs/github/code_intelligence.ts b/browser/src/libs/github/code_intelligence.ts index 54aae0ab05d..9d05dac9aff 100644 --- a/browser/src/libs/github/code_intelligence.ts +++ b/browser/src/libs/github/code_intelligence.ts @@ -273,7 +273,7 @@ const nativeTooltipResolver: ViewResolver = { resolveView: element => ({ element }), } -const iconClassName = 'action-item__icon--github v-align-text-bottom' +const iconClassName = 'icon--github v-align-text-bottom' export const githubCodeHost: CodeHost = { type: 'github', @@ -324,7 +324,7 @@ export const githubCodeHost: CodeHost = { listItemClass: 'code-view-toolbar__item--github BtnGroup', actionItemClass: 'btn btn-sm tooltipped tooltipped-s BtnGroup-item action-item--github', actionItemPressedClass: 'selected', - actionItemIconClass: 'action-item__icon--github v-align-text-bottom', + actionItemIconClass: 'icon--github v-align-text-bottom', }, completionWidgetClassProps: { widgetClassName: 'suggester-container', @@ -337,7 +337,7 @@ export const githubCodeHost: CodeHost = { className: 'Box', actionItemClassName: 'btn btn-secondary', actionItemPressedClassName: 'active', - closeButtonClassName: 'btn', + iconButtonClassName: 'btn-octicon p-0', infoAlertClassName: 'flash flash-full', errorAlertClassName: 'flash flash-full flash-error', iconClassName, diff --git a/browser/src/libs/github/style.scss b/browser/src/libs/github/style.scss index f2b1055d5b2..e565e1094a4 100644 --- a/browser/src/libs/github/style.scss +++ b/browser/src/libs/github/style.scss @@ -6,12 +6,12 @@ .action-item--github { // Match GitHub's button height even if button only contains icon // (no text that would push the height) - // stylelint-disable-next-line declaration-property-unit-whitelist height: 28px; } -.action-item__icon--github { +.icon--github { height: 16px; + width: 16px; } .code-view-toolbar--github { diff --git a/browser/src/libs/gitlab/code_intelligence.ts b/browser/src/libs/gitlab/code_intelligence.ts index 8cfed5f9f91..96cee31c393 100644 --- a/browser/src/libs/gitlab/code_intelligence.ts +++ b/browser/src/libs/gitlab/code_intelligence.ts @@ -197,14 +197,15 @@ export const gitlabCodeHost = subTypeOf()({ }, codeViewToolbarClassProps: { className: 'code-view-toolbar--gitlab', - actionItemClass: 'btn btn-sm btn-secondary action-item--gitlab', + actionItemClass: 'btn btn-sm btn-secondary ml-2 action-item--gitlab', actionItemPressedClass: 'active', }, hoverOverlayClassProps: { - className: 'card', + className: 'card hover-overlay--gitlab', actionItemClassName: 'btn btn-secondary action-item--gitlab', actionItemPressedClassName: 'active', - closeButtonClassName: 'btn', + iconButtonClassName: 'btn btn-transparent p-0 btn-icon--gitlab', + iconClassName: 'square s16', infoAlertClassName: notificationClassNames[NotificationType.Info], errorAlertClassName: notificationClassNames[NotificationType.Error], }, diff --git a/browser/src/libs/gitlab/style.scss b/browser/src/libs/gitlab/style.scss index fcfcc7fa0dc..5003c33a30a 100644 --- a/browser/src/libs/gitlab/style.scss +++ b/browser/src/libs/gitlab/style.scss @@ -3,15 +3,33 @@ z-index: 1001 !important; } -.hover-overlay-mount__gitlab { - .hover-overlay { - code { - background: none; - color: unset; +.btn-icon--gitlab { + img { + // Gitlab applies this to svgs, + // need to apply it to imgs for consistency too + position: relative; + top: 2px; + vertical-align: baseline; + + // Replicate svg color hover effect + opacity: 0.6; + &:hover { + opacity: 1; } } +} - // stylelint-disable +.hover-overlay--gitlab { + border-color: #e5e5e5; + + // Override Gitlab's default styles + pre, + code { + padding: 0; + border: none; + background: none; + color: inherit; + } // highlight.js styles diff --git a/browser/src/libs/phabricator/code_intelligence.ts b/browser/src/libs/phabricator/code_intelligence.ts index 375fece47dd..4710f1ef7ef 100644 --- a/browser/src/libs/phabricator/code_intelligence.ts +++ b/browser/src/libs/phabricator/code_intelligence.ts @@ -195,7 +195,8 @@ export const phabricatorCodeHost: CodeHost = { hoverOverlayClassProps: { className: 'aphront-dialog-view hover-overlay--phabricator', actionItemClassName: 'button grey hover-overlay-action-item--phabricator', - closeButtonClassName: 'button grey hover-overlay__close-button--phabricator', + iconButtonClassName: 'button grey btn-icon--phabricator', + iconClassName: 'icon--phabricator', infoAlertClassName: 'phui-info-view phui-info-severity-notice', errorAlertClassName: 'phui-info-view phui-info-severity-error', }, diff --git a/browser/src/libs/phabricator/style.scss b/browser/src/libs/phabricator/style.scss index 47ead7be8e0..d8baf67004c 100644 --- a/browser/src/libs/phabricator/style.scss +++ b/browser/src/libs/phabricator/style.scss @@ -2,18 +2,21 @@ margin: 0; } -.hover-overlay__close-button--phabricator { +.btn-icon--phabricator { // Fight Phabricator selector specificity background: transparent !important; border: none !important; + padding: 0 !important; + + &:hover { + opacity: 0.7; + } } // Mimics Phabricator's font icon style -.action-item__icon--phabricator { +.icon--phabricator { height: 14px; width: 14px; - vertical-align: middle; - margin-top: -3px; } .action-item--phabricator.action-item--pressed { @@ -26,6 +29,9 @@ border-bottom: none !important; border-top: none !important; border-right: none !important; + .icon--phabricator { + vertical-align: middle; + } &:first-child { border-left: none !important; } diff --git a/browser/src/shared.scss b/browser/src/shared.scss index d1a0b2ca26b..984e40a4549 100644 --- a/browser/src/shared.scss +++ b/browser/src/shared.scss @@ -13,7 +13,6 @@ $body-bg-color-light: #ffffff; @import '../../shared/src/extensions/ExtensionStatus'; @import '../../shared/src/notifications/NotificationItem'; @import '../../shared/src/notifications/Notifications'; -@import '../../shared/src/components/BadgeAttachment'; $body-color-light: #2b3750; $body-color-dark: #f2f4f8; diff --git a/package.json b/package.json index c5bdcea918f..c1c7d024512 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ ] }, "devDependencies": { + "@atlassian/aui": "^7.10.1", "@babel/core": "^7.9.6", "@babel/plugin-proposal-class-properties": "^7.8.3", "@babel/plugin-proposal-decorators": "^7.8.3", diff --git a/shared/src/components/BadgeAttachment.scss b/shared/src/components/BadgeAttachment.scss deleted file mode 100644 index 1ca79d4233a..00000000000 --- a/shared/src/components/BadgeAttachment.scss +++ /dev/null @@ -1,45 +0,0 @@ -@import '../../src/global-styles/colors.scss'; - -.badge-decoration-attachment { - &__icon-svg, - &__contents { - width: 1.75rem; - height: 1.75rem; - background: transparent; - z-index: 1; - border: none; - border-radius: 100%; - background-color: rgba($body-bg-color-dark, 0.8); - } - &__icon-svg { - // TODO is there a better way to style this? padding vs margin (see __contents) - margin: 0.25rem; - } - &__contents { - padding: 0.25rem; - } - - width: 100%; - text-align: right; - color: inherit; - - &:hover { - color: #ffffff; - - .theme-light & { - color: $color-light-text-2; - } - } -} - -.theme-light { - .badge-decoration-attachment { - &__contents { - background-color: rgba($body-bg-color-light, 0.8); - } - - &__icon-svg { - background-color: rgba($body-bg-color-light, 0.8); - } - } -} diff --git a/shared/src/components/BadgeAttachment.story.tsx b/shared/src/components/BadgeAttachment.story.tsx deleted file mode 100644 index 8dd82013f15..00000000000 --- a/shared/src/components/BadgeAttachment.story.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import * as React from 'react' -import { storiesOf } from '@storybook/react' -import { BadgeAttachment } from './BadgeAttachment' -import badgeStyles from './BadgeAttachment.scss' -import webStyles from '../../../web/src/SourcegraphWebApp.scss' -import { BadgeAttachmentRenderOptions } from 'sourcegraph' - -import { radios } from '@storybook/addon-knobs' - -const label = 'Theme' -const options = { - Light: 'light', - Dark: 'dark', -} -const defaultValue = 'light' -const groupId = 'GROUP-ID1' - -const isLightTheme = () => radios(label, options, defaultValue, groupId) === 'light' - -const { add } = storiesOf('BadgeAttachment', module).addDecorator(story => ( - <> - - -
-
{story()}
-
- -)) - -add('info', () => ( - -)) - -add('warning', () => ( - -)) - -add('error', () => ( - -)) - -const oldFormatBadge: Omit = { - icon: makeInfoIcon('#ffffff'), - light: { icon: makeInfoIcon('#000000') }, -} - -add('old format icon', () => ( - -)) - -function makeIcon(svg: string): string { - return `data:image/svg+xml;base64,${btoa( - svg - .split('\n') - .map(r => r.trimStart()) - .join(' ') - )}` -} - -function makeInfoIcon(color: string): string { - return makeIcon(` - - - - `) -} diff --git a/shared/src/components/BadgeAttachment.test.tsx b/shared/src/components/BadgeAttachment.test.tsx index bf036ef0c8d..8b899d98fff 100644 --- a/shared/src/components/BadgeAttachment.test.tsx +++ b/shared/src/components/BadgeAttachment.test.tsx @@ -6,7 +6,7 @@ import { BadgeAttachmentRenderOptions } from 'sourcegraph' const base64icon = 'data:image/svg+xml;base64,IDxzdmcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyBzdHlsZT0id2lkdGg6MjRweDtoZWlnaHQ6MjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSIjZmZmZmZmIj4gPHBhdGggZD0iIE0xMSwgOUgxM1Y3SDExTTEyLCAyMEM3LjU5LCAyMCA0LCAxNi40MSA0LCAxMkM0LCA3LjU5IDcuNTksIDQgMTIsIDRDMTYuNDEsIDQgMjAsIDcuNTkgMjAsIDEyQzIwLCAxNi40MSAxNi40MSwgMjAgMTIsIDIwTTEyLCAyQTEwLCAxMCAwIDAsIDAgMiwgMTJBMTAsIDEwIDAgMCwgMCAxMiwgMjJBMTAsIDEwIDAgMCwgMCAyMiwgMTJBMTAsIDEwIDAgMCwgMCAxMiwgMk0xMSwgMTdIMTNWMTFIMTFWMTdaIiAvPiA8L3N2Zz4g' -export const oldFormatBadge: Omit = { +export const base64ImageBadge: Omit = { icon: base64icon, light: { icon: base64icon }, hoverMessage: @@ -14,8 +14,8 @@ export const oldFormatBadge: Omit = { linkURL: 'https://docs.sourcegraph.com/user/code_intelligence/basic_code_intelligence', } -export const newFormatBadge: BadgeAttachmentRenderOptions = { - ...oldFormatBadge, +export const badgeWithKind: BadgeAttachmentRenderOptions = { + ...base64ImageBadge, kind: 'info', } @@ -23,21 +23,14 @@ describe('BadgeAttachment', () => { afterAll(cleanup) it('renders an img element with a base64 icon', () => { - // note '"kind" is missing' const { container } = render( - + ) - const item = container.querySelector('.badge-decoration-attachment__contents') - expect(item).toBeTruthy() - // we used to render an image with base64 content as a source - expect(item?.nodeName.toLowerCase()).toBe('img') + expect(container).toMatchSnapshot() }) it('renders an svg element with a predefined icon', () => { - const { container } = render() - const item = container.querySelector('.badge-decoration-attachment__icon-svg') - expect(item).toBeTruthy() - // now we are using a proper svg for icons - expect(item?.nodeName.toLowerCase()).toBe('svg') + const { container } = render() + expect(container).toMatchSnapshot() }) }) diff --git a/shared/src/components/BadgeAttachment.tsx b/shared/src/components/BadgeAttachment.tsx index 2239b261381..b86782de000 100644 --- a/shared/src/components/BadgeAttachment.tsx +++ b/shared/src/components/BadgeAttachment.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import isAbsoluteUrl from 'is-absolute-url' +import { isExternalLink } from '../util/url' import InformationIcon from 'mdi-react/InfoCircleOutlineIcon' import WarningIcon from 'mdi-react/AlertCircleOutlineIcon' import ErrorIcon from 'mdi-react/AlertDecagramOutlineIcon' @@ -7,7 +7,8 @@ import { BadgeAttachmentRenderOptions } from 'sourcegraph' import { badgeAttachmentStyleForTheme } from '../api/client/services/decoration' import { LinkOrSpan } from './LinkOrSpan' import { isEncodedImage } from '../util/icon' -import { MdiReactIconComponentType } from 'mdi-react' +import { MdiReactIconComponentType, MdiReactIconProps } from 'mdi-react' +import classNames from 'classnames' const iconComponents: Record = { info: InformationIcon, @@ -15,46 +16,44 @@ const iconComponents: Record { - if ('kind' in badge) { - // means that we are using predefined icons - const Icon = iconComponents[badge.kind] - return - } - - const style = badgeAttachmentStyleForTheme(badge, isLightTheme) - - if (!style.icon || !isEncodedImage(style.icon)) { - return null - } - - return ( - - ) -} - export const BadgeAttachment: React.FunctionComponent<{ attachment: BadgeAttachmentRenderOptions isLightTheme: boolean -}> = ({ attachment, isLightTheme }) => ( - - {renderIcon(attachment, isLightTheme)} - -) + className?: string + iconClassName?: string + iconButtonClassName?: string +}> = ({ attachment, isLightTheme, className, iconButtonClassName, iconClassName }) => { + const style = badgeAttachmentStyleForTheme(attachment, isLightTheme) + const PredefinedIcon: React.ComponentType | undefined = + attachment.kind && iconComponents[attachment.kind] + + return ( + + {PredefinedIcon ? ( + + ) : ( + style.icon && + isEncodedImage(style.icon) && ( + + ) + )} + + ) +} diff --git a/shared/src/components/__snapshots__/BadgeAttachment.test.tsx.snap b/shared/src/components/__snapshots__/BadgeAttachment.test.tsx.snap new file mode 100644 index 00000000000..726583c7c16 --- /dev/null +++ b/shared/src/components/__snapshots__/BadgeAttachment.test.tsx.snap @@ -0,0 +1,43 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BadgeAttachment renders an img element with a base64 icon 1`] = ` + +`; + +exports[`BadgeAttachment renders an svg element with a predefined icon 1`] = ` + +`; diff --git a/shared/src/components/linkClickHandler.ts b/shared/src/components/linkClickHandler.ts index 671ec396ab4..63fe190d13f 100644 --- a/shared/src/components/linkClickHandler.ts +++ b/shared/src/components/linkClickHandler.ts @@ -1,6 +1,7 @@ import * as React from 'react' import { anyOf, isInstanceOf } from '../util/types' import * as H from 'history' +import { isExternalLink } from '../util/url' /** * Returns a click handler that will make sure clicks on in-app links are handled on the client @@ -23,7 +24,7 @@ export const createLinkClickHandler = (history: H.History): React.MouseEventHand const href = typeof anchor.href === 'string' ? anchor.href : anchor.href.baseVal // Check if URL is outside the app - if (!href.startsWith(window.location.origin)) { + if (isExternalLink(href)) { return } diff --git a/shared/src/globals.d.ts b/shared/src/globals.d.ts index ce51b619566..41a8a4d7574 100644 --- a/shared/src/globals.d.ts +++ b/shared/src/globals.d.ts @@ -14,6 +14,10 @@ declare module '*.scss' { const cssModule: string export default cssModule } +declare module '*.css' { + const cssModule: string + export default cssModule +} /** * Set by shared/dev/jest-environment.js diff --git a/shared/src/hover/HoverOverlay.scss b/shared/src/hover/HoverOverlay.scss index f4edcf73df2..47a7819c289 100644 --- a/shared/src/hover/HoverOverlay.scss +++ b/shared/src/hover/HoverOverlay.scss @@ -4,100 +4,97 @@ position: absolute; min-width: 6rem; max-width: 32rem; - display: flex; - flex-direction: column; - align-items: stretch; z-index: 100; + transition: opacity 100ms ease-in-out; + // Make sure content doesn't leak behind border-radius + overflow: hidden; - $animation-duration: 100ms; - transition: opacity $animation-duration ease-in-out; + &__close-button, + &__badge { + // We want text to wrap around the close button and badges. + float: right; + margin-left: 0.25rem; + } &__close-button { - position: absolute; - top: 1px; - right: 1px; - padding: 0.25rem; - border-radius: 100%; - background: transparent; - z-index: 1; - border: none; - background-color: rgba($body-bg-color-dark, 0.8); - opacity: 0; - transition: opacity $animation-duration ease-in-out; - } - &:hover &__close-button { - opacity: 1; - } + // The close button should stay in place when scrolling a large hover content. + position: sticky; + top: 0; - &__row { - position: relative; - display: block; - margin: 0; - &:not(:first-child) { - border-top: 1px solid var(--border-color); + // Overlay alert background + z-index: 1; + + // When loading, we want the loader to be centered in the hover overlay, + // not centered within the space left of the close button. + &--loading { + position: absolute; + top: 0.5rem; + right: 0.5rem; } } &__contents { - flex: 1 1 auto; + padding: 0.5rem; + // Make very large MarkupContents scroll. overflow-y: auto; + max-height: 10rem; + border-bottom: 1px solid var(--border-color); } - &__alert { - border-top: 1px solid var(--border-color); - padding: 0.5rem; + // We use
s as a divider between multiple contents. + // This has the nice property of having floating buttons that text wraps around. + // stylelint-disable-next-line selector-max-compound-selectors + hr { + margin: 0.5rem -0.5rem; + overflow: visible; + background: var(--border-color); + border: none; + // The
acts like a border, which should always be exactly 1px + // stylelint-disable-next-line declaration-property-unit-whitelist + height: 1px; } - &__alert-close { - float: right; - } - - // __content elements are a subset of the __row elements - // (the ones that contain markdown or code, but not errors or the actions) &__content { + display: contents; max-height: 15rem; - padding: 0.5rem; overflow-x: auto; word-wrap: normal; // Descendant selectors are needed here to style rendered markdown // stylelint-disable selector-max-compound-selectors - //
s must looks the same as using the using multiple deprecated MarkedStrings - hr { - margin: 0.5rem -0.5rem; - background: var(--border-color); - border: none; - // The
acts like a border, which should always be exactly 1px - // stylelint-disable-next-line declaration-property-unit-whitelist - height: 1px; - } p, pre { margin-bottom: 0.5rem; - overflow: auto; &:last-child { margin-bottom: 0; } } + pre, code { - white-space: pre; + padding: 0; + // We want code to wrap, not scroll (but whitespace needs to be preserved). + white-space: pre-wrap; + // Any other value would create a new block formatting context, + // which would prevent wrapping around the floating buttons. + overflow: visible; } // stylelint-enable selector-max-compound-selectors } - &__badge { - position: absolute; - top: 1px; - right: 1px; - text-align: right; + &__alert { + padding: 0.5rem; + margin: 0; + border-radius: 0; + border: none; + border-bottom: 1px solid var(--border-color); } - &:hover &__badge--offset { - right: 1.5rem; + + &__alert-close { + float: right; } &__actions { - flex: 0 0 auto; display: flex; } @@ -106,37 +103,23 @@ } &__action { + flex: 1 1 auto; text-align: center; border: none; - } - - &__action, - &__actions-placeholder { - flex: 1 1 auto; border-radius: 0; } &__loader-row { - text-align: center; + display: flex; + align-items: center; + justify-content: center; } &__loader-row, - &__hover-error, - &__content-error, - &__alert-below { + &__hover-error { padding: 0.5rem; - } - - &__alert-below { - margin: 0; - overflow-y: auto; - } -} - -.theme-light { - .hover-overlay { - &__close-button { - background-color: rgba($body-bg-color-light, 0.8); - } + margin: -0.5rem; + border: none; + border-radius: 0; } } diff --git a/shared/src/hover/HoverOverlay.story.tsx b/shared/src/hover/HoverOverlay.story.tsx new file mode 100644 index 00000000000..c388f5047a9 --- /dev/null +++ b/shared/src/hover/HoverOverlay.story.tsx @@ -0,0 +1,289 @@ +import { storiesOf } from '@storybook/react' +import React from 'react' +import { action } from '@storybook/addon-actions' +import { boolean } from '@storybook/addon-knobs' +import { createMemoryHistory } from 'history' +import { HoverOverlay, HoverOverlayClassProps } from './HoverOverlay' +import { MarkupKind } from '@sourcegraph/extension-api-classes' +import { NOOP_TELEMETRY_SERVICE } from '../telemetry/telemetryService' +import { of } from 'rxjs' +import { registerHighlightContributions } from '../highlight/contributions' +import { PlatformContext } from '../platform/context' + +import webStyles from '../../../web/src/SourcegraphWebApp.scss' +import bitbucketStyles from '@atlassian/aui/dist/aui/css/aui.css' +import browserExtensionStyles from '../../../browser/src/app.scss' +import { BadgeAttachmentRenderOptions, MarkupContent, Badged } from 'sourcegraph' + +registerHighlightContributions() + +const { add } = storiesOf('HoverOverlay', module) + +const history = createMemoryHistory() +const NOOP_EXTENSIONS_CONTROLLER = { executeCommand: () => Promise.resolve() } +const NOOP_PLATFORM_CONTEXT: Pick = { + forceUpdateTooltip: () => undefined, + settings: of({ final: {}, subjects: [] }), +} + +const commonProps = () => ({ + showCloseButton: boolean('showCloseButton', true), + location: history.location, + telemetryService: NOOP_TELEMETRY_SERVICE, + extensionsController: NOOP_EXTENSIONS_CONTROLLER, + platformContext: NOOP_PLATFORM_CONTEXT, + isLightTheme: true, + overlayPosition: { top: 16, left: 16 }, + onAlertDismissed: action('onAlertDismissed'), + onCloseButtonClick: action('onCloseButtonClick'), +}) +const webHoverOverlayClassProps: HoverOverlayClassProps = { + className: 'card', + iconClassName: 'icon-inline', + iconButtonClassName: 'btn btn-icon', + actionItemClassName: 'btn btn-secondary', + infoAlertClassName: 'alert alert-info', + errorAlertClassName: 'alert alert-danger', +} +const bitbucketClassProps: HoverOverlayClassProps = { + className: 'aui-dialog', + actionItemClassName: 'aui-button hover-action-item--bitbucket-server', + iconButtonClassName: 'aui-button btn-icon--bitbucket-server', + infoAlertClassName: 'aui-message aui-message-info', + errorAlertClassName: 'aui-message aui-message-error', + iconClassName: 'aui-icon', +} + +const FIXTURE_BADGE: BadgeAttachmentRenderOptions = { + kind: 'info', + hoverMessage: + 'Search-based results - click to see how these results are calculated and how to get precise intelligence with LSIF.', + linkURL: 'https://docs.sourcegraph.com/user/code_intelligence/basic_code_intelligence', +} + +const LEGACY_FIXTURE_BADGE = { + icon: + 'data:image/svg+xml;base64,IDxzdmcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyBzdHlsZT0id2lkdGg6MjRweDtoZWlnaHQ6MjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSIjZmZmZmZmIj4gPHBhdGggZD0iIE0xMSwgOUgxM1Y3SDExTTEyLCAyMEM3LjU5LCAyMCA0LCAxNi40MSA0LCAxMkM0LCA3LjU5IDcuNTksIDQgMTIsIDRDMTYuNDEsIDQgMjAsIDcuNTkgMjAsIDEyQzIwLCAxNi40MSAxNi40MSwgMjAgMTIsIDIwTTEyLCAyQTEwLCAxMCAwIDAsIDAgMiwgMTJBMTAsIDEwIDAgMCwgMCAxMiwgMjJBMTAsIDEwIDAgMCwgMCAyMiwgMTJBMTAsIDEwIDAgMCwgMCAxMiwgMk0xMSwgMTdIMTNWMTFIMTFWMTdaIiAvPiA8L3N2Zz4g', + light: { + icon: + 'data:image/svg+xml;base64,IDxzdmcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyBzdHlsZT0id2lkdGg6MjRweDtoZWlnaHQ6MjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSIjMDAwMDAwIj4gPHBhdGggZD0iIE0xMSwgOUgxM1Y3SDExTTEyLCAyMEM3LjU5LCAyMCA0LCAxNi40MSA0LCAxMkM0LCA3LjU5IDcuNTksIDQgMTIsIDRDMTYuNDEsIDQgMjAsIDcuNTkgMjAsIDEyQzIwLCAxNi40MSAxNi40MSwgMjAgMTIsIDIwTTEyLCAyQTEwLCAxMCAwIDAsIDAgMiwgMTJBMTAsIDEwIDAgMCwgMCAxMiwgMjJBMTAsIDEwIDAgMCwgMCAyMiwgMTJBMTAsIDEwIDAgMCwgMCAxMiwgMk0xMSwgMTdIMTNWMTFIMTFWMTdaIiAvPiA8L3N2Zz4g', + }, + hoverMessage: + 'Search-based results - click to see how these results are calculated and how to get precise intelligence with LSIF.', + linkURL: 'https://docs.sourcegraph.com/user/code_intelligence/basic_code_intelligence', +} as BadgeAttachmentRenderOptions + +const FIXTURE_CONTENT: Badged = { + value: + '```typescript\nexport interface TestInterface\n```\n\n' + + '---\n\nVeniam voluptate quis magna mollit aliqua enim id ea fugiat. Aliqua anim eiusmod nisi excepteur.\n', + kind: MarkupKind.Markdown, + badge: FIXTURE_BADGE, +} + +const FIXTURE_CONTENT_LONG_CODE = { + ...FIXTURE_CONTENT, + value: + '```typescript\nexport interface LongTestInterface\n```\n\n' + + '---\n\nNisi id deserunt culpa dolore aute pariatur ut amet veniam. Proident id Lorem reprehenderit veniam sunt velit.\n', +} + +const FIXTURE_CONTENT_LONG_TEXT_ONLY = { + ...FIXTURE_CONTENT, + value: + 'Mollit ea esse magna incididunt aliquip mollit non reprehenderit veniam anim. Veniam in dolor elit sint aliqua non cillum. Est sit pariatur ut cupidatat magna dolore. Sint et culpa voluptate ad sit eu ea dolor. Dolore Lorem cillum esse pariatur elit dolore dolor quis fugiat labore non. Elit nostrud minim aliqua adipisicing laborum ad sunt velit amet. In voluptate est voluptate labore consectetur proident. Nostrud exercitation ut officia enim minim tempor qui adipisicing sunt et occaecat anim irure. Culpa irure reprehenderit reprehenderit dolore sint aliquip non ex excepteur ipsum dolor. Et qui anim officia magna enim laboris enim exercitation pariatur. Cillum consequat elit dolore tempor magna exercitation ad laborum consequat aute consequat.', +} + +const FIXTURE_ACTIONS = [ + { + action: { + id: 'goToDefinition.preloaded', + title: 'Go to definition', + command: 'open', + commandArguments: ['/github.com/sourcegraph/codeintellify/-/blob/src/hoverifier.ts?subtree=true#L57:1'], + }, + }, + { + action: { + id: 'findReferences', + title: 'Find references', + command: 'open', + commandArguments: [ + '/github.com/sourcegraph/codeintellify/-/blob/src/hoverifier.ts?subtree=true#L57:18&tab=references', + ], + }, + }, +] + +add('Loading', () => ( + <> + +
+ +
+ +)) + +add('Error', () => ( + <> + +
+ +
+ +)) + +add('Common content', () => ( + <> + +
+ +
+ +)) + +add('Legacy badge', () => ( + <> + +
+ +
+ +)) + +add('Only actions', () => ( + <> + +
+ +
+ +)) + +add('Long code', () => ( + <> + +
+ +
+ +)) + +add('Long text only', () => ( + <> + +
+ +
+ +)) + +add('Multiple MarkupContents', () => ( + <> + +
+ +
+ +)) + +add('With alert', () => ( + <> + +
+ + This is a test alert. Enim esse quis commodo ex. Pariatur tempor laborum officia + irure est do est laborum nostrud cillum. Cupidatat id consectetur et eiusmod Lorem + proident cupidatat ullamco dolor nostrud. Cupidatat sit do dolor aliqua labore ad + laboris cillum deserunt dolor. Sunt labore veniam Lorem reprehenderit quis occaecat + sint do mollit aliquip. Consectetur mollit mollit magna eiusmod duis ex. Sint nisi + labore labore nulla laboris. + + ), + }, + ], + }} + actionsOrError={FIXTURE_ACTIONS} + onAlertDismissed={action('onAlertDismissed')} + /> +
+ +)) + +add('Bitbucket styles', () => ( + <> + + + + +)) diff --git a/shared/src/hover/HoverOverlay.test.tsx b/shared/src/hover/HoverOverlay.test.tsx index cec937438a5..23862c2ebeb 100644 --- a/shared/src/hover/HoverOverlay.test.tsx +++ b/shared/src/hover/HoverOverlay.test.tsx @@ -1,12 +1,8 @@ -import { HoverAttachment } from '@sourcegraph/codeintellify/lib/types' import { MarkupKind } from '@sourcegraph/extension-api-classes' -import { registerLanguage } from 'highlight.js/lib/core' import * as H from 'history' -import { castArray } from 'lodash' import React from 'react' import renderer from 'react-test-renderer' import { createRenderer } from 'react-test-renderer/shallow' -import { HoverMerged } from '../api/client/types/hover' import { NOOP_TELEMETRY_SERVICE } from '../telemetry/telemetryService' import { HoverOverlay, HoverOverlayProps } from './HoverOverlay' import { NEVER } from 'rxjs' @@ -214,69 +210,4 @@ describe('HoverOverlay', () => { ) ).toMatchSnapshot() }) - - describe('hover content rendering', () => { - const renderMarkdownHover = (hover: HoverAttachment & HoverMerged): string | null => { - // TODO this test depends on internals of the HoverOverlay. - // If we want to test this rendering, it would be better to - // extract the markdown rendering into another small component - // and unit test that in isolation - const r = renderShallow() - const contents = castArray(r.props.children).find(element => - element?.props?.className?.includes('hover-overlay__contents') - ) - if (!contents) { - return null - } - - const grabContent = (c: any) => { - if (c.props && c.props.className && c.props.className.includes('hover-overlay__content')) { - if (typeof c.props.children === 'string') { - return c.props.children - } - return c.props.dangerouslySetInnerHTML.__html - } - return '' - } - - return castArray(contents.props.children) - .map(c => { - // Grab un-badged content - const content = grabContent(c) - if (content !== '') { - return content - } - // Grab badged content in the grand-child level - if (c.props && c.props.className && c.props.className.includes('e2e-tooltip-badged-content')) { - return castArray(c.props.children).map(grabContent).join('').trim() - } - return '' - }) - .join('') - .trim() - } - - const renderPlainTextHover = (hover: HoverAttachment & HoverMerged): React.ReactChild[] => - renderer - .create() - .root.find(c => c.props && c.props.className && c.props.className.includes('hover-overlay__content')) - .props.children.map((c: renderer.ReactTestInstance) => c.props.children) - - test('MarkupKind.Markdown', () => { - expect(renderMarkdownHover({ contents: [{ kind: MarkupKind.Markdown, value: '*v*' }] })).toEqual( - '

v

' - ) - }) - - test('MarkupKind.PlainText', () => { - expect(renderPlainTextHover({ contents: [{ kind: MarkupKind.PlainText, value: 'v<' }] })).toEqual(['v<']) - }) - - test('code', () => { - registerLanguage('testlang', x => ({})) - expect( - renderMarkdownHover({ contents: [{ kind: MarkupKind.Markdown, value: '```testlang\n<>\n```' }] }) - ).toEqual('
<>
') - }) - }) }) diff --git a/shared/src/hover/HoverOverlay.tsx b/shared/src/hover/HoverOverlay.tsx index 925eaba2db1..d64d43dc00f 100644 --- a/shared/src/hover/HoverOverlay.tsx +++ b/shared/src/hover/HoverOverlay.tsx @@ -29,7 +29,7 @@ export type HoverData = HoverMerged & HoverAlerts export interface HoverOverlayClassProps { /** An optional class name to apply to the outermost element of the HoverOverlay */ className?: string - closeButtonClassName?: string + iconButtonClassName?: string iconClassName?: string @@ -171,76 +171,70 @@ export class HoverOverlay extends React.PureComponent - {showCloseButton && ( - - )} -
+
+ {showCloseButton && ( + + )} {hoverOrError === LOADING ? ( -
- +
+
) : isErrorLike(hoverOrError) ? ( -
+
{upperFirst(hoverOrError.message)}
+ ) : hoverOrError === null ? ( + // Show some content to give the close button space + // and communicate to the user we couldn't find a hover. + No hover information available. ) : ( hoverOrError?.contents.map((content, i) => { if (content.kind === 'markdown') { try { - // Offset first badge when the close button is shown to avoid conflict. - const offsetBadge = showCloseButton && i === 0 return ( -
- {'badge' in content && content.badge && this.state.showBadges && ( -
- -
+ + {i !== 0 &&
} + + {content.badge && this.state.showBadges && ( + )} -
-
+
) } catch (err) { return ( -
+
{upperFirst(asError(err).message)}
) } } return ( -
+ {content.value} -
+ ) }) )} @@ -249,11 +243,7 @@ export class HoverOverlay
extends React.PureComponent {hoverOrError.alerts.map(({ content, type }) => (
@@ -275,7 +265,7 @@ export class HoverOverlay extends React.PureComponent 0 && ( -
+
{actionsOrError.map((action, i) => (
+ > + + No hover information available. + +
`; @@ -34,7 +38,7 @@ exports[`HoverOverlay actions and hover error 1`] = ` className="hover-overlay__contents" >
M2
@@ -58,10 +62,10 @@ exports[`HoverOverlay actions and hover loading 1`] = ` className="hover-overlay__contents" >
@@ -83,10 +87,8 @@ exports[`HoverOverlay actions and hover present 1`] = `
-
-
+ -
+
-
-
+ -
+
`; @@ -233,7 +233,7 @@ exports[`HoverOverlay actions present 1`] = ` className="hover-overlay__contents" />
- +
-
-
+ -
+
M
@@ -504,13 +500,13 @@ exports[`HoverOverlay hover error, actions present 1`] = ` className="hover-overlay__contents" >
M
@@ -598,19 +594,15 @@ exports[`HoverOverlay hover present 1`] = `
-
-
v

+ v

", - } } - /> -
+ } + />
`; @@ -630,10 +622,8 @@ exports[`HoverOverlay hover present, actions loading 1`] = `
-
-
+ -
+
`; @@ -662,32 +652,25 @@ exports[`HoverOverlay multiple hovers present 1`] = `
-
-
v

+ v

", - } } - /> -
-
-
v2

+ } + /> +
+ v2

", - } } - /> -
+ } + />
`; diff --git a/shared/src/index.scss b/shared/src/index.scss index 40dccf74245..29ebdc71feb 100644 --- a/shared/src/index.scss +++ b/shared/src/index.scss @@ -11,7 +11,6 @@ @import './components/ResultContainer'; @import './components/Toggle'; @import './components/Tabs'; -@import './components/BadgeAttachment'; @import './extensions/ExtensionStatus'; @import './hover/HoverOverlay'; @import './notifications/NotificationItem'; diff --git a/web/src/components/shared.tsx b/web/src/components/shared.tsx index eda38bafa24..bd77f99b7a6 100644 --- a/web/src/components/shared.tsx +++ b/web/src/components/shared.tsx @@ -15,7 +15,7 @@ export const WebHoverOverlay: React.FunctionComponent> {...props} className="card" iconClassName="icon-inline" - closeButtonClassName="btn btn-icon" + iconButtonClassName="btn btn-icon" actionItemClassName="btn btn-secondary" infoAlertClassName="alert alert-info" errorAlertClassName="alert alert-danger" diff --git a/yarn.lock b/yarn.lock index ccb0851373c..7764386c5ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11,6 +11,23 @@ call-me-maybe "^1.0.1" js-yaml "^3.13.1" +"@atlassian/aui@^7.10.1": + version "7.10.1" + resolved "https://registry.npmjs.org/@atlassian/aui/-/aui-7.10.1.tgz#21fae26d599ffc7d4e35d4f603e5747a5ac913ce" + integrity sha512-KNCaQA40mdH22/yzk0JQ78j7Z0OCx5t0q1S/JJay6o+gDmbpizAaZAxzDdoptzvMsV2BjddehzJPYg5wcmIgJQ== + dependencies: + backbone "1.1.2" + clipboard-js "0.2.0" + css.escape "1.5.0" + fancy-file-input "2.0.3" + object-assign "4.0.1" + skatejs "0.13.17" + skatejs-template-html "0.0.0" + tether atlassian/tether#amd-with-global + trim-extra-html-whitespace "1.3.0" + underscore "1.6.0" + webcomponents.js "0.7.20" + "@babel/code-frame@7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" @@ -2097,7 +2114,8 @@ integrity sha512-KWxkyphmlwam8kfYPSmoitKQRMGQCsr1ZRmNZgijT7ABKaVyk/+I5ezt2J213tM04Hi0vyg4L7iH1VCkNvm2Jw== "@sourcegraph/extension-api-types@link:packages/@sourcegraph/extension-api-types": - version "2.1.0" + version "0.0.0" + uid "" "@sourcegraph/prettierrc@^3.0.3": version "3.0.3" @@ -5182,6 +5200,13 @@ bach@^1.0.0: async-settle "^1.0.0" now-and-later "^2.0.0" +backbone@1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/backbone/-/backbone-1.1.2.tgz#c2c04c66bf87268fb82c177acebeff7d37ba6f2d" + integrity sha1-wsBMZr+HJo+4LBd6zr7/fTe6by0= + dependencies: + underscore ">=1.5.0" + backo2@1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" @@ -6314,6 +6339,11 @@ cli-width@^2.0.0: resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +clipboard-js@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/clipboard-js/-/clipboard-js-0.2.0.tgz#ba1a6092ccbce43ccc9817407737692e432f409b" + integrity sha1-uhpgksy85DzMmBdAdzdpLkMvQJs= + clipboard@^2.0.0: version "2.0.4" resolved "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d" @@ -7139,6 +7169,11 @@ css-what@2.1, css-what@^2.1.2: resolved "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" integrity sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ== +css.escape@1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.0.tgz#95984d7887ce4ca90684e813966f42d1ef87ecea" + integrity sha1-lZhNeIfOTKkGhOgTlm9C0e+H7Oo= + cssesc@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" @@ -9054,6 +9089,11 @@ fake-tag@^1.0.0: resolved "https://registry.npmjs.org/fake-tag/-/fake-tag-1.0.1.tgz#1d59da482240a02bd83500ca98976530ed154b0d" integrity sha512-qmewZoBpa71mM+y6oxXYW/d1xOYQmeIvnEXAt1oCmdP0sqcogWYLepR87QL1jQVLSVMVYDq2cjY6ec/Wu8/4pg== +fancy-file-input@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/fancy-file-input/-/fancy-file-input-2.0.3.tgz#4b2d6a6313b8a62127e52a548a8d6995f46446ce" + integrity sha512-9StOIYtZWoGGWG/OAuI3PmFDNI+TRiTb7nYyglQL2OZPbmp2lw0/qwIEoRD3s5YtNyf078oDTY4xIIDQzk/pdA== + fancy-log@^1.3.2, fancy-log@^1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" @@ -15068,6 +15108,11 @@ oauth-sign@~0.9.0: resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +object-assign@4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz#99504456c3598b5cad4fc59c26e8a9bb107fe0bd" + integrity sha1-mVBEVsNZi1ytT8WcJuipuxB/4L0= + object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -18863,6 +18908,16 @@ sisteransi@^1.0.0: resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c" integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ== +skatejs-template-html@0.0.0: + version "0.0.0" + resolved "https://registry.npmjs.org/skatejs-template-html/-/skatejs-template-html-0.0.0.tgz#e990c1a7d4b58b7305ffcc3338939bf402023df7" + integrity sha1-6ZDBp9S1i3MF/8wzOJOb9AICPfc= + +skatejs@0.13.17: + version "0.13.17" + resolved "https://registry.npmjs.org/skatejs/-/skatejs-0.13.17.tgz#7a21fbb3434da45e52b47b61647168ee9e778071" + integrity sha1-eiH7s0NNpF5StHthZHFo7p53gHE= + slash@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -19128,7 +19183,8 @@ source-map@^0.7.3: integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== "sourcegraph@link:packages/sourcegraph-extension-api": - version "24.4.0" + version "0.0.0" + uid "" space-separated-tokens@^1.0.0: version "1.1.2" @@ -20127,6 +20183,10 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +tether@atlassian/tether#amd-with-global: + version "0.6.5" + resolved "https://codeload.github.com/atlassian/tether/tar.gz/bf85430889b5231fbe5b383416cce6281225bf06" + text-table@0.2.0, text-table@^0.2.0, text-table@~0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -20415,6 +20475,11 @@ treeify@^1.1.0: resolved "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== +trim-extra-html-whitespace@1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/trim-extra-html-whitespace/-/trim-extra-html-whitespace-1.3.0.tgz#b47efb0d1a5f2a56a85cc45cea525651e93404cf" + integrity sha1-tH77DRpfKlaoXMRc6lJWUek0BM8= + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -20627,6 +20692,11 @@ unc-path-regex@^0.1.2: resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= +underscore@1.6.0, underscore@>=1.5.0: + version "1.6.0" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" + integrity sha1-izixDKze9jM3uLJOT/htRa6lKag= + undertaker-registry@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" @@ -21322,6 +21392,11 @@ web-namespaces@^1.1.2: resolved "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.2.tgz#c8dc267ab639505276bae19e129dbd6ae72b22b4" integrity sha512-II+n2ms4mPxK+RnIxRPOw3zwF2jRscdJIUE9BfkKHm4FYEg9+biIoTMnaZF5MpemE3T+VhMLrhbyD4ilkPCSbg== +webcomponents.js@0.7.20: + version "0.7.20" + resolved "https://registry.npmjs.org/webcomponents.js/-/webcomponents.js-0.7.20.tgz#36727218fbf59433ae10139e59eaf3cfdddc11c5" + integrity sha1-NnJyGPv1lDOuEBOeWerzz93cEcU= + webext-additional-permissions@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/webext-additional-permissions/-/webext-additional-permissions-1.0.0.tgz#fe8977f702bcb7a3aa23d09efc87f308135b4fca"