Skip to content
This repository has been archived by the owner on Sep 30, 2024. It is now read-only.

Add 'Take Sourcegraph to your [code host]' popover #14256

Merged
merged 43 commits into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
186ce4f
convert GoToCodeHostAction to function component
tjkandala Sep 28, 2020
e657bbd
implement modal visuals
tjkandala Sep 28, 2020
6029d66
implemented popup links and events
tjkandala Sep 28, 2020
6787e0d
remove browser extension toast
tjkandala Sep 28, 2020
6cb8078
fix copy typo
tjkandala Sep 28, 2020
2cbf829
implement focus trapping for modal
tjkandala Sep 28, 2020
ea7ad98
rename LinkOrButton to ButtonLink
tjkandala Sep 30, 2020
6466de6
implement hover count tracking for popover
tjkandala Sep 30, 2020
05bfa7a
create useModality hook
tjkandala Sep 30, 2020
4910dbd
create popover component
tjkandala Sep 30, 2020
4f22429
only hijack link when hover threshold reached
tjkandala Sep 30, 2020
0349509
make hover tracking optional (by page) for now
tjkandala Sep 30, 2020
38a7125
update story
tjkandala Sep 30, 2020
06e0a31
add integration tests for browser extension discoverability changes
tjkandala Oct 1, 2020
a6a7927
move inline sizes to css
tjkandala Oct 1, 2020
961a132
remove size from sourcegraph icon
tjkandala Oct 2, 2020
5e5aade
use storybook actions
tjkandala Oct 2, 2020
9621a9c
move hover threshold logic up to RepoContainer
tjkandala Oct 2, 2020
89bab12
add visibility assertions to integration test
tjkandala Oct 2, 2020
abf71ac
update imports
tjkandala Oct 2, 2020
e0d8821
remove click event hijacking in test
tjkandala Oct 2, 2020
3213eee
add dialog polyfill and implement dialog component
tjkandala Oct 3, 2020
bea653c
replace dialogPolyfill with reach dialog
tjkandala Oct 4, 2020
49e6750
remove ModalContainer from webapp
tjkandala Oct 4, 2020
c0af4d3
replace custom popover with reactstrap popover
tjkandala Oct 4, 2020
f81e738
update story
tjkandala Oct 4, 2020
fd13729
use react focus lock
tjkandala Oct 4, 2020
56f286a
setTimeout in popover story
tjkandala Oct 4, 2020
c9f4887
import bootstrap popover
tjkandala Oct 5, 2020
379a781
explain popover story timeout
tjkandala Oct 5, 2020
7fe9e84
improve popover arrow for dark theme
tjkandala Oct 6, 2020
8b08524
simplify data fetching observable
tjkandala Oct 6, 2020
0a8ab66
open popover links in new tab when original click was aux
tjkandala Oct 6, 2020
263173d
always open links in new tabs, rename popover
tjkandala Oct 6, 2020
4e14860
use bootstrap variables for popover
tjkandala Oct 6, 2020
a35e728
fix old popovers with bootstrap var
tjkandala Oct 6, 2020
d0b2f0a
improve code comments
tjkandala Oct 6, 2020
f8fa073
close popover on hard rejection
tjkandala Oct 6, 2020
117d424
change copy by codehost, flesh out story
tjkandala Oct 7, 2020
55349e9
Add 'Install the browser extension' alert + animation (#14399)
tjkandala Oct 8, 2020
a2412e4
fix imports from branded
tjkandala Oct 8, 2020
c024504
remove dialog polyfill
tjkandala Oct 8, 2020
288c720
Consolidate popover stories
tjkandala Oct 8, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/branded/src/global-styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ $code-bg: var(--body-bg);
@import './card';
@import './dropdown';
@import './modal';
@import './popover';
@import './nav';
@import './type';
@import './list-group';
Expand Down
6 changes: 6 additions & 0 deletions client/branded/src/global-styles/popover.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
$popover-font-size: $font-size-base;
$popover-arrow-outer-color: var(--border-color);
$popover-arrow-color: var(--body-bg);
$popover-max-width: auto;

@import 'bootstrap/scss/popover';
6 changes: 3 additions & 3 deletions client/shared/src/actions/ActionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { catchError, map, mapTo, mergeMap, startWith, tap } from 'rxjs/operators
import { ExecuteCommandParams } from '../api/client/services/command'
import { ActionContribution, Evaluated } from '../api/protocol'
import { urlForOpenPanel } from '../commands/commands'
import { LinkOrButton } from '../components/LinkOrButton'
import { ButtonLink } from '../components/LinkOrButton'
import { ExtensionsControllerProps } from '../extensions/controller'
import { PlatformContextProps } from '../platform/context'
import { TelemetryProps } from '../telemetry/telemetryService'
Expand Down Expand Up @@ -206,7 +206,7 @@ export class ActionItem extends React.PureComponent<ActionItemProps, State> {
: {}

return (
<LinkOrButton
<ButtonLink
data-tooltip={
this.props.showInlineError && isErrorLike(this.state.actionOrError)
? `Error: ${this.state.actionOrError.message}`
Expand Down Expand Up @@ -237,7 +237,7 @@ export class ActionItem extends React.PureComponent<ActionItemProps, State> {
<LoadingSpinner className={this.props.iconClassName} />
</div>
)}
</LinkOrButton>
</ButtonLink>
)
}

Expand Down
6 changes: 3 additions & 3 deletions client/shared/src/components/LinkOrButton.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from 'react'
import renderer from 'react-test-renderer'
import { LinkOrButton } from './LinkOrButton'
import { ButtonLink } from './LinkOrButton'

describe('LinkOrButton', () => {
test('render a link when "to" is set', () => {
const component = renderer.create(<LinkOrButton to="http://example.com">foo</LinkOrButton>)
const component = renderer.create(<ButtonLink to="http://example.com">foo</ButtonLink>)
expect(component.toJSON()).toMatchSnapshot()
})

test('render a button when "to" is undefined', () => {
const component = renderer.create(<LinkOrButton to={undefined}>foo</LinkOrButton>)
const component = renderer.create(<ButtonLink to={undefined}>foo</ButtonLink>)
expect(component.toJSON()).toMatchSnapshot()
})
})
9 changes: 6 additions & 3 deletions client/shared/src/components/LinkOrButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,17 @@ interface Props extends Pick<AnchorHTMLAttributes<never>, 'target' | 'rel'> {
className?: string

disabled?: boolean

id?: string
}

/**
* A component that is displayed in the same way, regardless of whether it's a link (with a
* destination URL) or a button (with a click handler).
*
* It is keyboard accessible: unlike <Link> or <a>, pressing the enter key triggers it. Unlike
* <button>, it shows a focus ring.
* It is keyboard accessible: unlike `<Link>` or `<a>`, pressing the enter key triggers it.
*/
export const LinkOrButton: React.FunctionComponent<Props> = ({
export const ButtonLink: React.FunctionComponent<Props> = ({
className = 'nav-link',
to,
target,
Expand All @@ -53,6 +54,7 @@ export const LinkOrButton: React.FunctionComponent<Props> = ({
'data-tooltip': tooltip,
onSelect = noop,
children,
id,
}) => {
// We need to set up a keypress listener because <a onclick> doesn't get
// triggered by enter.
Expand All @@ -76,6 +78,7 @@ export const LinkOrButton: React.FunctionComponent<Props> = ({
tabIndex: 0,
onClick: onSelect,
onKeyPress: onAnchorKeyPress,
id,
}

const onClickPreventDefault: React.MouseEventHandler<HTMLAnchorElement> = useCallback(
Expand Down
12 changes: 11 additions & 1 deletion client/web/src/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LoadingSpinner } from '@sourcegraph/react-loading-spinner'
import React, { Suspense } from 'react'
import React, { Suspense, useCallback } from 'react'
import { Redirect, Route, RouteComponentProps, Switch, matchPath } from 'react-router'
import { Observable } from 'rxjs'
import { ActivationProps } from '../../shared/src/components/activation/Activation'
Expand Down Expand Up @@ -64,6 +64,7 @@ import { AuthenticatedUser, authRequired as authRequiredObservable } from './aut
import { SearchPatternType } from './graphql-operations'
import { TelemetryProps } from '../../shared/src/telemetry/telemetryService'
import { useObservable } from '../../shared/src/util/useObservable'
import { useExtensionAlertAnimation } from './nav/UserNavItem'

export interface LayoutProps
extends RouteComponentProps<{}>,
Expand Down Expand Up @@ -169,6 +170,13 @@ export const Layout: React.FunctionComponent<LayoutProps> = props => {

const breadcrumbProps = useBreadcrumbs()

// Control browser extension discoverability animation here.
// `Layout` is the lowest common ancestor of `UserNavItem` (target) and `RepoContainer` (trigger)
const { isExtensionAlertAnimating, startExtensionAlertAnimation } = useExtensionAlertAnimation()
const onExtensionAlertDismissed = useCallback(() => {
startExtensionAlertAnimation()
}, [startExtensionAlertAnimation])

useScrollToLocationHash(props.location)
// Remove trailing slash (which is never valid in any of our URLs).
if (props.location.pathname !== '/' && props.location.pathname.endsWith('/')) {
Expand All @@ -178,6 +186,7 @@ export const Layout: React.FunctionComponent<LayoutProps> = props => {
const context = {
...props,
...breadcrumbProps,
onExtensionAlertDismissed,
}

return (
Expand Down Expand Up @@ -211,6 +220,7 @@ export const Layout: React.FunctionComponent<LayoutProps> = props => {
}
hideNavLinks={false}
minimalNavLinks={minimalNavLinks}
isExtensionAlertAnimating={isExtensionAlertAnimating}
/>
)}
{needsSiteInit && !isSiteInit && <Redirect to="/site-admin/init" />}
Expand Down
2 changes: 2 additions & 0 deletions client/web/src/SourcegraphWebApp.scss
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ body,
@import './repo/RepoContainer';
@import './repo/FilePathBreadcrumbs.scss';
@import './repo/settings/RepoSettingsArea';
@import './repo/actions//InstallBrowserExtensionPopover.scss';
@import './repo/actions/InstallBrowserExtensionAlert.scss';
@import './components/LoaderInput';
@import '../../branded/src/components/CodeSnippet';
@import './components/PageHeader';
Expand Down
2 changes: 1 addition & 1 deletion client/web/src/auth/icons.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'

export const SourcegraphIcon: React.FunctionComponent<React.SVGAttributes<SVGSVGElement>> = props => (
<svg width="65" height="64" viewBox="0 0 65 64" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<svg viewBox="0 0 65 64" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
fillRule="evenodd"
clipRule="evenodd"
Expand Down
16 changes: 16 additions & 0 deletions client/web/src/components/Dialog.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import '~@reach/dialog/styles.css';

.modal-body {
background-color: var(--body-bg);
padding: 1rem;
width: 32rem;
max-width: 100vw;

&--centered {
transform: translate(-50%, -50%);
position: absolute;
top: 50%;
margin: 0;
left: 50%;
}
}
23 changes: 0 additions & 23 deletions client/web/src/components/ModalContainer.scss

This file was deleted.

44 changes: 0 additions & 44 deletions client/web/src/components/ModalContainer.story.tsx

This file was deleted.

93 changes: 0 additions & 93 deletions client/web/src/components/ModalContainer.tsx

This file was deleted.

17 changes: 15 additions & 2 deletions client/web/src/components/shared.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import classNames from 'classnames'
import React, { useCallback } from 'react'
import React, { useCallback, useEffect } from 'react'
import { ActionsNavItems, ActionsNavItemsProps } from '../../../shared/src/actions/ActionsNavItems'
import { CommandListPopoverButton, CommandListPopoverButtonProps } from '../../../shared/src/commandPalette/CommandList'
import {
Expand All @@ -9,10 +9,11 @@ import {
import { isErrorLike } from '../../../shared/src/util/errors'
import { HoverOverlay, HoverOverlayProps } from '../../../shared/src/hover/HoverOverlay'
import { useLocalStorage } from '../util/useLocalStorage'
import { HoverThresholdProps } from '../repo/RepoContainer'

// Components from shared with web-styling class names applied

export const WebHoverOverlay: React.FunctionComponent<HoverOverlayProps> = props => {
export const WebHoverOverlay: React.FunctionComponent<HoverOverlayProps & HoverThresholdProps> = props => {
const [dismissedAlerts, setDismissedAlerts] = useLocalStorage<string[]>('WebHoverOverlay.dismissedAlerts', [])
const onAlertDismissed = useCallback(
(alertType: string) => {
Expand All @@ -31,6 +32,18 @@ export const WebHoverOverlay: React.FunctionComponent<HoverOverlayProps> = props
propsToUse = { ...props, hoverOrError: { ...props.hoverOrError, alerts: filteredAlerts } }
}

const { hoverOrError } = propsToUse
const { onHoverShown, hoveredToken } = props

/** Whether the hover has actual content (that provides value to the user) */
const hoverHasValue = hoverOrError !== 'loading' && !isErrorLike(hoverOrError) && !!hoverOrError?.contents?.length

useEffect(() => {
if (hoverHasValue) {
onHoverShown?.()
}
}, [hoveredToken?.filePath, hoveredToken?.line, hoveredToken?.character, onHoverShown, hoverHasValue])

return (
<HoverOverlay
{...propsToUse}
Expand Down
Loading