mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 19:21:50 +00:00
feat(bext): add ability to remove saved sg url from suggestion list (#52555)
This commit is contained in:
parent
123e752481
commit
6ee50e0ce9
@ -13,6 +13,8 @@ All notable changes to Sourcegraph [Browser Extensions](./README.md) are documen
|
||||
|
||||
## Unreleased
|
||||
|
||||
- add ability to remove saved sg url from suggestion list [pull/52555](https://github.com/sourcegraph/sourcegraph/pull/52555)
|
||||
|
||||
- Fix code-intel tooltips for pull request pages on Bitbucket: https://github.com/sourcegraph/sourcegraph/pull/52609
|
||||
|
||||
## Chrome & Firefox 23.4.14.1343, Safari 1.24
|
||||
|
||||
@ -35,46 +35,10 @@
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-flex;
|
||||
place-items: center;
|
||||
padding: 0.5rem;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.popover[data-reach-combobox-popover] {
|
||||
margin: 0;
|
||||
padding: 0.25rem 0;
|
||||
background: var(--color-bg-1);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
box-shadow: var(--dropdown-shadow);
|
||||
|
||||
:global(.theme-dark) & {
|
||||
background-color: var(--input-bg);
|
||||
}
|
||||
|
||||
&,
|
||||
[data-suggested-value] {
|
||||
font-size: 0.875rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
[data-reach-combobox-list] {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[data-reach-combobox-option] {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0.5rem 0.75rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&[aria-selected='true'] {
|
||||
background: var(--color-bg-3);
|
||||
}
|
||||
.suggestion-remove-button {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
&:hover {
|
||||
background: var(--color-bg-3);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import { DecoratorFn, Meta, Story } from '@storybook/react'
|
||||
import GithubIcon from 'mdi-react/GithubIcon'
|
||||
import { Observable, of } from 'rxjs'
|
||||
|
||||
import { H1, H2, H3 } from '@sourcegraph/wildcard'
|
||||
import { Grid, H1, H2, H3 } from '@sourcegraph/wildcard'
|
||||
import { BrandedStory } from '@sourcegraph/wildcard/src/stories'
|
||||
|
||||
import { OptionsPage, OptionsPageProps } from './OptionsPage'
|
||||
@ -26,21 +26,30 @@ const config: Meta = {
|
||||
|
||||
export default config
|
||||
|
||||
const OptionsPageWrapper: React.FunctionComponent<React.PropsWithChildren<Partial<OptionsPageProps>>> = props => (
|
||||
<OptionsPage
|
||||
isFullPage={false}
|
||||
isActivated={true}
|
||||
onToggleActivated={action('onToggleActivated')}
|
||||
optionFlags={[{ key: 'allowErrorReporting', label: 'Allow error reporting', value: false }]}
|
||||
onChangeOptionFlag={action('onChangeOptionFlag')}
|
||||
version=""
|
||||
sourcegraphUrl=""
|
||||
validateSourcegraphUrl={validateSourcegraphUrl}
|
||||
onChangeSourcegraphUrl={action('onChangeSourcegraphUrl')}
|
||||
suggestedSourcegraphUrls={['https://k8s.sgdev.org', 'https://sourcegraph.com']}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
const OptionsPageWrapper: React.FunctionComponent<React.PropsWithChildren<Partial<OptionsPageProps>>> = props => {
|
||||
const [urls, setUrls] = useState([
|
||||
'https://sourcegraph.com',
|
||||
'https://k8s.sgdev.org',
|
||||
'https://sourcegraph.sourcegraph.com',
|
||||
])
|
||||
|
||||
return (
|
||||
<OptionsPage
|
||||
isFullPage={false}
|
||||
isActivated={true}
|
||||
onToggleActivated={action('onToggleActivated')}
|
||||
optionFlags={[{ key: 'allowErrorReporting', label: 'Allow error reporting', value: false }]}
|
||||
onChangeOptionFlag={action('onChangeOptionFlag')}
|
||||
version=""
|
||||
sourcegraphUrl=""
|
||||
validateSourcegraphUrl={validateSourcegraphUrl}
|
||||
onChangeSourcegraphUrl={action('onChangeSourcegraphUrl')}
|
||||
suggestedSourcegraphUrls={urls}
|
||||
onSuggestedSourcegraphUrlDelete={url => setUrls(urls.filter(item => item !== url))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const Interactive: Story = args => {
|
||||
const [isActivated, setIsActivated] = useState(false)
|
||||
@ -68,75 +77,73 @@ const WithAdvancedSettings: Story = args => {
|
||||
export const AllOptionsPages: Story = (args = {}) => (
|
||||
<div>
|
||||
<H1 className="text-center mb-3">All Options Pages</H1>
|
||||
<div>
|
||||
<div className="d-flex justify-content-center">
|
||||
<div className="mx-4">
|
||||
<H3 className="text-center">Interactive</H3>
|
||||
<Interactive {...args} />
|
||||
</div>
|
||||
<div className="mx-4">
|
||||
<H3 className="text-center">URL validation error</H3>
|
||||
<OptionsPageWrapper validateSourcegraphUrl={invalidSourcegraphUrl} {...args} />
|
||||
</div>
|
||||
<div className="mx-4">
|
||||
<H3 className="text-center">With advanced settings</H3>
|
||||
<WithAdvancedSettings {...args} />
|
||||
</div>
|
||||
<Grid columnCount={3}>
|
||||
<div>
|
||||
<H3 className="text-center">Interactive</H3>
|
||||
<Interactive {...args} />
|
||||
</div>
|
||||
|
||||
<div className="d-flex justify-content-center mt-5">
|
||||
<div className="mx-4">
|
||||
<H3 className="text-center">On Sourcegraph.com</H3>
|
||||
<OptionsPageWrapper
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
showSourcegraphComAlert={true}
|
||||
sourcegraphUrl={args.sourcegraphUrl}
|
||||
version={args.version}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx-4">
|
||||
<H3 className="text-center">Asking for permission</H3>
|
||||
<OptionsPageWrapper
|
||||
permissionAlert={{ name: 'GitHub', icon: GithubIcon }}
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
{...args}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<H3 className="text-center">URL validation error</H3>
|
||||
<OptionsPageWrapper validateSourcegraphUrl={invalidSourcegraphUrl} {...args} />
|
||||
</div>
|
||||
|
||||
<H2 className="mt-5 text-center">Not synced repository</H2>
|
||||
<div className="d-flex justify-content-center mb-3">
|
||||
<div className="mx-4">
|
||||
<H3 className="text-center">Sourcegraph.com</H3>
|
||||
<OptionsPageWrapper
|
||||
sourcegraphUrl="https://sourcegraph.com"
|
||||
currentUser={{ settingsURL: '/users/john-doe/settings', siteAdmin: false }}
|
||||
hasRepoSyncError={true}
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
showSourcegraphComAlert={args.showSourcegraphComAlert}
|
||||
version={args.version}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx-4">
|
||||
<H3 className="text-center">Self-hosted</H3>
|
||||
<OptionsPageWrapper
|
||||
currentUser={{ settingsURL: '/users/john-doe/settings', siteAdmin: false }}
|
||||
hasRepoSyncError={true}
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
{...args}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx-4">
|
||||
<H3 className="text-center">Self-hosted instance, user is admin</H3>
|
||||
<OptionsPageWrapper
|
||||
currentUser={{ settingsURL: '/users/john-doe/settings', siteAdmin: true }}
|
||||
hasRepoSyncError={true}
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
{...args}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<H3 className="text-center">With advanced settings</H3>
|
||||
<WithAdvancedSettings {...args} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<H3 className="text-center">No previous url suggestion</H3>
|
||||
<OptionsPageWrapper suggestedSourcegraphUrls={[]} {...args} />
|
||||
</div>
|
||||
<div>
|
||||
<H3 className="text-center">On Sourcegraph.com</H3>
|
||||
<OptionsPageWrapper
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
showSourcegraphComAlert={true}
|
||||
sourcegraphUrl={args.sourcegraphUrl}
|
||||
version={args.version}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<H3 className="text-center">Asking for permission</H3>
|
||||
<OptionsPageWrapper
|
||||
permissionAlert={{ name: 'GitHub', icon: GithubIcon }}
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
{...args}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
<H2 className="mt-5 text-center">Not synced repository</H2>
|
||||
<Grid columnCount={3}>
|
||||
<div>
|
||||
<H3 className="text-center">Sourcegraph.com</H3>
|
||||
<OptionsPageWrapper
|
||||
sourcegraphUrl="https://sourcegraph.com"
|
||||
currentUser={{ settingsURL: '/users/john-doe/settings', siteAdmin: false }}
|
||||
hasRepoSyncError={true}
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
showSourcegraphComAlert={args.showSourcegraphComAlert}
|
||||
version={args.version}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<H3 className="text-center">Self-hosted</H3>
|
||||
<OptionsPageWrapper
|
||||
currentUser={{ settingsURL: '/users/john-doe/settings', siteAdmin: false }}
|
||||
hasRepoSyncError={true}
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
{...args}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<H3 className="text-center">Self-hosted instance, user is admin</H3>
|
||||
<OptionsPageWrapper
|
||||
currentUser={{ settingsURL: '/users/john-doe/settings', siteAdmin: true }}
|
||||
hasRepoSyncError={true}
|
||||
requestPermissionsHandler={requestPermissionsHandler}
|
||||
{...args}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
AllOptionsPages.argTypes = {
|
||||
|
||||
@ -1,15 +1,35 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { mdiEarth, mdiBookOpenPageVariant, mdiCheckCircleOutline, mdiLock, mdiBlockHelper, mdiOpenInNew } from '@mdi/js'
|
||||
import { Combobox, ComboboxInput, ComboboxOption, ComboboxPopover, ComboboxList } from '@reach/combobox'
|
||||
import {
|
||||
mdiEarth,
|
||||
mdiBookOpenPageVariant,
|
||||
mdiCheckCircleOutline,
|
||||
mdiLock,
|
||||
mdiBlockHelper,
|
||||
mdiOpenInNew,
|
||||
mdiClose,
|
||||
} from '@mdi/js'
|
||||
import classNames from 'classnames'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
import { SourcegraphLogo } from '@sourcegraph/branded/src/components/SourcegraphLogo'
|
||||
import { Toggle } from '@sourcegraph/branded/src/components/Toggle'
|
||||
import { createURLWithUTM } from '@sourcegraph/shared/src/tracking/utm'
|
||||
import { useInputValidation, deriveInputClassName } from '@sourcegraph/shared/src/util/useInputValidation'
|
||||
import { Button, Link, Icon, Label, H4, Text, LoaderInput } from '@sourcegraph/wildcard'
|
||||
import { InputValidationState, useInputValidation } from '@sourcegraph/shared/src/util/useInputValidation'
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxInput,
|
||||
ComboboxOption,
|
||||
ComboboxPopover,
|
||||
ComboboxList,
|
||||
Button,
|
||||
Link,
|
||||
Icon,
|
||||
Label,
|
||||
H4,
|
||||
Text,
|
||||
InputStatus,
|
||||
} from '@sourcegraph/wildcard'
|
||||
|
||||
import { CurrentUserResult } from '../../graphql-operations'
|
||||
import { getPlatformName, isDefaultSourcegraphUrl } from '../../shared/util/context'
|
||||
@ -31,6 +51,7 @@ export interface OptionsPageProps {
|
||||
|
||||
// Suggested Sourcegraph URLs
|
||||
suggestedSourcegraphUrls: string[]
|
||||
onSuggestedSourcegraphUrlDelete: (url: string) => void
|
||||
|
||||
// Option flags
|
||||
optionFlags: { key: string; label: string; value: boolean }[]
|
||||
@ -75,6 +96,7 @@ export const OptionsPage: React.FunctionComponent<React.PropsWithChildren<Option
|
||||
suggestedSourcegraphUrls,
|
||||
hasRepoSyncError,
|
||||
currentUser,
|
||||
onSuggestedSourcegraphUrlDelete,
|
||||
}) => {
|
||||
const [showAdvancedSettings, setShowAdvancedSettings] = useState(initialShowAdvancedSettings)
|
||||
|
||||
@ -110,6 +132,7 @@ export const OptionsPage: React.FunctionComponent<React.PropsWithChildren<Option
|
||||
<SourcegraphURLForm
|
||||
value={sourcegraphUrl}
|
||||
suggestions={suggestedSourcegraphUrls}
|
||||
onSuggestionDelete={onSuggestedSourcegraphUrlDelete}
|
||||
onChange={onChangeSourcegraphUrl}
|
||||
validate={validateSourcegraphUrl}
|
||||
/>
|
||||
@ -276,13 +299,28 @@ interface SourcegraphURLFormProps {
|
||||
value: OptionsPageProps['sourcegraphUrl']
|
||||
validate: OptionsPageProps['validateSourcegraphUrl']
|
||||
onChange: OptionsPageProps['onChangeSourcegraphUrl']
|
||||
suggestions: OptionsPageProps['sourcegraphUrl'][]
|
||||
suggestions: OptionsPageProps['suggestedSourcegraphUrls']
|
||||
onSuggestionDelete: OptionsPageProps['onSuggestedSourcegraphUrlDelete']
|
||||
}
|
||||
|
||||
const getInputStatusFromKind = (kind: InputValidationState['kind']): InputStatus => {
|
||||
switch (kind) {
|
||||
case 'INVALID':
|
||||
return InputStatus.error
|
||||
case 'VALID':
|
||||
return InputStatus.valid
|
||||
case 'LOADING':
|
||||
return InputStatus.loading
|
||||
default:
|
||||
return InputStatus.initial
|
||||
}
|
||||
}
|
||||
|
||||
export const SourcegraphURLForm: React.FunctionComponent<React.PropsWithChildren<SourcegraphURLFormProps>> = ({
|
||||
value,
|
||||
validate,
|
||||
suggestions,
|
||||
onSuggestionDelete,
|
||||
onChange,
|
||||
}) => {
|
||||
const urlInputReference = useRef<HTMLInputElement | null>(null)
|
||||
@ -332,29 +370,51 @@ export const SourcegraphURLForm: React.FunctionComponent<React.PropsWithChildren
|
||||
<form onSubmit={preventDefault} noValidate={true}>
|
||||
<Label htmlFor="sourcegraph-url">Sourcegraph URL</Label>
|
||||
<Combobox openOnFocus={true} onSelect={nextUrlFieldChange}>
|
||||
<LoaderInput loading={urlState.kind === 'LOADING'} className={deriveInputClassName(urlState)}>
|
||||
<ComboboxInput
|
||||
type="url"
|
||||
required={true}
|
||||
spellCheck={false}
|
||||
autoComplete="off"
|
||||
autocomplete={false}
|
||||
pattern="^https://.*"
|
||||
placeholder="https://"
|
||||
onFocus={onFocus}
|
||||
id="sourcegraph-url"
|
||||
ref={urlInputElements}
|
||||
value={urlState.value}
|
||||
onChange={nextUrlFieldChange}
|
||||
className={classNames('form-control', 'test-sourcegraph-url', deriveInputClassName(urlState))}
|
||||
/>
|
||||
</LoaderInput>
|
||||
<ComboboxInput
|
||||
type="url"
|
||||
required={true}
|
||||
spellCheck={false}
|
||||
autoComplete="off"
|
||||
autocomplete={false}
|
||||
status={getInputStatusFromKind(urlState.kind)}
|
||||
pattern="^https://.*"
|
||||
placeholder="https://"
|
||||
onFocus={onFocus}
|
||||
id="sourcegraph-url"
|
||||
ref={urlInputElements}
|
||||
value={urlState.value}
|
||||
onChange={nextUrlFieldChange}
|
||||
className="test-sourcegraph-url"
|
||||
/>
|
||||
|
||||
{suggestions.length > 1 && hasInteracted && (
|
||||
<ComboboxPopover className={styles.popover}>
|
||||
<ComboboxPopover>
|
||||
<ComboboxList>
|
||||
{suggestions.map(suggestion => (
|
||||
<ComboboxOption key={suggestion} value={suggestion} />
|
||||
<ComboboxOption
|
||||
key={suggestion}
|
||||
value={suggestion}
|
||||
className="d-flex justify-content-between p-0"
|
||||
>
|
||||
<Text className="py-2 pl-3 m-0">{suggestion}</Text>
|
||||
<Button
|
||||
className={classNames('m-0 py-0 px-2', styles.suggestionRemoveButton)}
|
||||
onClick={event => {
|
||||
// prevent click from becoming option selection
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
if (
|
||||
confirm(
|
||||
`Are you sure you want to remove ${suggestion} from auto suggestion list?`
|
||||
)
|
||||
) {
|
||||
onSuggestionDelete(suggestion)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon svgPath={mdiClose} aria-label="Remove suggestion" />
|
||||
</Button>
|
||||
</ComboboxOption>
|
||||
))}
|
||||
</ComboboxList>
|
||||
</ComboboxPopover>
|
||||
|
||||
@ -248,6 +248,20 @@ const Options: React.FunctionComponent<React.PropsWithChildren<unknown>> = () =>
|
||||
[telemetryService]
|
||||
)
|
||||
|
||||
const handleRemovePreviousSourcegraphUrl = useCallback(
|
||||
(url: string): void => {
|
||||
if (!url || previouslyUsedUrls?.length === 0) {
|
||||
return
|
||||
}
|
||||
storage.sync
|
||||
.set({
|
||||
previouslyUsedURLs: previouslyUsedUrls?.filter(previouslyUsedUrl => previouslyUsedUrl !== url),
|
||||
})
|
||||
.catch(console.error)
|
||||
},
|
||||
[previouslyUsedUrls]
|
||||
)
|
||||
|
||||
return (
|
||||
<ThemeWrapper>
|
||||
<WildcardThemeProvider isBranded={true}>
|
||||
@ -255,6 +269,7 @@ const Options: React.FunctionComponent<React.PropsWithChildren<unknown>> = () =>
|
||||
isFullPage={isFullPage}
|
||||
sourcegraphUrl={sourcegraphUrl || ''}
|
||||
suggestedSourcegraphUrls={uniqURLs(previouslyUsedUrls || [])}
|
||||
onSuggestedSourcegraphUrlDelete={handleRemovePreviousSourcegraphUrl}
|
||||
onChangeSourcegraphUrl={handleChangeSourcegraphUrl}
|
||||
version={version}
|
||||
validateSourcegraphUrl={validateSourcegraphUrl}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user