various OrgArea fixes (#63962)

- Make it a React function component (not class componnt)
- Remove prop drilling for isSourcegraphDotCom to remove a bunch of code
- fix issue in Back button after clicking to an org from the site admin
orgs page. Repro: 1. Go to /site-admin/organizations. 2. Click on an
org. 3. Click the browser's back button. This was already being done for
the user routes.

## Test plan

Repro described above
This commit is contained in:
Quinn Slack 2024-07-20 20:45:32 -07:00 committed by GitHub
parent 341bfe749f
commit 30c0184f25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 150 additions and 260 deletions

View File

@ -46,7 +46,6 @@ const BatchChangeClosePage = lazyComponent<BatchChangeClosePageProps, 'BatchChan
interface Props extends TelemetryProps, TelemetryV2Props, SettingsCascadeProps {
authenticatedUser: AuthenticatedUser | null
isSourcegraphDotCom: boolean
}
/**
@ -54,9 +53,9 @@ interface Props extends TelemetryProps, TelemetryV2Props, SettingsCascadeProps {
*/
export const GlobalBatchChangesArea: React.FunctionComponent<React.PropsWithChildren<Props>> = ({
authenticatedUser,
isSourcegraphDotCom,
...props
}) => {
const isSourcegraphDotCom = Boolean(window.context?.sourcegraphDotComMode)
const canCreate: true | string = useMemo(() => {
if (isSourcegraphDotCom) {
return NO_ACCESS_SOURCEGRAPH_COM
@ -77,7 +76,6 @@ export const GlobalBatchChangesArea: React.FunctionComponent<React.PropsWithChil
headingElement="h1"
canCreate={canCreate}
authenticatedUser={authenticatedUser}
isSourcegraphDotCom={isSourcegraphDotCom}
{...props}
/>
}

View File

@ -1,5 +1,5 @@
import type { Decorator, StoryFn, Meta } from '@storybook/react'
import { WildcardMockLink, MATCH_ANY_PARAMETERS } from 'wildcard-mock-link'
import type { Decorator, Meta, StoryFn } from '@storybook/react'
import { MATCH_ANY_PARAMETERS, WildcardMockLink } from 'wildcard-mock-link'
import { getDocumentNode } from '@sourcegraph/http-client'
import { EMPTY_SETTINGS_CASCADE } from '@sourcegraph/shared/src/settings/settings'
@ -11,10 +11,10 @@ import { WebStory } from '../../../components/WebStory'
import type { GlobalChangesetsStatsResult } from '../../../graphql-operations'
import {
GLOBAL_CHANGESETS_STATS,
BATCH_CHANGES,
BATCH_CHANGES_BY_NAMESPACE,
GET_LICENSE_AND_USAGE_INFO,
GLOBAL_CHANGESETS_STATS,
} from './backend'
import { BatchChangeListPage } from './BatchChangeListPage'
import {
@ -103,7 +103,6 @@ export const ListOfBatchChanges: StoryFn<Args> = args => {
headingElement="h1"
canCreate={args.canCreate || "You don't have permission to create batch changes"}
settingsCascade={EMPTY_SETTINGS_CASCADE}
isSourcegraphDotCom={args.isDotCom}
authenticatedUser={null}
telemetryRecorder={noOpTelemetryRecorder}
/>
@ -142,7 +141,6 @@ export const ListOfBatchChangesSpecificNamespace: StoryFn = () => {
canCreate={true}
namespaceID="test-12345"
settingsCascade={EMPTY_SETTINGS_CASCADE}
isSourcegraphDotCom={false}
authenticatedUser={null}
telemetryRecorder={noOpTelemetryRecorder}
/>
@ -171,7 +169,6 @@ export const ListOfBatchChangesServerSideExecutionEnabled: StoryFn = () => {
experimentalFeatures: { batchChangesExecution: true },
},
}}
isSourcegraphDotCom={false}
authenticatedUser={null}
telemetryRecorder={noOpTelemetryRecorder}
/>
@ -195,7 +192,6 @@ export const LicensingNotEnforced: StoryFn = () => {
headingElement="h1"
canCreate={true}
settingsCascade={EMPTY_SETTINGS_CASCADE}
isSourcegraphDotCom={false}
authenticatedUser={null}
telemetryRecorder={noOpTelemetryRecorder}
/>
@ -219,7 +215,6 @@ export const NoBatchChanges: StoryFn = () => {
headingElement="h1"
canCreate={true}
settingsCascade={EMPTY_SETTINGS_CASCADE}
isSourcegraphDotCom={false}
authenticatedUser={null}
telemetryRecorder={noOpTelemetryRecorder}
/>
@ -244,7 +239,6 @@ export const AllBatchChangesTabEmpty: StoryFn = () => {
canCreate={true}
openTab="batchChanges"
settingsCascade={EMPTY_SETTINGS_CASCADE}
isSourcegraphDotCom={false}
authenticatedUser={null}
telemetryRecorder={noOpTelemetryRecorder}
/>

View File

@ -57,7 +57,6 @@ export interface BatchChangeListPageProps extends TelemetryProps, TelemetryV2Pro
canCreate: true | string
headingElement: 'h1' | 'h2'
namespaceID?: Scalars['ID']
isSourcegraphDotCom: boolean
authenticatedUser: AuthenticatedUser | null
/** For testing only. */
openTab?: SelectedTab
@ -78,7 +77,6 @@ export const BatchChangeListPage: React.FunctionComponent<React.PropsWithChildre
settingsCascade,
telemetryService,
telemetryRecorder,
isSourcegraphDotCom,
authenticatedUser,
}) => {
const location = useLocation()
@ -92,6 +90,7 @@ export const BatchChangeListPage: React.FunctionComponent<React.PropsWithChildre
const canUseBatchChanges = !!window.context.licenseInfo?.batchChanges
const { selectedFilters, setSelectedFilters, availableFilters } = useBatchChangeListFilters({ isExecutionEnabled })
const isSourcegraphDotCom = Boolean(window.context?.sourcegraphDotComMode)
const [selectedTab, setSelectedTab] = useState<SelectedTab>(
openTab ?? (isSourcegraphDotCom || !canUseBatchChanges ? 'gettingStarted' : 'batchChanges')
)

View File

@ -1,4 +1,4 @@
import { type PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { AuthenticatedUser } from '../auth'
import type { BatchChangesProps } from '../batches'
@ -11,13 +11,10 @@ import type { NamespaceProps } from '.'
*/
export interface NamespaceAreaContext extends PlatformContextProps, NamespaceProps {
authenticatedUser: AuthenticatedUser | null
isSourcegraphDotCom: boolean
}
export interface NamespaceAreaRoute extends RouteV6Descriptor<NamespaceAreaContext> {}
interface NavItemDescriptorContext extends BatchChangesProps {
isSourcegraphDotCom: boolean
}
interface NavItemDescriptorContext extends BatchChangesProps {}
export interface NamespaceAreaNavItem extends NavItemWithIconDescriptor<NavItemDescriptorContext> {}

View File

@ -22,7 +22,7 @@ export const namespaceAreaRoutes: readonly NamespaceAreaRoute[] = [
const SavedSearchesRedirect: FunctionComponent<NamespaceProps> = ({ namespace }) => {
const navigate = useNavigate()
useEffect(() => {
navigate(urlToSavedSearchesList(namespace.id))
navigate(urlToSavedSearchesList(namespace.id), { replace: true })
}, [navigate, namespace.id])
return null
}

View File

@ -1,6 +1,6 @@
import * as React from 'react'
import { Routes, Route, useParams, useLocation, useNavigate } from 'react-router-dom'
import { Route, Routes } from 'react-router-dom'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
@ -9,10 +9,10 @@ import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetry
import type { AuthenticatedUser } from '../auth'
import { withAuthenticatedUser } from '../auth/withAuthenticatedUser'
import type { BatchChangesProps } from '../batches'
import type { BreadcrumbsProps, BreadcrumbSetters } from '../components/Breadcrumbs'
import type { BreadcrumbSetters, BreadcrumbsProps } from '../components/Breadcrumbs'
import { NotFoundPage } from '../components/HeroPage'
import { OrgArea, type OrgAreaProps, type OrgAreaRoute } from './area/OrgArea'
import { OrgArea, type OrgAreaRoute } from './area/OrgArea'
import type { OrgAreaHeaderNavItem } from './area/OrgHeader'
import { OrgInvitationPage } from './invitations/OrgInvitationPage'
import { NewOrganizationPage } from './new/NewOrganizationPage'
@ -32,7 +32,6 @@ export interface Props
orgSettingsAreaRoutes: readonly OrgSettingsAreaRoute[]
authenticatedUser: AuthenticatedUser
isSourcegraphDotCom: boolean
}
/**
@ -40,28 +39,17 @@ export interface Props
*/
const AuthenticatedOrgsArea: React.FunctionComponent<React.PropsWithChildren<Props>> = props => (
<Routes>
{(!props.isSourcegraphDotCom || props.authenticatedUser.siteAdmin) && (
<Route
path="new"
element={<NewOrganizationPage telemetryRecorder={props.platformContext.telemetryRecorder} />}
/>
)}
<Route
path="new"
element={<NewOrganizationPage telemetryRecorder={props.platformContext.telemetryRecorder} />}
/>
<Route
path="invitation/:token"
element={<OrgInvitationPage {...props} telemetryRecorder={props.platformContext.telemetryRecorder} />}
/>
<Route path=":orgName/*" element={<OrgAreaWithRouteProps {...props} />} />
<Route path=":orgName/*" element={<OrgArea {...props} />} />
<Route path="*" element={<NotFoundPage pageType="organization" />} />
</Routes>
)
// TODO: Migrate this into the OrgArea component once it's migrated to a function component.
function OrgAreaWithRouteProps(props: Omit<OrgAreaProps, 'orgName' | 'location' | 'navigate'>): JSX.Element {
const { orgName } = useParams<{ orgName: string }>()
const location = useLocation()
const navigate = useNavigate()
return <OrgArea {...props} orgName={orgName!} location={location} navigate={navigate} />
}
export const OrgsArea = withAuthenticatedUser(AuthenticatedOrgsArea)

View File

@ -1,14 +1,10 @@
import * as React from 'react'
import React, { Suspense, useMemo } from 'react'
import type * as H from 'history'
import AlertCircleIcon from 'mdi-react/AlertCircleIcon'
import MapSearchIcon from 'mdi-react/MapSearchIcon'
import { Route, Routes, type NavigateFunction } from 'react-router-dom'
import { Subject, Subscription, combineLatest, merge, of, type Observable } from 'rxjs'
import { catchError, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators'
import { Route, Routes, useLocation, useNavigate, useParams } from 'react-router-dom'
import { asError, isErrorLike, logger, type ErrorLike } from '@sourcegraph/common'
import { dataOrThrowErrors, gql } from '@sourcegraph/http-client'
import { gql, useQuery } from '@sourcegraph/http-client'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry'
@ -16,68 +12,20 @@ import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetry
import { ErrorMessage, LoadingSpinner } from '@sourcegraph/wildcard'
import type { AuthenticatedUser } from '../../auth'
import { requestGraphQL } from '../../backend/graphql'
import type { BatchChangesProps } from '../../batches'
import type { BreadcrumbSetters, BreadcrumbsProps } from '../../components/Breadcrumbs'
import { RouteError } from '../../components/ErrorBoundary'
import { HeroPage } from '../../components/HeroPage'
import { Page } from '../../components/Page'
import type { OrgAreaOrganizationFields, OrganizationResult, OrganizationVariables } from '../../graphql-operations'
import type { OrgAreaOrganizationFields } from '../../graphql-operations'
import type { NamespaceProps } from '../../namespaces'
import type { RouteV6Descriptor } from '../../util/contributions'
import type { OrgSettingsAreaRoute } from '../settings/OrgSettingsArea'
import type { OrgSettingsSidebarItems } from '../settings/OrgSettingsSidebar'
import { OrgHeader, type OrgAreaHeaderNavItem } from './OrgHeader'
import { type OrgAreaHeaderNavItem, OrgHeader } from './OrgHeader'
import { OrgInvitationPageLegacy } from './OrgInvitationPageLegacy'
function queryOrganization(args: { name: string }): Observable<OrgAreaOrganizationFields> {
return requestGraphQL<OrganizationResult, OrganizationVariables>(
gql`
query Organization($name: String!) {
organization(name: $name) {
...OrgAreaOrganizationFields
}
}
fragment OrgAreaOrganizationFields on Org {
__typename
id
name
displayName
url
settingsURL
viewerPendingInvitation {
id
sender {
username
displayName
avatarURL
createdAt
}
respondURL
}
viewerIsMember
viewerCanAdminister
createdAt
}
`,
args
).pipe(
map(dataOrThrowErrors),
map(data => {
if (!data.organization) {
throw new Error(`Organization not found: ${JSON.stringify(args.name)}`)
}
return data.organization
})
)
}
const NotFoundPage: React.FunctionComponent<React.PropsWithChildren<unknown>> = () => (
<HeroPage icon={MapSearchIcon} title="404: Not Found" subtitle="Sorry, the requested organization was not found." />
)
export interface OrgAreaRoute extends RouteV6Descriptor<OrgAreaRouteContext> {
/** When true, the header is not rendered and the component is not wrapped in a container. */
fullPage?: boolean
@ -99,19 +47,41 @@ export interface OrgAreaProps
* The currently authenticated user.
*/
authenticatedUser: AuthenticatedUser
isSourcegraphDotCom: boolean
location: H.Location
navigate: NavigateFunction
orgName: string
}
interface State extends BreadcrumbSetters {
/**
* The fetched org or an error if an error occurred; undefined while loading.
*/
orgOrError?: OrgAreaOrganizationFields | ErrorLike
}
const ORGANIZATION_QUERY = gql`
query Organization($name: String!) {
organization(name: $name) {
...OrgAreaOrganizationFields
}
}
fragment OrgAreaOrganizationFields on Org {
__typename
id
name
displayName
url
settingsURL
viewerPendingInvitation {
id
sender {
username
displayName
avatarURL
createdAt
}
respondURL
}
viewerIsMember
viewerCanAdminister
createdAt
}
`
const NotFoundPage: React.FunctionComponent<React.PropsWithChildren<unknown>> = () => (
<HeroPage icon={MapSearchIcon} title="404: Not Found" subtitle="Sorry, the requested organization was not found." />
)
/**
* Properties passed to all page components in the org area.
@ -134,164 +104,116 @@ export interface OrgAreaRouteContext
/** The currently authenticated user. */
authenticatedUser: AuthenticatedUser
isSourcegraphDotCom: boolean
orgSettingsSideBarItems: OrgSettingsSidebarItems
orgSettingsAreaRoutes: readonly OrgSettingsAreaRoute[]
}
/**
* An organization's public profile area.
*/
export class OrgArea extends React.Component<OrgAreaProps> {
public state: State
private componentUpdates = new Subject<OrgAreaProps>()
private refreshRequests = new Subject<void>()
private subscriptions = new Subscription()
constructor(props: OrgAreaProps) {
super(props)
this.state = {
setBreadcrumb: props.setBreadcrumb,
useBreadcrumb: props.useBreadcrumb,
}
export const OrgArea: React.FunctionComponent<OrgAreaProps> = ({
orgAreaRoutes,
orgAreaHeaderNavItems,
orgSettingsSideBarItems,
orgSettingsAreaRoutes,
authenticatedUser,
useBreadcrumb,
...props
}) => {
const { orgName } = useParams<{ orgName: string }>()
if (!orgName) {
throw new Error('orgName is required')
}
public componentDidMount(): void {
// Changes to the route-matched org name.
const nameChanges = this.componentUpdates.pipe(
map(props => props.orgName),
distinctUntilChanged()
const navigate = useNavigate()
const location = useLocation()
const { data, error, loading, refetch } = useQuery(ORGANIZATION_QUERY, {
variables: { name: orgName },
})
const childBreadcrumbSetters = useBreadcrumb(
useMemo(
() =>
data?.organization
? {
key: 'OrgArea',
link: { to: data.organization.url, label: data.organization.name },
}
: null,
[data]
)
)
// Fetch organization.
this.subscriptions.add(
combineLatest([nameChanges, merge(this.refreshRequests.pipe(map(() => false)), of(true))])
.pipe(
switchMap(([name, forceRefresh]) => {
type PartialStateUpdate = Pick<State, 'orgOrError'>
return queryOrganization({ name }).pipe(
catchError((error): [ErrorLike] => [asError(error)]),
map((orgOrError): PartialStateUpdate => ({ orgOrError })),
// Don't clear old org data while we reload, to avoid unmounting all components during
// loading.
startWith<PartialStateUpdate>(forceRefresh ? { orgOrError: undefined } : {})
)
})
)
.subscribe(
stateUpdate => {
if (stateUpdate.orgOrError && !isErrorLike(stateUpdate.orgOrError)) {
const childBreadcrumbSetters = this.props.setBreadcrumb({
key: 'OrgArea',
link: { to: stateUpdate.orgOrError.url, label: stateUpdate.orgOrError.name },
})
this.subscriptions.add(childBreadcrumbSetters)
this.setState({
useBreadcrumb: childBreadcrumbSetters.useBreadcrumb,
setBreadcrumb: childBreadcrumbSetters.setBreadcrumb,
orgOrError: stateUpdate.orgOrError,
})
} else {
this.setState(stateUpdate)
}
},
error => logger.error(error)
)
)
this.componentUpdates.next(this.props)
if (loading && !data) {
return <LoadingSpinner className="m-2" />
}
public componentDidUpdate(): void {
this.componentUpdates.next(this.props)
if (error) {
return <HeroPage icon={AlertCircleIcon} title="Error" subtitle={<ErrorMessage error={error} />} />
}
public componentWillUnmount(): void {
this.subscriptions.unsubscribe()
if (!data?.organization) {
return <NotFoundPage />
}
public render(): JSX.Element | null {
if (!this.state.orgOrError) {
return null // loading
}
if (isErrorLike(this.state.orgOrError)) {
return (
<HeroPage
icon={AlertCircleIcon}
title="Error"
subtitle={<ErrorMessage error={this.state.orgOrError} />}
/>
)
}
const context: OrgAreaRouteContext = {
authenticatedUser: this.props.authenticatedUser,
org: this.state.orgOrError,
onOrganizationUpdate: this.onDidUpdateOrganization,
platformContext: this.props.platformContext,
settingsCascade: this.props.settingsCascade,
namespace: this.state.orgOrError,
telemetryService: this.props.telemetryService,
telemetryRecorder: this.props.platformContext.telemetryRecorder,
isSourcegraphDotCom: this.props.isSourcegraphDotCom,
batchChangesEnabled: this.props.batchChangesEnabled,
batchChangesExecutionEnabled: this.props.batchChangesExecutionEnabled,
batchChangesWebhookLogsEnabled: this.props.batchChangesWebhookLogsEnabled,
breadcrumbs: this.props.breadcrumbs,
setBreadcrumb: this.state.setBreadcrumb,
useBreadcrumb: this.state.useBreadcrumb,
orgSettingsAreaRoutes: this.props.orgSettingsAreaRoutes,
orgSettingsSideBarItems: this.props.orgSettingsSideBarItems,
}
if (this.props.location.pathname === `/organizations/${this.props.orgName}/invitation`) {
// The OrgInvitationPageLegacy is displayed without the OrgHeader because it is modal-like.
return <OrgInvitationPageLegacy {...context} onDidRespondToInvitation={this.onDidRespondToInvitation} />
}
return (
<React.Suspense fallback={<LoadingSpinner className="m-2" />}>
<Routes>
{this.props.orgAreaRoutes.map(
({ path, render, condition = () => true, fullPage }) =>
condition(context) && (
<Route
path={path}
key="hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
errorElement={<RouteError />}
element={
fullPage ? (
render(context)
) : (
<Page className="org-area">
<OrgHeader
{...this.props}
{...context}
navItems={this.props.orgAreaHeaderNavItems}
className="mb-3"
/>
<div className="container">{render(context)}</div>
</Page>
)
}
/>
)
)}
<Route path="*" element={<NotFoundPage />} />
</Routes>
</React.Suspense>
)
const context: OrgAreaRouteContext = {
authenticatedUser,
org: data.organization,
onOrganizationUpdate: refetch,
platformContext: props.platformContext,
settingsCascade: props.settingsCascade,
namespace: data.organization,
telemetryService: props.telemetryService,
telemetryRecorder: props.platformContext.telemetryRecorder,
batchChangesEnabled: props.batchChangesEnabled,
batchChangesExecutionEnabled: props.batchChangesExecutionEnabled,
batchChangesWebhookLogsEnabled: props.batchChangesWebhookLogsEnabled,
breadcrumbs: props.breadcrumbs,
...childBreadcrumbSetters,
orgSettingsAreaRoutes,
orgSettingsSideBarItems,
}
private onDidRespondToInvitation = (accepted: boolean): void => {
const handleRespondToInvitation = (accepted: boolean): void => {
if (!accepted) {
this.props.navigate('/user/settings')
navigate('/user/settings')
return
}
this.refreshRequests.next()
refetch()
}
private onDidUpdateOrganization = (): void => this.refreshRequests.next()
if (location.pathname === `/organizations/${orgName}/invitation`) {
return <OrgInvitationPageLegacy {...context} onDidRespondToInvitation={handleRespondToInvitation} />
}
return (
<Suspense fallback={<LoadingSpinner className="m-2" />}>
<Routes>
{orgAreaRoutes.map(
({ path, render, condition = () => true, fullPage }) =>
condition(context) && (
<Route
path={path}
key="hardcoded-key"
errorElement={<RouteError />}
element={
fullPage ? (
render(context)
) : (
<Page className="org-area">
<OrgHeader
{...props}
{...context}
navItems={orgAreaHeaderNavItems}
className="mb-3"
/>
<div className="container">{render(context)}</div>
</Page>
)
}
/>
)
)}
<Route path="*" element={<NotFoundPage />} />
</Routes>
</Suspense>
)
}

View File

@ -11,14 +11,11 @@ import { OrgAvatar } from '../OrgAvatar'
import type { OrgAreaRouteContext } from './OrgArea'
interface Props extends OrgAreaRouteContext {
isSourcegraphDotCom: boolean
navItems: readonly OrgAreaHeaderNavItem[]
className?: string
}
export interface OrgAreaHeaderContext extends BatchChangesProps, Pick<Props, 'org'> {
isSourcegraphDotCom: boolean
}
export interface OrgAreaHeaderContext extends BatchChangesProps, Pick<Props, 'org'> {}
export interface OrgAreaHeaderNavItem extends NavItemWithIconDescriptor<OrgAreaHeaderContext> {}
@ -32,14 +29,12 @@ export const OrgHeader: React.FunctionComponent<React.PropsWithChildren<Props>>
org,
navItems,
className = '',
isSourcegraphDotCom,
}) => {
const context: OrgAreaHeaderContext = {
batchChangesEnabled,
batchChangesExecutionEnabled,
batchChangesWebhookLogsEnabled,
org,
isSourcegraphDotCom,
}
const url = `/organizations/${org.name}`

View File

@ -51,12 +51,12 @@ export const orgAreaRoutes: readonly OrgAreaRoute[] = [
// Redirect from /organizations/:orgname -> /organizations/:orgname/settings/profile.
{
path: '',
render: () => <Navigate to="./settings/profile" />,
render: () => <Navigate to="./settings/profile" replace={true} />,
},
// Redirect from previous /organizations/:orgname/account -> /organizations/:orgname/settings/profile.
{
path: 'account',
render: () => <Navigate to="../settings/profile" />,
render: () => <Navigate to="../settings/profile" replace={true} />,
},
{

View File

@ -19,7 +19,6 @@ import styles from './OrgSettingsSidebar.module.scss'
export interface OrgSettingsSidebarItemConditionContext extends BatchChangesProps {
org: OrgAreaOrganizationFields
authenticatedUser: AuthenticatedUser
isSourcegraphDotCom: boolean
}
type OrgSettingsSidebarItem = NavItemDescriptor<OrgSettingsSidebarItemConditionContext> & {
@ -30,7 +29,6 @@ export type OrgSettingsSidebarItems = readonly OrgSettingsSidebarItem[]
export interface OrgSettingsSidebarProps extends OrgSettingsAreaRouteContext, BatchChangesProps {
items: OrgSettingsSidebarItems
isSourcegraphDotCom: boolean
className?: string
}
@ -53,7 +51,6 @@ export const OrgSettingsSidebar: React.FunctionComponent<React.PropsWithChildren
batchChangesWebhookLogsEnabled: props.batchChangesWebhookLogsEnabled,
org,
authenticatedUser,
isSourcegraphDotCom: props.isSourcegraphDotCom,
}
return (

View File

@ -38,8 +38,8 @@ export const orgSettingsAreaRoutes: readonly OrgSettingsAreaRoute[] = [
telemetryRecorder={props.platformContext.telemetryRecorder}
/>
),
condition: ({ org: { viewerCanAdminister } }) =>
viewerCanAdminister && window.context?.codeSearchEnabledOnInstance,
condition: ({ batchChangesEnabled, org: { viewerCanAdminister } }) =>
batchChangesEnabled && viewerCanAdminister && window.context?.codeSearchEnabledOnInstance,
},
]