mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 19:21:50 +00:00
Update dependency @sourcegraph/eslint-config to ^0.19.3 (#11683)
Co-authored-by: Renovate Bot <bot@renovateapp.com> Co-authored-by: Felix Becker <felix.b@outlook.com>
This commit is contained in:
parent
7926b6c5b0
commit
2c2dcb66ea
@ -26,4 +26,6 @@ shared/dev/**/*.js
|
||||
client/contrib/GH2SG.bookmarklet.js
|
||||
docker-images/grafana/jsonnet/*.json
|
||||
docker-images/grafana/grafonnet-lib/
|
||||
docker-images/grafana/config/provisioning/dashboards/sourcegraph/
|
||||
storybook-static/
|
||||
!/.storybook/**
|
||||
|
||||
@ -2,9 +2,9 @@ export function assertEnvironment(environment: typeof window['EXTENSION_ENV']):
|
||||
if (window.EXTENSION_ENV !== environment) {
|
||||
throw new Error(
|
||||
'Detected transitive import of an entrypoint! ' +
|
||||
window.EXTENSION_ENV +
|
||||
String(window.EXTENSION_ENV) +
|
||||
' attempted to import a file that is only intended to be imported by ' +
|
||||
environment +
|
||||
String(environment) +
|
||||
'.'
|
||||
)
|
||||
}
|
||||
|
||||
@ -100,11 +100,8 @@ export const diffDOMFunctions: DOMFunctions = {
|
||||
|
||||
const diffSide = codeElement.closest('.diff-editor')!
|
||||
|
||||
return diffSide.previousElementSibling &&
|
||||
// If the sibling to the left is the diff divider, it's in the HEAD.
|
||||
diffSide.previousElementSibling.classList.contains('segment-connector-column')
|
||||
? 'head'
|
||||
: 'base'
|
||||
// If the sibling to the left is the diff divider, it's in the HEAD.
|
||||
return diffSide.previousElementSibling?.classList.contains('segment-connector-column') ? 'head' : 'base'
|
||||
},
|
||||
isFirstCharacterDiffIndicator: () => false,
|
||||
}
|
||||
|
||||
@ -404,7 +404,7 @@ export const githubCodeHost: CodeHost = {
|
||||
|
||||
// Go to blob URL
|
||||
const fragment = target.position
|
||||
? `#L${target.position.line}${target.position.character ? ':' + target.position.character : ''}`
|
||||
? `#L${target.position.line}${target.position.character ? `:${target.position.character}` : ''}`
|
||||
: ''
|
||||
return `https://${target.rawRepoName}/blob/${revision}/${target.filePath}${fragment}`
|
||||
},
|
||||
|
||||
@ -22,7 +22,7 @@ const getTextContent = (element: HTMLElement): { textContent: string; adjust: nu
|
||||
|
||||
// For some reason, phabricator adds an invisible element to the beginning of lines containing the diff indicator
|
||||
// followed by a space (ex: '+ '). We need to adjust the position accordingly.
|
||||
if (element.firstElementChild && element.firstElementChild.classList.contains('aural-only')) {
|
||||
if (element.firstElementChild?.classList.contains('aural-only')) {
|
||||
const pre = element.firstElementChild.textContent || ''
|
||||
// Codeintellify handles ignoring one character for diff indicators so we'll allow it to adjust for that.
|
||||
adjust = pre.replace(/^(\+|-)/, '').length
|
||||
|
||||
@ -226,8 +226,7 @@ export function testDOMFunctions(
|
||||
it('should return correctly whether the first character is a diff indicator', () => {
|
||||
// Default is false
|
||||
const actualFirstCharacterIsDiffIndicator = Boolean(
|
||||
domFunctions.isFirstCharacterDiffIndicator &&
|
||||
domFunctions.isFirstCharacterDiffIndicator(codeElement)
|
||||
domFunctions.isFirstCharacterDiffIndicator?.(codeElement)
|
||||
)
|
||||
expect(actualFirstCharacterIsDiffIndicator).toBe(Boolean(firstCharacterIsDiffIndicator))
|
||||
if (actualFirstCharacterIsDiffIndicator) {
|
||||
|
||||
@ -9,7 +9,7 @@ import { DEFAULT_SOURCEGRAPH_URL, getExtensionVersion, observeSourcegraphURL } f
|
||||
const IS_EXTENSION = true
|
||||
|
||||
const isExtensionStackTrace = (stacktrace: Sentry.Stacktrace, extensionID: string): boolean =>
|
||||
!!(stacktrace.frames && stacktrace.frames.some(({ filename }) => !!filename?.includes(extensionID)))
|
||||
!!stacktrace.frames?.some(({ filename }) => !!filename?.includes(extensionID))
|
||||
|
||||
const callSentryInit = once((extensionID: string) => {
|
||||
Sentry.init({
|
||||
@ -18,7 +18,7 @@ const callSentryInit = once((extensionID: string) => {
|
||||
// Filter out events if we can tell from the stack trace that
|
||||
// they didn't originate from extension code.
|
||||
let keep = true
|
||||
if (event.exception && event.exception.values) {
|
||||
if (event.exception?.values) {
|
||||
keep = event.exception.values.some(
|
||||
({ stacktrace }) => !!(stacktrace && isExtensionStackTrace(stacktrace, extensionID))
|
||||
)
|
||||
|
||||
@ -75,13 +75,14 @@
|
||||
"@babel/runtime": "^7.10.2",
|
||||
"@gql2ts/from-schema": "^1.10.1",
|
||||
"@gql2ts/language-typescript": "^1.9.0",
|
||||
"@gql2ts/types": "^1.9.0",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@octokit/rest": "^16.36.0",
|
||||
"@percy/puppeteer": "^1.1.0",
|
||||
"@percy/storybook": "^3.3.0",
|
||||
"@slack/web-api": "^5.8.1",
|
||||
"@sourcegraph/babel-plugin-transform-react-hot-loader-wrapper": "^1.0.0",
|
||||
"@sourcegraph/eslint-config": "^0.19.2",
|
||||
"@sourcegraph/eslint-config": "^0.19.3",
|
||||
"@sourcegraph/prettierrc": "^3.0.3",
|
||||
"@sourcegraph/stylelint-config": "^1.1.9",
|
||||
"@sourcegraph/tsconfig": "^4.0.1",
|
||||
|
||||
@ -47,10 +47,10 @@ async function graphQLTypes() {
|
||||
},
|
||||
{
|
||||
generateNamespace: (name, interfaces) => interfaces,
|
||||
interfaceBuilder: (name, body) => 'export ' + DEFAULT_OPTIONS.interfaceBuilder(name, body),
|
||||
interfaceBuilder: (name, body) => `export ${DEFAULT_OPTIONS.interfaceBuilder(name, body)}`,
|
||||
enumTypeBuilder: (name, values) =>
|
||||
'export ' + DEFAULT_OPTIONS.enumTypeBuilder(name, values).replace(/^const enum/, 'enum'),
|
||||
typeBuilder: (name, body) => 'export ' + DEFAULT_OPTIONS.typeBuilder(name, body),
|
||||
`export ${DEFAULT_OPTIONS.enumTypeBuilder(name, values).replace(/^const enum/, 'enum')}`,
|
||||
typeBuilder: (name, body) => `export ${DEFAULT_OPTIONS.typeBuilder(name, body)}`,
|
||||
wrapList: type => `${type}[]`,
|
||||
postProcessor: code => format(code, { ...formatOptions, parser: 'typescript' }),
|
||||
}
|
||||
|
||||
@ -120,8 +120,8 @@ export class ActionItem extends React.PureComponent<ActionItemProps, State> {
|
||||
|
||||
public componentDidUpdate(previousProps: ActionItemProps, previousState: State): void {
|
||||
// If the tooltip changes while it's visible, we need to force-update it to show the new value.
|
||||
const previousTooltip = previousProps.action.actionItem && previousProps.action.actionItem.description
|
||||
const tooltip = this.props.action.actionItem && this.props.action.actionItem.description
|
||||
const previousTooltip = previousProps.action.actionItem?.description
|
||||
const tooltip = this.props.action.actionItem?.description
|
||||
const descriptionTooltipChanged = previousTooltip !== tooltip
|
||||
|
||||
const errorTooltipChanged =
|
||||
|
||||
@ -97,6 +97,7 @@ function exec(node: ExpressionNode, context: ComputedContext): any {
|
||||
case '>=':
|
||||
return left >= right()
|
||||
case '+':
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
return left + right()
|
||||
case '-':
|
||||
return left - right()
|
||||
|
||||
@ -15,7 +15,7 @@ export function fromHoverMerged(values: (Badged<Hover | PlainHover> | null | und
|
||||
let range: Range | undefined
|
||||
for (const result of values) {
|
||||
if (result) {
|
||||
if (result.contents && result.contents.value) {
|
||||
if (result.contents?.value) {
|
||||
contents.push({
|
||||
value: result.contents.value,
|
||||
kind: result.contents.kind || MarkupKind.PlainText,
|
||||
|
||||
@ -6,7 +6,7 @@ import MenuDownIcon from 'mdi-react/MenuDownIcon'
|
||||
import MenuIcon from 'mdi-react/MenuIcon'
|
||||
import MenuUpIcon from 'mdi-react/MenuUpIcon'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import TooltipPopoverWrapper from 'reactstrap/lib/TooltipPopoverWrapper'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
@ -67,7 +67,7 @@ export class ActivationChecklist extends React.PureComponent<ActivationChecklist
|
||||
key={step.id}
|
||||
{...step}
|
||||
history={this.props.history}
|
||||
done={(this.props.completed && this.props.completed[step.id]) || false}
|
||||
done={this.props.completed?.[step.id] || false}
|
||||
className={this.props.buttonClassName}
|
||||
/>
|
||||
</AccordionButton>
|
||||
|
||||
@ -18,7 +18,7 @@ export function getContributedActionItems(
|
||||
}
|
||||
|
||||
const allItems: ActionItemAction[] = []
|
||||
const menuItems = contributions.menus && contributions.menus[menu]
|
||||
const menuItems = contributions.menus?.[menu]
|
||||
if (menuItems) {
|
||||
for (const { action: actionID, alt: altActionID } of sortBy(menuItems, MENU_ITEMS_PROP_SORT_ORDER)) {
|
||||
const action = contributions.actions.find(a => a.id === actionID)
|
||||
|
||||
@ -160,8 +160,8 @@ export class HoverOverlay<A extends string> extends React.PureComponent<HoverOve
|
||||
? {
|
||||
opacity: 1,
|
||||
visibility: 'visible',
|
||||
left: overlayPosition.left + 'px',
|
||||
top: overlayPosition.top + 'px',
|
||||
left: `${overlayPosition.left}px`,
|
||||
top: `${overlayPosition.top}px`,
|
||||
}
|
||||
: {
|
||||
opacity: 0,
|
||||
|
||||
@ -115,7 +115,7 @@ export class NotificationItem extends React.PureComponent<Props, State> {
|
||||
<div
|
||||
className="sourcegraph-notification-item__progressbar progress-bar"
|
||||
// eslint-disable-next-line react/forbid-dom-props
|
||||
style={{ width: this.state.progress.percentage + '%' }}
|
||||
style={{ width: `${this.state.progress.percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -78,7 +78,7 @@ export interface PlatformContext {
|
||||
* @returns A promise that resolves after the update succeeds and {@link PlatformContext#settings} reflects the
|
||||
* update.
|
||||
*/
|
||||
updateSettings(subject: GQL.ID, edit: SettingsEdit | string): Promise<void>
|
||||
updateSettings: (subject: GQL.ID, edit: SettingsEdit | string) => Promise<void>
|
||||
|
||||
/**
|
||||
* Sends a request to the Sourcegraph GraphQL API and returns the response.
|
||||
@ -87,7 +87,7 @@ export interface PlatformContext {
|
||||
* could leak private information such as repository names.
|
||||
* @returns Observable that emits the result or an error if the HTTP request failed
|
||||
*/
|
||||
requestGraphQL<R extends GQL.IQuery | GQL.IMutation>(options: {
|
||||
requestGraphQL: <R extends GQL.IQuery | GQL.IMutation>(options: {
|
||||
/**
|
||||
* The GraphQL request (query or mutation)
|
||||
*/
|
||||
@ -101,12 +101,12 @@ export interface PlatformContext {
|
||||
* could leak private information such as repository names.
|
||||
*/
|
||||
mightContainPrivateInfo: boolean
|
||||
}): Observable<GraphQLResult<R>>
|
||||
}) => Observable<GraphQLResult<R>>
|
||||
|
||||
/**
|
||||
* Forces the currently displayed tooltip, if any, to update its contents.
|
||||
*/
|
||||
forceUpdateTooltip(): void
|
||||
forceUpdateTooltip: () => void
|
||||
|
||||
/**
|
||||
* Spawns a new JavaScript execution context (such as a Web Worker or browser extension
|
||||
@ -116,7 +116,7 @@ export interface PlatformContext {
|
||||
* @returns An observable that emits at most once with the message transports for communicating
|
||||
* with the execution context (using, e.g., postMessage/onmessage) when it is ready.
|
||||
*/
|
||||
createExtensionHost(): Observable<EndpointPair>
|
||||
createExtensionHost: () => Observable<EndpointPair>
|
||||
|
||||
/**
|
||||
* Returns the script URL suitable for passing to importScripts for an extension's bundle.
|
||||
@ -129,7 +129,7 @@ export interface PlatformContext {
|
||||
* @returns A script URL suitable for passing to importScripts, typically either the original
|
||||
* https:// URL for the extension's bundle or a blob: URI for it.
|
||||
*/
|
||||
getScriptURLForExtension(bundleURL: string): string | Promise<string>
|
||||
getScriptURLForExtension: (bundleURL: string) => string | Promise<string>
|
||||
|
||||
/**
|
||||
* Constructs the URL (possibly relative or absolute) to the file with the specified options.
|
||||
@ -138,7 +138,7 @@ export interface PlatformContext {
|
||||
* @param context Contextual information about the context of this invocation.
|
||||
* @returns The URL to the file with the specified options.
|
||||
*/
|
||||
urlToFile(
|
||||
urlToFile: (
|
||||
target: RepoSpec &
|
||||
Partial<RawRepoSpec> &
|
||||
RevisionSpec &
|
||||
@ -146,7 +146,7 @@ export interface PlatformContext {
|
||||
Partial<UIPositionSpec> &
|
||||
Partial<ViewStateSpec>,
|
||||
context: URLToFileContext
|
||||
): string
|
||||
) => string
|
||||
|
||||
/**
|
||||
* The URL to the Sourcegraph site that the user's session is associated with. This refers to
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
// @ts-expect-error
|
||||
import configure from 'core-js/configurator'
|
||||
|
||||
configure({
|
||||
|
||||
@ -111,7 +111,7 @@ function findElementMatchingRegexps(tag: string, regexps: string[]): HTMLElement
|
||||
// Ignore hidden elements
|
||||
continue
|
||||
}
|
||||
if (element.textContent && element.textContent.match(regexp)) {
|
||||
if (element.textContent?.match(regexp)) {
|
||||
return element
|
||||
}
|
||||
}
|
||||
@ -317,10 +317,10 @@ export class Driver {
|
||||
await this.page.waitForSelector(`[data-e2e-external-service-card-link="${kind.toUpperCase()}"]`, {
|
||||
visible: true,
|
||||
})
|
||||
await this.page.evaluate(selector => {
|
||||
await this.page.evaluate((selector: string) => {
|
||||
const element = document.querySelector<HTMLElement>(selector)
|
||||
if (!element) {
|
||||
throw new Error('Could not find element to click on for selector ' + selector)
|
||||
throw new Error(`Could not find element to click on for selector ${selector}`)
|
||||
}
|
||||
element.click()
|
||||
}, `[data-e2e-external-service-card-link="${kind.toUpperCase()}"]`)
|
||||
|
||||
1
shared/src/types/core-js/configurator.d.ts
vendored
Normal file
1
shared/src/types/core-js/configurator.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export default function configure(config: { usePolyfill?: string[] }): void
|
||||
@ -272,7 +272,7 @@ export function toPositionOrRangeHash(context: {
|
||||
* @param ctx 1-indexed partial position
|
||||
*/
|
||||
export function toPositionHashComponent(position: { line: number; character?: number }): string {
|
||||
return position.line.toString() + (position.character ? ':' + position.character : '')
|
||||
return position.line.toString() + (position.character ? `:${position.character}` : '')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -515,7 +515,7 @@ export function toViewStateHashComponent(viewState: string | undefined): string
|
||||
}
|
||||
|
||||
const positionString = (position: Position): string =>
|
||||
position.line + '' + (position.character ? ',' + position.character : '')
|
||||
position.line.toString() + (position.character ? `,${position.character}` : '')
|
||||
|
||||
/**
|
||||
* The inverse of parseRepoURI, this generates a string from parsed values.
|
||||
|
||||
@ -68,7 +68,7 @@ export function refreshAuthenticatedUser(): Observable<never> {
|
||||
export const authRequired = authenticatedUser.pipe(map(user => user === null && !window.context?.sourcegraphDotComMode))
|
||||
|
||||
// Populate authenticatedUser.
|
||||
if (window.context && window.context.isAuthenticatedUser) {
|
||||
if (window.context?.isAuthenticatedUser) {
|
||||
refreshAuthenticatedUser()
|
||||
.toPromise()
|
||||
.then(
|
||||
|
||||
@ -220,7 +220,7 @@ class ConnectionNodes<C extends Connection<N>, N, NP = {}> extends React.PureCom
|
||||
</small>
|
||||
</p>
|
||||
)
|
||||
} else if (this.props.connection.pageInfo && this.props.connection.pageInfo.hasNextPage) {
|
||||
} else if (this.props.connection.pageInfo?.hasNextPage) {
|
||||
// No total count to show, but it will show a 'Show more' button.
|
||||
} else if (totalCount === 0) {
|
||||
summary = this.props.emptyElement || (
|
||||
|
||||
@ -159,10 +159,9 @@ export class MonacoEditor extends React.PureComponent<Props, State> {
|
||||
id={this.props.id}
|
||||
className={classNames(this.props.className, this.props.border !== false && 'border')}
|
||||
/>
|
||||
{this.props.keyboardShortcutForFocus &&
|
||||
this.props.keyboardShortcutForFocus.keybindings.map((keybinding, index) => (
|
||||
<Shortcut key={index} {...keybinding} onMatch={this.focusInput} />
|
||||
))}
|
||||
{this.props.keyboardShortcutForFocus?.keybindings.map((keybinding, index) => (
|
||||
<Shortcut key={index} {...keybinding} onMatch={this.focusInput} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ export class BarChart<T extends BarChartSeries> extends React.Component<Props<T>
|
||||
.data(series.slice().reverse())
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('transform', (data, index) => 'translate(0,' + index * 20 + ')')
|
||||
.attr('transform', (data, index) => `translate(0,${index * 20})`)
|
||||
legend
|
||||
.append('rect')
|
||||
.attr('x', width - 19)
|
||||
@ -188,12 +188,7 @@ function wrapLabel(text: Selection<any, any, any, any>, width: number): void {
|
||||
// currentLine holds the line as it grows, until it overflows.
|
||||
let currentLine: string[] = []
|
||||
// tspan holds the current <tspan> element as it grows, until it overflows.
|
||||
let tspan = text
|
||||
.text(null)
|
||||
.append('tspan')
|
||||
.attr('x', 0)
|
||||
.attr('y', yAttribute)
|
||||
.attr('dy', dyAttribute + 'em')
|
||||
let tspan = text.text(null).append('tspan').attr('x', 0).attr('y', yAttribute).attr('dy', `${dyAttribute}em`)
|
||||
|
||||
while (words.length) {
|
||||
currentWord = words.pop() || ''
|
||||
@ -208,7 +203,7 @@ function wrapLabel(text: Selection<any, any, any, any>, width: number): void {
|
||||
.append('tspan')
|
||||
.attr('x', 0)
|
||||
.attr('y', yAttribute)
|
||||
.attr('dy', ++lineNumber * lineHeight + dyAttribute + 'em')
|
||||
.attr('dy', `${++lineNumber * lineHeight + dyAttribute}em`)
|
||||
.text(currentWord)
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ export class FileDiffNode extends React.PureComponent<FileDiffNodeProps, State>
|
||||
|
||||
let stat: React.ReactFragment
|
||||
// If one of the files was binary, display file size change instead of DiffStat.
|
||||
if ((node.oldFile && node.oldFile.binary) || (node.newFile && node.newFile.binary)) {
|
||||
if (node.oldFile?.binary || node.newFile?.binary) {
|
||||
const sizeChange = (node.newFile?.byteSize ?? 0) - (node.oldFile?.byteSize ?? 0)
|
||||
const className = sizeChange >= 0 ? 'text-success' : 'text-danger'
|
||||
stat = <strong className={classNames(className, 'mr-2 code')}>{prettyBytes(sizeChange)}</strong>
|
||||
@ -117,7 +117,7 @@ export class FileDiffNode extends React.PureComponent<FileDiffNodeProps, State>
|
||||
</div>
|
||||
</div>
|
||||
{this.state.expanded &&
|
||||
((node.oldFile && node.oldFile.binary) || (node.newFile && node.newFile.binary) ? (
|
||||
(node.oldFile?.binary || node.newFile?.binary ? (
|
||||
<div className="text-muted m-2">Binary files can't be rendered.</div>
|
||||
) : (
|
||||
<FileDiffHunks
|
||||
|
||||
@ -100,13 +100,13 @@ describe('e2e test suite', () => {
|
||||
})
|
||||
await driver.page.click('.e2e-settings-file .e2e-save-toolbar-save')
|
||||
await driver.page.waitForSelector('.e2e-global-alert .notices .global-alerts__alert', { visible: true })
|
||||
await driver.page.evaluate(message => {
|
||||
await driver.page.evaluate((message: string) => {
|
||||
const element = document.querySelector<HTMLElement>('.e2e-global-alert .notices .global-alerts__alert')
|
||||
if (!element) {
|
||||
throw new Error('No .e2e-global-alert .notices .global-alerts__alert element found')
|
||||
}
|
||||
if (!element.textContent?.includes(message)) {
|
||||
throw new Error('Expected "' + message + '" message, but didn\'t find it')
|
||||
throw new Error(`Expected "${message}" message, but didn't find it`)
|
||||
}
|
||||
}, message)
|
||||
})
|
||||
@ -115,7 +115,7 @@ describe('e2e test suite', () => {
|
||||
await driver.page.goto(sourcegraphBaseUrl + '/users/test/settings/tokens/new')
|
||||
await driver.page.waitForSelector('.e2e-create-access-token-description')
|
||||
|
||||
const name = 'E2E Test ' + new Date().toISOString() + ' ' + random(1, 1e7)
|
||||
const name = `E2E Test ${new Date().toISOString()} ${random(1, 1e7)}`
|
||||
|
||||
await driver.replaceText({
|
||||
selector: '.e2e-create-access-token-description',
|
||||
|
||||
@ -126,7 +126,7 @@ export const CampaignStatus: React.FunctionComponent<CampaignStatusProps> = ({
|
||||
<div className="progress mt-2 mb-1">
|
||||
{/* we need to set the width to control the progress bar, so: */}
|
||||
{/* eslint-disable-next-line react/forbid-dom-props */}
|
||||
<div className="progress-bar" style={{ width: progress + '%' }}>
|
||||
<div className="progress-bar" style={{ width: `${progress}%` }}>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -5,7 +5,7 @@ import { subMinutes } from 'date-fns'
|
||||
|
||||
describe('ChangesetLastSynced', () => {
|
||||
for (const viewerCanAdminister of [false, true]) {
|
||||
describe('ViewerCanAdminister: ' + viewerCanAdminister, () => {
|
||||
describe(`ViewerCanAdminister: ${viewerCanAdminister}`, () => {
|
||||
test('renders not scheduled', () => {
|
||||
const result = renderer.create(
|
||||
<ChangesetLastSynced
|
||||
|
||||
@ -25,7 +25,7 @@ export const ProductSubscriptionLabel: React.FunctionComponent<{
|
||||
{productSubscription.invoiceItem.plan[planField || 'nameWithBrand']} (
|
||||
{formatUserCount(productSubscription.invoiceItem.userCount)})
|
||||
</>
|
||||
) : productSubscription.activeLicense && productSubscription.activeLicense.info ? (
|
||||
) : productSubscription.activeLicense?.info ? (
|
||||
<>
|
||||
{productSubscription.activeLicense.info.productNameWithBrand} (
|
||||
{formatUserCount(productSubscription.activeLicense.info.userCount)})
|
||||
|
||||
@ -81,7 +81,7 @@ export class ExtensionsExploreSection extends React.PureComponent<Props, State>
|
||||
<ExtensionsExploreSectionExtensionCard
|
||||
key={extension.id}
|
||||
extensionID={extension.extensionIDWithoutRegistry}
|
||||
description={(extension.manifest && extension.manifest.description) || undefined}
|
||||
description={extension.manifest?.description || undefined}
|
||||
url={extension.url}
|
||||
className="list-group-item list-group-item-action"
|
||||
/>
|
||||
|
||||
@ -87,7 +87,7 @@ export const RegistryNewExtensionPage = withAuthenticatedUser(
|
||||
concat(
|
||||
[{ publishersOrError: 'loading' }],
|
||||
queryViewerRegistryPublishers().pipe(
|
||||
map(result => ({ publishersOrError: result, publisher: result[0] && result[0].id })),
|
||||
map(result => ({ publishersOrError: result, publisher: result[0]?.id })),
|
||||
catchError(error => [{ publishersOrError: asError(error) }])
|
||||
)
|
||||
).subscribe(
|
||||
|
||||
@ -96,7 +96,7 @@ export const SiteAdminProductSubscriptionNode: React.FunctionComponent<SiteAdmin
|
||||
)}
|
||||
</td>
|
||||
<td className="w-100">
|
||||
{node.activeLicense && node.activeLicense.info && node.activeLicense.info.tags.length > 0 ? (
|
||||
{node.activeLicense?.info && node.activeLicense.info.tags.length > 0 ? (
|
||||
<ProductLicenseTags tags={node.activeLicense.info.tags} />
|
||||
) : (
|
||||
<span className="text-muted font-italic">None</span>
|
||||
|
||||
@ -81,28 +81,25 @@ export const UserSubscriptionsProductSubscriptionPage: React.FunctionComponent<P
|
||||
) : (
|
||||
<>
|
||||
<h2>Subscription {productSubscription.name}</h2>
|
||||
{(productSubscription.invoiceItem ||
|
||||
(productSubscription.activeLicense && productSubscription.activeLicense.info)) && (
|
||||
{(productSubscription.invoiceItem || productSubscription.activeLicense?.info) && (
|
||||
<UserProductSubscriptionStatus
|
||||
subscriptionName={productSubscription.name}
|
||||
productNameWithBrand={
|
||||
productSubscription.activeLicense && productSubscription.activeLicense.info
|
||||
productSubscription.activeLicense?.info
|
||||
? productSubscription.activeLicense.info.productNameWithBrand
|
||||
: productSubscription.invoiceItem!.plan.nameWithBrand
|
||||
}
|
||||
userCount={
|
||||
productSubscription.activeLicense && productSubscription.activeLicense.info
|
||||
productSubscription.activeLicense?.info
|
||||
? productSubscription.activeLicense.info.userCount
|
||||
: productSubscription.invoiceItem!.userCount
|
||||
}
|
||||
expiresAt={
|
||||
productSubscription.activeLicense && productSubscription.activeLicense.info
|
||||
productSubscription.activeLicense?.info
|
||||
? parseISO(productSubscription.activeLicense.info.expiresAt)
|
||||
: parseISO(productSubscription.invoiceItem!.expiresAt)
|
||||
}
|
||||
licenseKey={
|
||||
productSubscription.activeLicense && productSubscription.activeLicense.licenseKey
|
||||
}
|
||||
licenseKey={productSubscription.activeLicense?.licenseKey ?? null}
|
||||
/>
|
||||
)}
|
||||
<div className="card mt-3">
|
||||
|
||||
@ -65,10 +65,7 @@ export class ExtensionCard extends React.PureComponent<Props> {
|
||||
<div className="text-truncate w-100">
|
||||
<div className="d-flex align-items-center">
|
||||
<h4 className="card-title extension-card__body-title mb-0 mr-1 text-truncate font-weight-normal flex-1">
|
||||
<LinkOrSpan
|
||||
to={node.registryExtension && node.registryExtension.url}
|
||||
className="stretched-link"
|
||||
>
|
||||
<LinkOrSpan to={node.registryExtension?.url} className="stretched-link">
|
||||
<Path
|
||||
path={
|
||||
node.registryExtension
|
||||
@ -78,7 +75,7 @@ export class ExtensionCard extends React.PureComponent<Props> {
|
||||
/>
|
||||
</LinkOrSpan>
|
||||
</h4>
|
||||
{node.registryExtension && node.registryExtension.isWorkInProgress && (
|
||||
{node.registryExtension?.isWorkInProgress && (
|
||||
<WorkInProgressBadge
|
||||
viewerCanAdminister={node.registryExtension.viewerCanAdminister}
|
||||
/>
|
||||
|
||||
@ -9,6 +9,7 @@ import { SettingsCascade, SettingsCascadeOrError, SettingsCascadeProps } from '.
|
||||
import { ErrorLike, isErrorLike } from '../../../shared/src/util/errors'
|
||||
import { eventLogger } from '../tracking/eventLogger'
|
||||
import { isExtensionAdded } from './extension/extension'
|
||||
import { property } from '../../../shared/src/util/types'
|
||||
|
||||
interface Props extends SettingsCascadeProps, PlatformContextProps<'updateSettings'> {
|
||||
/** The extension that this element is for. */
|
||||
@ -113,13 +114,14 @@ function confirmAddExtension(extensionID: string): boolean {
|
||||
/** Converts a SettingsCascadeOrError to a SettingsCascade, returning the first error it finds. */
|
||||
function extractErrors(settingsCascade: SettingsCascadeOrError): SettingsCascade | ErrorLike {
|
||||
if (settingsCascade.subjects === null) {
|
||||
return new Error('Subjects was ' + settingsCascade.subjects)
|
||||
return new Error('Subjects was null')
|
||||
}
|
||||
if (settingsCascade.final === null || isErrorLike(settingsCascade.final)) {
|
||||
return new Error('Merged was ' + settingsCascade.final)
|
||||
return new Error(`Merged was ${String(settingsCascade.final)}`)
|
||||
}
|
||||
if (settingsCascade.subjects.find(isErrorLike)) {
|
||||
return new Error('One of the subjects was ' + settingsCascade.subjects.find(isErrorLike))
|
||||
const found = settingsCascade.subjects.find(property('settings', isErrorLike))
|
||||
if (found) {
|
||||
return new Error(`One of the subjects was ${found.settings.message}`)
|
||||
}
|
||||
return settingsCascade as SettingsCascade
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ export const ExtensionAreaHeader: React.FunctionComponent<ExtensionAreaHeaderPro
|
||||
// noop
|
||||
}
|
||||
|
||||
const isWorkInProgress = props.extension.registryExtension && props.extension.registryExtension.isWorkInProgress
|
||||
const isWorkInProgress = props.extension.registryExtension?.isWorkInProgress
|
||||
|
||||
return (
|
||||
<div className={`extension-area-header ${props.className || ''}`}>
|
||||
|
||||
@ -72,7 +72,7 @@ function toContributionsGroups(manifest: ExtensionManifest): ContributionGroup[]
|
||||
|
||||
const settingsGroup: ContributionGroup = { title: 'Settings', columnHeaders: ['Name', 'Description'], rows: [] }
|
||||
try {
|
||||
if (manifest.contributes.configuration && manifest.contributes.configuration.properties) {
|
||||
if (manifest.contributes.configuration?.properties) {
|
||||
for (const [name, schema] of Object.entries(manifest.contributes.configuration.properties)) {
|
||||
settingsGroup.rows.push([
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
|
||||
@ -15,7 +15,7 @@ export const ExtensionNoManifestAlert: React.FunctionComponent<{
|
||||
}> = ({ extension }) => (
|
||||
<div className="alert alert-info">
|
||||
This extension is not yet published.
|
||||
{extension.registryExtension && extension.registryExtension.viewerCanAdminister && (
|
||||
{extension.registryExtension?.viewerCanAdminister && (
|
||||
<>
|
||||
<br />
|
||||
<Link className="mt-3 btn btn-primary" to={`${extension.registryExtension.url}/-/releases/new`}>
|
||||
@ -76,15 +76,14 @@ export class RegistryExtensionManifestPage extends React.PureComponent<Props, St
|
||||
{this.state.viewMode === ViewMode.Plain ? ViewMode.Rich : ViewMode.Plain} viewer
|
||||
</button>
|
||||
)}{' '}
|
||||
{this.props.extension.registryExtension &&
|
||||
this.props.extension.registryExtension.viewerCanAdminister && (
|
||||
<Link
|
||||
className="btn btn-primary"
|
||||
to={`${this.props.extension.registryExtension.url}/-/releases/new`}
|
||||
>
|
||||
Publish new release
|
||||
</Link>
|
||||
)}
|
||||
{this.props.extension.registryExtension?.viewerCanAdminister && (
|
||||
<Link
|
||||
className="btn btn-primary"
|
||||
to={`${this.props.extension.registryExtension.url}/-/releases/new`}
|
||||
>
|
||||
Publish new release
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
|
||||
@ -101,47 +101,41 @@ export class RegistryExtensionOverviewPage extends React.PureComponent<Props> {
|
||||
)}
|
||||
<small className="text-muted">
|
||||
<dl className="border-top pt-2">
|
||||
{this.props.extension.registryExtension &&
|
||||
this.props.extension.registryExtension.publisher && (
|
||||
<>
|
||||
<dt>Publisher</dt>
|
||||
<dd>
|
||||
{this.props.extension.registryExtension.publisher ? (
|
||||
<Link to={this.props.extension.registryExtension.publisher.url}>
|
||||
{extensionIDPrefix(
|
||||
this.props.extension.registryExtension.publisher
|
||||
)}
|
||||
</Link>
|
||||
) : (
|
||||
'Unavailable'
|
||||
)}
|
||||
</dd>
|
||||
</>
|
||||
)}
|
||||
{this.props.extension.registryExtension &&
|
||||
this.props.extension.registryExtension.registryName && (
|
||||
<>
|
||||
<dt
|
||||
className={
|
||||
this.props.extension.registryExtension.publisher
|
||||
? 'border-top pt-2'
|
||||
: ''
|
||||
{this.props.extension.registryExtension?.publisher && (
|
||||
<>
|
||||
<dt>Publisher</dt>
|
||||
<dd>
|
||||
{this.props.extension.registryExtension.publisher ? (
|
||||
<Link to={this.props.extension.registryExtension.publisher.url}>
|
||||
{extensionIDPrefix(this.props.extension.registryExtension.publisher)}
|
||||
</Link>
|
||||
) : (
|
||||
'Unavailable'
|
||||
)}
|
||||
</dd>
|
||||
</>
|
||||
)}
|
||||
{this.props.extension.registryExtension?.registryName && (
|
||||
<>
|
||||
<dt
|
||||
className={
|
||||
this.props.extension.registryExtension.publisher ? 'border-top pt-2' : ''
|
||||
}
|
||||
>
|
||||
Published on
|
||||
</dt>
|
||||
<dd>
|
||||
<LinkOrSpan
|
||||
to={this.props.extension.registryExtension.remoteURL}
|
||||
target={
|
||||
this.props.extension.registryExtension.isLocal ? undefined : '_self'
|
||||
}
|
||||
>
|
||||
Published on
|
||||
</dt>
|
||||
<dd>
|
||||
<LinkOrSpan
|
||||
to={this.props.extension.registryExtension.remoteURL}
|
||||
target={
|
||||
this.props.extension.registryExtension.isLocal ? undefined : '_self'
|
||||
}
|
||||
>
|
||||
{this.props.extension.registryExtension.registryName}
|
||||
</LinkOrSpan>
|
||||
</dd>
|
||||
</>
|
||||
)}
|
||||
{this.props.extension.registryExtension.registryName}
|
||||
</LinkOrSpan>
|
||||
</dd>
|
||||
</>
|
||||
)}
|
||||
<dt className="border-top pt-2">Extension ID</dt>
|
||||
<dd>{this.props.extension.id}</dd>
|
||||
{this.props.extension.registryExtension &&
|
||||
|
||||
@ -15,7 +15,7 @@ const PublishNewManifestAlert: React.FunctionComponent<{
|
||||
}> = ({ extension, text, buttonLabel, alertClass }) => (
|
||||
<div className={`alert ${alertClass}`}>
|
||||
{text}
|
||||
{extension.registryExtension && extension.registryExtension.viewerCanAdminister && (
|
||||
{extension.registryExtension?.viewerCanAdminister && (
|
||||
<>
|
||||
<br />
|
||||
<Link className="mt-3 btn btn-primary" to={`${extension.registryExtension.url}/-/releases/new`}>
|
||||
|
||||
@ -17,13 +17,6 @@ import { GlobalAlert } from './GlobalAlert'
|
||||
import { Notices } from './Notices'
|
||||
import * as H from 'history'
|
||||
|
||||
// This module is not in @types/semver yet. We can't use the top-level semver module because it uses
|
||||
// dynamic requires, which Webpack complains about.
|
||||
//
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
||||
// @ts-ignore
|
||||
import semverParse from 'semver/functions/parse'
|
||||
|
||||
interface Props extends SettingsCascadeProps {
|
||||
history: H.History
|
||||
isSiteAdmin: boolean
|
||||
|
||||
@ -150,10 +150,10 @@ export class GlobalNavbar extends React.PureComponent<Props, State> {
|
||||
const branding = window.context ? window.context.branding : null
|
||||
if (branding) {
|
||||
if (this.props.isLightTheme) {
|
||||
if (branding.light && branding.light.symbol) {
|
||||
if (branding.light?.symbol) {
|
||||
logoSource = branding.light.symbol
|
||||
}
|
||||
} else if (branding.dark && branding.dark.symbol) {
|
||||
} else if (branding.dark?.symbol) {
|
||||
logoSource = branding.dark.symbol
|
||||
}
|
||||
if (branding.disableSymbolSpin) {
|
||||
|
||||
@ -29,7 +29,7 @@ interface State {
|
||||
*/
|
||||
export class UserNavItem extends React.PureComponent<Props, State> {
|
||||
private supportsSystemTheme = Boolean(
|
||||
window.matchMedia && window.matchMedia('not all and (prefers-color-scheme), (prefers-color-scheme)').matches
|
||||
window.matchMedia?.('not all and (prefers-color-scheme), (prefers-color-scheme)').matches
|
||||
)
|
||||
|
||||
public state: State = { isOpen: false }
|
||||
@ -98,10 +98,9 @@ export class UserNavItem extends React.PureComponent<Props, State> {
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
{this.props.keyboardShortcutForSwitchTheme &&
|
||||
this.props.keyboardShortcutForSwitchTheme.keybindings.map((keybinding, index) => (
|
||||
<Shortcut key={index} {...keybinding} onMatch={this.onThemeCycle} />
|
||||
))}
|
||||
{this.props.keyboardShortcutForSwitchTheme?.keybindings.map((keybinding, index) => (
|
||||
<Shortcut key={index} {...keybinding} onMatch={this.onThemeCycle} />
|
||||
))}
|
||||
</div>
|
||||
{this.props.authenticatedUser.organizations.nodes.length > 0 && (
|
||||
<>
|
||||
@ -130,7 +129,7 @@ export class UserNavItem extends React.PureComponent<Props, State> {
|
||||
Help
|
||||
</Link>
|
||||
)}
|
||||
{this.props.authenticatedUser.session && this.props.authenticatedUser.session.canSignOut && (
|
||||
{this.props.authenticatedUser.session?.canSignOut && (
|
||||
<a href="/-/sign-out" className="dropdown-item">
|
||||
Sign out
|
||||
</a>
|
||||
|
||||
@ -44,7 +44,7 @@ export const OrgHeader: React.FunctionComponent<Props> = ({ org, navItems, match
|
||||
)}
|
||||
</ul>
|
||||
<div className="flex-1" />
|
||||
{org.viewerPendingInvitation && org.viewerPendingInvitation.respondURL && (
|
||||
{org.viewerPendingInvitation?.respondURL && (
|
||||
<div className="pb-1">
|
||||
<small className="mr-2">Join organization:</small>
|
||||
<Link to={org.viewerPendingInvitation.respondURL} className="btn btn-success btn-sm">
|
||||
|
||||
@ -310,30 +310,27 @@ export class InviteForm extends React.PureComponent<Props, State> {
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
{this.props.authenticatedUser &&
|
||||
this.props.authenticatedUser.siteAdmin &&
|
||||
!window.context.emailEnabled && (
|
||||
<DismissibleAlert className="alert-info" partialStorageKey="org-invite-email-config">
|
||||
<p className=" mb-0">
|
||||
Set <code>email.smtp</code> in{' '}
|
||||
<Link to="/site-admin/configuration">site configuration</Link> to send email
|
||||
notfications about invitations.
|
||||
</p>
|
||||
</DismissibleAlert>
|
||||
)}
|
||||
{this.state.invited &&
|
||||
this.state.invited.map(({ username, sentInvitationEmail, invitationURL }, index) => (
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
<InvitedNotification
|
||||
key={index}
|
||||
className="alert alert-success invite-form__alert"
|
||||
username={username}
|
||||
sentInvitationEmail={sentInvitationEmail}
|
||||
invitationURL={invitationURL}
|
||||
onDismiss={() => this.dismissNotification(index)}
|
||||
/>
|
||||
/* eslint-enable react/jsx-no-bind */
|
||||
))}
|
||||
{this.props.authenticatedUser?.siteAdmin && !window.context.emailEnabled && (
|
||||
<DismissibleAlert className="alert-info" partialStorageKey="org-invite-email-config">
|
||||
<p className=" mb-0">
|
||||
Set <code>email.smtp</code> in{' '}
|
||||
<Link to="/site-admin/configuration">site configuration</Link> to send email notfications
|
||||
about invitations.
|
||||
</p>
|
||||
</DismissibleAlert>
|
||||
)}
|
||||
{this.state.invited?.map(({ username, sentInvitationEmail, invitationURL }, index) => (
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
<InvitedNotification
|
||||
key={index}
|
||||
className="alert alert-success invite-form__alert"
|
||||
username={username}
|
||||
sentInvitationEmail={sentInvitationEmail}
|
||||
invitationURL={invitationURL}
|
||||
onDismiss={() => this.dismissNotification(index)}
|
||||
/>
|
||||
/* eslint-enable react/jsx-no-bind */
|
||||
))}
|
||||
{this.state.error && (
|
||||
<ErrorAlert className="invite-form__alert" error={this.state.error} history={this.props.history} />
|
||||
)}
|
||||
|
||||
@ -211,9 +211,7 @@ describe('Search regression test suite', () => {
|
||||
if (results.length === 0) {
|
||||
return false
|
||||
}
|
||||
const hasExcludedRepo = results.some(
|
||||
element => element.textContent && element.textContent.includes('google')
|
||||
)
|
||||
const hasExcludedRepo = results.some(element => element.textContent?.includes('google'))
|
||||
if (hasExcludedRepo) {
|
||||
throw new Error('Results contain excluded repository')
|
||||
}
|
||||
@ -467,7 +465,7 @@ describe('Search regression test suite', () => {
|
||||
this.timeout(2 * 1000)
|
||||
const response = await search(gqlClient, 'router index:no timeout:1ns', 'V2', GQL.SearchPatternType.literal)
|
||||
expect(response.results.matchCount).toBe(0)
|
||||
expect(response.results.alert && response.results.alert.title).toBe('Timed out while searching')
|
||||
expect(response.results.alert?.title).toBe('Timed out while searching')
|
||||
})
|
||||
|
||||
test('Search repo group', async () => {
|
||||
|
||||
@ -205,8 +205,8 @@ export async function getGlobalSettings(
|
||||
}
|
||||
return {
|
||||
subjectID: globalSettingsSubject.id,
|
||||
settingsID: globalSettingsSubject.latestSettings && globalSettingsSubject.latestSettings.id,
|
||||
contents: (globalSettingsSubject.latestSettings && globalSettingsSubject.latestSettings.contents) || '',
|
||||
settingsID: globalSettingsSubject.latestSettings?.id ?? null,
|
||||
contents: globalSettingsSubject.latestSettings?.contents || '',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ const Breadcrumb: React.FunctionComponent<Props> = props => {
|
||||
)
|
||||
if (index < parts.length - 1) {
|
||||
spans.push(
|
||||
<span key={'sep' + index} className="breadcrumb__separator">
|
||||
<span key={`sep${index}`} className="breadcrumb__separator">
|
||||
/
|
||||
</span>
|
||||
)
|
||||
|
||||
@ -33,7 +33,7 @@ export const GitReferenceNode: React.FunctionComponent<GitReferenceNodeProps> =
|
||||
(node.target.commit.committer && node.target.commit.committer.date > node.target.commit.author.date
|
||||
? node.target.commit.committer
|
||||
: node.target.commit.author)
|
||||
const behindAhead = node.target.commit && node.target.commit.behindAhead
|
||||
const behindAhead = node.target.commit?.behindAhead
|
||||
url = url !== undefined ? url : node.url
|
||||
|
||||
return (
|
||||
|
||||
@ -129,7 +129,7 @@ export class GoToCodeHostAction extends React.PureComponent<Props, State> {
|
||||
if (this.props.range) {
|
||||
url += `#L${this.props.range.start.line}-L${this.props.range.end.line}`
|
||||
} else if (this.props.position) {
|
||||
url += '#L' + this.props.position.line
|
||||
url += `#L${this.props.position.line}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -95,11 +95,7 @@ const TextSearchIndexedReference: React.FunctionComponent<{
|
||||
— indexed at{' '}
|
||||
<code>
|
||||
<LinkOrSpan
|
||||
to={
|
||||
indexedRef.indexedCommit && indexedRef.indexedCommit.commit
|
||||
? indexedRef.indexedCommit.commit.url
|
||||
: repo.url
|
||||
}
|
||||
to={indexedRef.indexedCommit?.commit ? indexedRef.indexedCommit.commit.url : repo.url}
|
||||
>
|
||||
{indexedRef.indexedCommit!.abbreviatedOID}
|
||||
</LinkOrSpan>
|
||||
|
||||
@ -157,7 +157,7 @@ export class SavedSearchForm extends React.Component<Props, State> {
|
||||
<div className="alert alert-warning mb-3">
|
||||
<strong>Warning:</strong> Sending emails is not currently configured on this Sourcegraph
|
||||
server.{' '}
|
||||
{this.props.authenticatedUser && this.props.authenticatedUser.siteAdmin
|
||||
{this.props.authenticatedUser?.siteAdmin
|
||||
? 'Use the email.smtp site configuration setting to enable sending emails.'
|
||||
: 'Contact your server admin for more information.'}
|
||||
</div>
|
||||
|
||||
@ -118,7 +118,7 @@ describe('search/helpers', () => {
|
||||
|
||||
test('filters suggestions for filter aliases', () => {
|
||||
const [{ value }] = filterStaticSuggestions({ query: 'l', cursorPosition: 1 }, searchFilterSuggestions)
|
||||
expect(value).toBe(filterAliases.l + ':')
|
||||
expect(value).toBe(`${filterAliases.l}:`)
|
||||
})
|
||||
|
||||
test('does not throw for query ":"', () => {
|
||||
|
||||
@ -426,10 +426,9 @@ export class QueryInput extends React.Component<Props, State> {
|
||||
)
|
||||
}}
|
||||
</Downshift>
|
||||
{this.props.keyboardShortcutForFocus &&
|
||||
this.props.keyboardShortcutForFocus.keybindings.map((keybinding, index) => (
|
||||
<Shortcut key={index} {...keybinding} onMatch={this.focusInputAndPositionCursorAtEnd} />
|
||||
))}
|
||||
{this.props.keyboardShortcutForFocus?.keybindings.map((keybinding, index) => (
|
||||
<Shortcut key={index} {...keybinding} onMatch={this.focusInputAndPositionCursorAtEnd} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import { FilterType } from '../../../../shared/src/search/interactive/util'
|
||||
import { SearchSuggestion } from '../../../../shared/src/search/suggestions'
|
||||
import { appendSubtreeQueryParameter } from '../../../../shared/src/util/url'
|
||||
|
||||
export const filterAliases: Record<string, FilterSuggestionTypes | undefined> = {
|
||||
export const filterAliases: Record<string, FilterSuggestionTypes> = {
|
||||
r: FilterType.repo,
|
||||
g: FilterType.repogroup,
|
||||
f: FilterType.file,
|
||||
|
||||
@ -202,10 +202,10 @@ export class InteractiveModeInput extends React.Component<InteractiveModeProps,
|
||||
const { branding } = window.context
|
||||
if (branding) {
|
||||
if (this.props.isLightTheme) {
|
||||
if (branding.light && branding.light.symbol) {
|
||||
if (branding.light?.symbol) {
|
||||
logoSource = branding.light.symbol
|
||||
}
|
||||
} else if (branding.dark && branding.dark.symbol) {
|
||||
} else if (branding.dark?.symbol) {
|
||||
logoSource = branding.dark.symbol
|
||||
}
|
||||
if (branding.disableSymbolSpin) {
|
||||
|
||||
@ -48,10 +48,7 @@ export const Toggles: React.FunctionComponent<TogglesProps> = (props: TogglesPro
|
||||
copyQueryButton,
|
||||
} = props
|
||||
|
||||
const structuralSearchDisabled =
|
||||
window.context &&
|
||||
window.context.experimentalFeatures &&
|
||||
window.context.experimentalFeatures.structuralSearch === 'disabled'
|
||||
const structuralSearchDisabled = window.context?.experimentalFeatures?.structuralSearch === 'disabled'
|
||||
|
||||
const submitOnToggle = useCallback(
|
||||
(args: { newPatternType: SearchPatternType } | { newCaseSensitivity: boolean }): void => {
|
||||
|
||||
@ -173,7 +173,7 @@ export const CommitSearchResult: React.FunctionComponent<Props> = (props: Props)
|
||||
|
||||
lineClasses.push({ line: index + 1, className: 'hunk-header', url: toPrettyBlobURL(rhsContext) })
|
||||
} else {
|
||||
if (rhsContext.position && rhsContext.position.line) {
|
||||
if (rhsContext.position?.line) {
|
||||
if (!line.startsWith('+')) {
|
||||
lhsContext.position.line++
|
||||
}
|
||||
|
||||
@ -154,11 +154,7 @@ export class SearchResults extends React.Component<SearchResultsProps, SearchRes
|
||||
? { mode: this.props.interactiveSearchMode ? 'interactive' : 'plain' }
|
||||
: {}),
|
||||
})
|
||||
if (
|
||||
query_data.query &&
|
||||
query_data.query.field_type &&
|
||||
query_data.query.field_type.value_diff > 0
|
||||
) {
|
||||
if (query_data.query?.field_type && query_data.query.field_type.value_diff > 0) {
|
||||
this.props.telemetryService.log('DiffSearchResultsQueried')
|
||||
}
|
||||
}),
|
||||
@ -293,7 +289,7 @@ export class SearchResults extends React.Component<SearchResultsProps, SearchRes
|
||||
public render(): JSX.Element | null {
|
||||
const query = parseSearchURLQuery(this.props.location.search)
|
||||
const filters = this.getFilters()
|
||||
const extensionFilters = this.state.contributions && this.state.contributions.searchFilters
|
||||
const extensionFilters = this.state.contributions?.searchFilters
|
||||
|
||||
const quickLinks =
|
||||
(isSettingsValid<Settings>(this.props.settingsCascade) && this.props.settingsCascade.final.quicklinks) || []
|
||||
|
||||
@ -52,7 +52,7 @@ export const SearchResultsFilterBars: React.FunctionComponent<{
|
||||
<FilterChip
|
||||
query={navbarSearchQuery}
|
||||
onFilterChosen={onFilterClick}
|
||||
key={filter.name + filter.value}
|
||||
key={String(filter.name) + filter.value}
|
||||
value={filter.value}
|
||||
name={filter.name}
|
||||
/>
|
||||
|
||||
@ -43,9 +43,9 @@ export class SettingsPage extends React.PureComponent<Props, State> {
|
||||
//
|
||||
// If the settings update is for some other subject that is unrelated to the viewer, then this is not
|
||||
// necessary.
|
||||
const isSubjectInViewerSettingsCascade =
|
||||
this.props.settingsCascade.subjects &&
|
||||
this.props.settingsCascade.subjects.some(({ subject }) => subject.id === this.props.subject.id)
|
||||
const isSubjectInViewerSettingsCascade = this.props.settingsCascade.subjects?.some(
|
||||
({ subject }) => subject.id === this.props.subject.id
|
||||
)
|
||||
|
||||
try {
|
||||
if (isSubjectInViewerSettingsCascade) {
|
||||
|
||||
@ -129,7 +129,7 @@ export class AccessTokenNode extends React.PureComponent<AccessTokenNodeProps, A
|
||||
)}{' '}
|
||||
<small className="text-muted">
|
||||
{' '}
|
||||
— <em>{this.props.node.scopes && this.props.node.scopes.join(', ')}</em>
|
||||
— <em>{this.props.node.scopes?.join(', ')}</em>
|
||||
<br />
|
||||
{this.props.node.lastUsedAt ? (
|
||||
<>
|
||||
|
||||
@ -244,7 +244,7 @@ export class SiteAdminConfigurationPage extends React.Component<Props, State> {
|
||||
.pipe(
|
||||
tap(() => this.setState({ saving: true, error: undefined })),
|
||||
concatMap(newContents => {
|
||||
const lastConfiguration = this.state.site && this.state.site.configuration
|
||||
const lastConfiguration = this.state.site?.configuration
|
||||
const lastConfigurationID = lastConfiguration?.id || 0
|
||||
|
||||
return updateSiteConfiguration(lastConfigurationID, newContents).pipe(
|
||||
@ -369,9 +369,7 @@ export class SiteAdminConfigurationPage extends React.Component<Props, State> {
|
||||
)
|
||||
}
|
||||
if (
|
||||
this.state.site &&
|
||||
this.state.site.configuration &&
|
||||
this.state.site.configuration.validationMessages &&
|
||||
this.state.site?.configuration?.validationMessages &&
|
||||
this.state.site.configuration.validationMessages.length > 0
|
||||
) {
|
||||
alerts.push(
|
||||
@ -389,8 +387,7 @@ export class SiteAdminConfigurationPage extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
// Avoid user confusion with values.yaml properties mixed in with site config properties.
|
||||
const contents =
|
||||
this.state.site && this.state.site.configuration && this.state.site.configuration.effectiveContents
|
||||
const contents = this.state.site?.configuration?.effectiveContents
|
||||
const legacyKubernetesConfigProps = [
|
||||
'alertmanagerConfig',
|
||||
'alertmanagerURL',
|
||||
@ -445,7 +442,7 @@ export class SiteAdminConfigurationPage extends React.Component<Props, State> {
|
||||
</p>
|
||||
<div className="site-admin-configuration-page__alerts">{alerts}</div>
|
||||
{this.state.loading && <LoadingSpinner className="icon-inline" />}
|
||||
{this.state.site && this.state.site.configuration && (
|
||||
{this.state.site?.configuration && (
|
||||
<div>
|
||||
<DynamicallyImportedMonacoSettingsEditor
|
||||
value={contents || ''}
|
||||
|
||||
@ -125,7 +125,7 @@ class UserSurveyResponseNode extends React.PureComponent<UserSurveyResponseNodeP
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{this.props.node.usageStatistics && this.props.node.usageStatistics.lastActiveTime ? (
|
||||
{this.props.node.usageStatistics?.lastActiveTime ? (
|
||||
<Timestamp date={this.props.node.usageStatistics.lastActiveTime} />
|
||||
) : (
|
||||
'?'
|
||||
|
||||
@ -110,7 +110,7 @@ export class SiteAdminUpdatesPage extends React.Component<Props, State> {
|
||||
<br />
|
||||
<small>
|
||||
<strong>Last update check:</strong>{' '}
|
||||
{this.state.updateCheck && this.state.updateCheck.checkedAt
|
||||
{this.state.updateCheck?.checkedAt
|
||||
? formatDistance(parseISO(this.state.updateCheck.checkedAt), new Date(), {
|
||||
addSuffix: true,
|
||||
})
|
||||
|
||||
@ -136,15 +136,14 @@ class UserUsageStatisticsNode extends React.PureComponent<UserUsageStatisticsNod
|
||||
{this.props.node.usageStatistics ? this.props.node.usageStatistics.codeIntelligenceActions : 'n/a'}
|
||||
</td>
|
||||
<td className="site-admin-usage-statistics-page__date-column">
|
||||
{this.props.node.usageStatistics && this.props.node.usageStatistics.lastActiveTime ? (
|
||||
{this.props.node.usageStatistics?.lastActiveTime ? (
|
||||
<Timestamp date={this.props.node.usageStatistics.lastActiveTime} />
|
||||
) : (
|
||||
'never'
|
||||
)}
|
||||
</td>
|
||||
<td className="site-admin-usage-statistics-page__date-column">
|
||||
{this.props.node.usageStatistics &&
|
||||
this.props.node.usageStatistics.lastActiveCodeHostIntegrationTime ? (
|
||||
{this.props.node.usageStatistics?.lastActiveCodeHostIntegrationTime ? (
|
||||
<Timestamp date={this.props.node.usageStatistics.lastActiveCodeHostIntegrationTime} />
|
||||
) : (
|
||||
'never'
|
||||
|
||||
@ -446,9 +446,7 @@ export function fetchAllConfigAndSettings(): Observable<AllConfig> {
|
||||
const finalSettings = parseJSONC(data.viewerSettings.final)
|
||||
return {
|
||||
site:
|
||||
data.site &&
|
||||
data.site.configuration &&
|
||||
data.site.configuration.effectiveContents &&
|
||||
data.site?.configuration?.effectiveContents &&
|
||||
parseJSONC(data.site.configuration.effectiveContents),
|
||||
externalServices,
|
||||
settings: {
|
||||
|
||||
@ -38,7 +38,7 @@ export class EventLogger implements TelemetryService {
|
||||
* Page titles should be specific and human-readable in pascal case, e.g. "SearchResults" or "Blob" or "NewOrg"
|
||||
*/
|
||||
public logViewEvent(pageTitle: string, logAsActiveUser = true): void {
|
||||
if ((window.context && window.context.userAgentIsBot) || !pageTitle) {
|
||||
if (window.context?.userAgentIsBot || !pageTitle) {
|
||||
return
|
||||
}
|
||||
pageTitle = `View${pageTitle}`
|
||||
@ -59,7 +59,7 @@ export class EventLogger implements TelemetryService {
|
||||
* Event labels should be specific and follow a ${noun}${verb} structure in pascal case, e.g. "ButtonClicked" or "SignInInitiated"
|
||||
*/
|
||||
public log(eventLabel: string, eventProperties?: any): void {
|
||||
if ((window.context && window.context.userAgentIsBot) || !eventLabel) {
|
||||
if (window.context?.userAgentIsBot || !eventLabel) {
|
||||
return
|
||||
}
|
||||
serverAdmin.trackAction(eventLabel, eventProperties)
|
||||
|
||||
@ -213,7 +213,7 @@ export class TreeLayer extends React.Component<TreeLayerProps, TreeLayerState> {
|
||||
|
||||
this.componentUpdates.next(this.props)
|
||||
|
||||
const isDirectory = this.props.entryInfo && this.props.entryInfo.isDirectory
|
||||
const isDirectory = this.props.entryInfo?.isDirectory
|
||||
// When scrolling through the tree with the keyboard, if we hover a child tree node, prefetch its children.
|
||||
if (this.node === this.props.selectedNode && isDirectory && this.props.onHover) {
|
||||
this.props.onHover(this.node.path)
|
||||
|
||||
@ -82,5 +82,5 @@ function gitTreeToTreeObject(entry: TreeEntryInfo): SingleChildGitTree {
|
||||
|
||||
/** Determines whether a Tree has single-child directories as children, in order to determine whether to render a SingleChildTreeLayer or TreeLayer */
|
||||
export function hasSingleChild(tree: TreeEntryInfo[]): boolean {
|
||||
return tree[0] && tree[0].isSingleChild
|
||||
return tree[0]?.isSingleChild
|
||||
}
|
||||
|
||||
@ -161,21 +161,19 @@ export class UserSettingsProfilePage extends React.Component<Props, State> {
|
||||
<PageTitle title="Profile" />
|
||||
<h2>Profile</h2>
|
||||
|
||||
{this.props.activation &&
|
||||
this.props.activation.completed &&
|
||||
percentageDone(this.props.activation.completed) < 100 && (
|
||||
<div className="card mb-3">
|
||||
<div className="card-body">
|
||||
<h3 className="mb-0">Almost there!</h3>
|
||||
<p className="mb-0">Complete the steps below to finish onboarding to Sourcegraph.</p>
|
||||
</div>
|
||||
<ActivationChecklist
|
||||
history={this.props.history}
|
||||
steps={this.props.activation.steps}
|
||||
completed={this.props.activation.completed}
|
||||
/>
|
||||
{this.props.activation?.completed && percentageDone(this.props.activation.completed) < 100 && (
|
||||
<div className="card mb-3">
|
||||
<div className="card-body">
|
||||
<h3 className="mb-0">Almost there!</h3>
|
||||
<p className="mb-0">Complete the steps below to finish onboarding to Sourcegraph.</p>
|
||||
</div>
|
||||
)}
|
||||
<ActivationChecklist
|
||||
history={this.props.history}
|
||||
steps={this.props.activation.steps}
|
||||
completed={this.props.activation.completed}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isErrorLike(this.state.userOrError) && (
|
||||
<ErrorAlert error={this.state.userOrError.message} history={this.props.history} />
|
||||
|
||||
67
yarn.lock
67
yarn.lock
@ -1355,6 +1355,13 @@
|
||||
"@gql2ts/util" "^1.9.0"
|
||||
humps "^2.0.0"
|
||||
|
||||
"@gql2ts/types@^1.9.0":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.npmjs.org/@gql2ts/types/-/types-1.9.0.tgz#f6725aa2beda33b6f1948e046be260e34d0b3405"
|
||||
integrity sha512-1gtNzOMczIBS+ZnA3wizxOhLgy3lxfUVydQVMeGCe8kGRRI51BQzJKOza39OsIB9Fmwc6psHyamk/Z8RllTksA==
|
||||
dependencies:
|
||||
"@gql2ts/util" "^1.9.0"
|
||||
|
||||
"@gql2ts/util@^1.9.0":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.npmjs.org/@gql2ts/util/-/util-1.9.0.tgz#d07a54832757d2f2d1fc9891e5b0e3e3b4886c6a"
|
||||
@ -2155,14 +2162,14 @@
|
||||
rxjs "^6.5.5"
|
||||
ts-key-enum "^2.0.0"
|
||||
|
||||
"@sourcegraph/eslint-config@^0.19.2":
|
||||
version "0.19.2"
|
||||
resolved "https://registry.npmjs.org/@sourcegraph/eslint-config/-/eslint-config-0.19.2.tgz#d1950d5010f1993043fd1080f72a86cfb8ccc6a3"
|
||||
integrity sha512-GedJzZd/cpnyS26Vn1bQIc5VdZjQeVE69uodYJODuwZDwP+xzFTvbURpK8phNWrC4UOdsK7tUGVe/WR7Mtsvrg==
|
||||
"@sourcegraph/eslint-config@^0.19.3":
|
||||
version "0.19.3"
|
||||
resolved "https://registry.npmjs.org/@sourcegraph/eslint-config/-/eslint-config-0.19.3.tgz#5cbb6a8ebf355affae3f6d596cea20468076d762"
|
||||
integrity sha512-1xtIEa4UXQ8gBcZkQ8MeHxstIM9M7IKJ4uAGnVJEKl6ytB7+12HUdmP0MNB7pZdrTDp/Q4x3cCqs1FeINRMRoQ==
|
||||
dependencies:
|
||||
"@sourcegraph/prettierrc" "^3.0.3"
|
||||
"@typescript-eslint/eslint-plugin" "^2.34.0"
|
||||
"@typescript-eslint/parser" "^2.34.0"
|
||||
"@typescript-eslint/eslint-plugin" "^3.0.2"
|
||||
"@typescript-eslint/parser" "^3.0.2"
|
||||
eslint-config-prettier "^6.11.0"
|
||||
eslint-plugin-ban "^1.4.0"
|
||||
eslint-plugin-etc "^0.0.1-beta.25"
|
||||
@ -2195,7 +2202,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"
|
||||
@ -3817,40 +3825,42 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^2.34.0":
|
||||
version "2.34.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9"
|
||||
integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==
|
||||
"@typescript-eslint/eslint-plugin@^3.0.2":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.4.0.tgz#8378062e6be8a1d049259bdbcf27ce5dfbeee62b"
|
||||
integrity sha512-wfkpiqaEVhZIuQRmudDszc01jC/YR7gMSxa6ulhggAe/Hs0KVIuo9wzvFiDbG3JD5pRFQoqnf4m7REDsUvBnMQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "2.34.0"
|
||||
"@typescript-eslint/experimental-utils" "3.4.0"
|
||||
debug "^4.1.1"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^3.0.0"
|
||||
semver "^7.3.2"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/experimental-utils@2.34.0":
|
||||
version "2.34.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f"
|
||||
integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==
|
||||
"@typescript-eslint/experimental-utils@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.4.0.tgz#8a44dfc6fb7f1d071937b390fe27608ebda122b8"
|
||||
integrity sha512-rHPOjL43lOH1Opte4+dhC0a/+ks+8gOBwxXnyrZ/K4OTAChpSjP76fbI8Cglj7V5GouwVAGaK+xVwzqTyE/TPw==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/typescript-estree" "2.34.0"
|
||||
"@typescript-eslint/typescript-estree" "3.4.0"
|
||||
eslint-scope "^5.0.0"
|
||||
eslint-utils "^2.0.0"
|
||||
|
||||
"@typescript-eslint/parser@^2.34.0":
|
||||
version "2.34.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8"
|
||||
integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==
|
||||
"@typescript-eslint/parser@^3.0.2":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.4.0.tgz#fe52b68c5cb3bba3f5d875bd17adb70420d49d8d"
|
||||
integrity sha512-ZUGI/de44L5x87uX5zM14UYcbn79HSXUR+kzcqU42gH0AgpdB/TjuJy3m4ezI7Q/jk3wTQd755mxSDLhQP79KA==
|
||||
dependencies:
|
||||
"@types/eslint-visitor-keys" "^1.0.0"
|
||||
"@typescript-eslint/experimental-utils" "2.34.0"
|
||||
"@typescript-eslint/typescript-estree" "2.34.0"
|
||||
"@typescript-eslint/experimental-utils" "3.4.0"
|
||||
"@typescript-eslint/typescript-estree" "3.4.0"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.34.0":
|
||||
version "2.34.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5"
|
||||
integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==
|
||||
"@typescript-eslint/typescript-estree@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.4.0.tgz#6a787eb70b48969e4cd1ea67b057083f96dfee29"
|
||||
integrity sha512-zKwLiybtt4uJb4mkG5q2t6+W7BuYx2IISiDNV+IY68VfoGwErDx/RfVI7SWL4gnZ2t1A1ytQQwZ+YOJbHHJ2rw==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
@ -19610,7 +19620,8 @@ source-map@^0.7.3:
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
||||
"sourcegraph@link:packages/sourcegraph-extension-api":
|
||||
version "24.5.0"
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
space-separated-tokens@^1.0.0:
|
||||
version "1.1.2"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user