@@ -8,10 +8,6 @@ import * as React from 'react';
88import { injectIntl } from 'react-intl' ;
99import type { IntlShape } from 'react-intl' ;
1010import noop from 'lodash/noop' ;
11- // $FlowFixMe
12- import { BoxAiLogo } from '@box/blueprint-web-assets/icons/Logo' ;
13- // $FlowFixMe
14- import { Size6 } from '@box/blueprint-web-assets/tokens/tokens' ;
1511import { usePromptFocus } from '@box/box-ai-content-answers' ;
1612import AdditionalTabs from './additional-tabs' ;
1713import DocGenIcon from '../../icon/fill/DocGenIcon' ;
@@ -33,19 +29,18 @@ import {
3329 SIDEBAR_VIEW_METADATA ,
3430 SIDEBAR_VIEW_SKILLS ,
3531} from '../../constants' ;
36- import { useFeatureConfig } from '../common/feature-checking' ;
37- import type { NavigateOptions , AdditionalSidebarTab } from './flowTypes' ;
32+ import type { NavigateOptions , AdditionalSidebarTab , CustomSidebarPanel } from './flowTypes' ;
3833import type { InternalSidebarNavigation , InternalSidebarNavigationHandler } from '../common/types/SidebarNavigation' ;
3934import './SidebarNav.scss' ;
4035import type { SignSidebarProps } from './SidebarNavSign' ;
4136
4237type Props = {
4338 additionalTabs ?: Array < AdditionalSidebarTab > ,
39+ customTabs ?: Array < CustomSidebarPanel > ,
4440 elementId : string ,
4541 fileId : string ,
4642 hasActivity : boolean ,
4743 hasAdditionalTabs : boolean ,
48- hasBoxAI : boolean ,
4944 hasDetails : boolean ,
5045 hasDocGen ?: boolean ,
5146 hasMetadata : boolean ,
@@ -62,11 +57,11 @@ type Props = {
6257
6358const SidebarNav = ( {
6459 additionalTabs,
60+ customTabs,
6561 elementId,
6662 fileId,
6763 hasActivity,
6864 hasAdditionalTabs,
69- hasBoxAI,
7065 hasDetails,
7166 hasMetadata,
7267 hasSkills,
@@ -81,8 +76,6 @@ const SidebarNav = ({
8176 signSidebarProps,
8277} : Props ) => {
8378 const { enabled : hasBoxSign } = signSidebarProps || { } ;
84- const { disabledTooltip : boxAIDisabledTooltip , showOnlyNavButton : showOnlyBoxAINavButton } =
85- useFeatureConfig ( 'boxai.sidebar' ) ;
8679
8780 const { focusPrompt } = usePromptFocus ( '.be.bcs' ) ;
8881
@@ -94,6 +87,139 @@ const SidebarNav = ({
9487 focusPrompt ( ) ;
9588 }
9689 } ;
90+ const boxAiTab = customTabs ? customTabs . find ( tab => tab . id === SIDEBAR_VIEW_BOXAI ) : undefined ;
91+ const otherCustomTabs = customTabs ? customTabs . filter ( tab => tab . id !== SIDEBAR_VIEW_BOXAI ) : [ ] ;
92+ const hasOtherCustomTabs = otherCustomTabs . length > 0 ;
93+
94+ const sidebarTabs = [
95+ boxAiTab && (
96+ < SidebarNavButton
97+ key = { boxAiTab . id }
98+ data-target-id = { `SidebarNavButton-${ boxAiTab . id } ` }
99+ data-testid = { `sidebar${ boxAiTab . id } ` }
100+ { ...boxAiTab . navButtonProps }
101+ data-resin-target = { SIDEBAR_NAV_TARGETS . BOXAI }
102+ isDisabled = { boxAiTab . isDisabled }
103+ onClick = { handleSidebarNavButtonClick }
104+ sidebarView = { boxAiTab . path }
105+ tooltip = { boxAiTab . title ?? boxAiTab . id }
106+ >
107+ { boxAiTab . icon &&
108+ ( React . isValidElement ( boxAiTab . icon ) ? (
109+ boxAiTab . icon
110+ ) : (
111+ // $FlowFixMe: Flow doesn't understand dynamic component creation
112+ < boxAiTab . icon className = "bcs-SidebarNav-icon" />
113+ ) ) }
114+ </ SidebarNavButton >
115+ ) ,
116+ hasActivity && (
117+ < SidebarNavButton
118+ key = { SIDEBAR_VIEW_ACTIVITY }
119+ data-resin-target = { SIDEBAR_NAV_TARGETS . ACTIVITY }
120+ data-target-id = "SidebarNavButton-activity"
121+ data-testid = "sidebaractivity"
122+ onClick = { handleSidebarNavButtonClick }
123+ sidebarView = { SIDEBAR_VIEW_ACTIVITY }
124+ tooltip = { intl . formatMessage ( messages . sidebarActivityTitle ) }
125+ >
126+ < IconChatRound className = "bcs-SidebarNav-icon" />
127+ </ SidebarNavButton >
128+ ) ,
129+ hasDetails && (
130+ < SidebarNavButton
131+ key = { SIDEBAR_VIEW_DETAILS }
132+ data-resin-target = { SIDEBAR_NAV_TARGETS . DETAILS }
133+ data-target-id = "SidebarNavButton-details"
134+ data-testid = "sidebardetails"
135+ onClick = { handleSidebarNavButtonClick }
136+ sidebarView = { SIDEBAR_VIEW_DETAILS }
137+ tooltip = { intl . formatMessage ( messages . sidebarDetailsTitle ) }
138+ >
139+ < IconDocInfo className = "bcs-SidebarNav-icon" />
140+ </ SidebarNavButton >
141+ ) ,
142+ hasSkills && (
143+ < SidebarNavButton
144+ key = { SIDEBAR_VIEW_SKILLS }
145+ data-resin-target = { SIDEBAR_NAV_TARGETS . SKILLS }
146+ data-target-id = "SidebarNavButton-skills"
147+ data-testid = "sidebarskills"
148+ onClick = { handleSidebarNavButtonClick }
149+ sidebarView = { SIDEBAR_VIEW_SKILLS }
150+ tooltip = { intl . formatMessage ( messages . sidebarSkillsTitle ) }
151+ >
152+ < IconMagicWand className = "bcs-SidebarNav-icon" />
153+ </ SidebarNavButton >
154+ ) ,
155+ hasMetadata && (
156+ < SidebarNavButton
157+ key = { SIDEBAR_VIEW_METADATA }
158+ data-resin-target = { SIDEBAR_NAV_TARGETS . METADATA }
159+ data-target-id = "SidebarNavButton-metadata"
160+ data-testid = "sidebarmetadata"
161+ onClick = { handleSidebarNavButtonClick }
162+ sidebarView = { SIDEBAR_VIEW_METADATA }
163+ tooltip = { intl . formatMessage ( messages . sidebarMetadataTitle ) }
164+ >
165+ < IconMetadataThick className = "bcs-SidebarNav-icon" />
166+ </ SidebarNavButton >
167+ ) ,
168+ hasDocGen && (
169+ < SidebarNavButton
170+ key = { SIDEBAR_VIEW_DOCGEN }
171+ data-resin-target = { SIDEBAR_NAV_TARGETS . DOCGEN }
172+ data-target-id = "SidebarNavButton-docgen"
173+ data-testid = "sidebardocgen"
174+ onClick = { handleSidebarNavButtonClick }
175+ sidebarView = { SIDEBAR_VIEW_DOCGEN }
176+ tooltip = { intl . formatMessage ( messages . sidebarDocGenTooltip ) }
177+ >
178+ < DocGenIcon className = "bcs-SidebarNav-icon" />
179+ </ SidebarNavButton >
180+ ) ,
181+ ] ;
182+
183+ // Filter out falsy values first
184+ const visibleTabs = sidebarTabs . filter ( Boolean ) ;
185+
186+ // Insert custom tabs - box-ai goes at the top, others at the end
187+ if ( hasOtherCustomTabs ) {
188+ // Add other custom tabs at the end
189+ otherCustomTabs . forEach ( customTab => {
190+ const {
191+ id : customTabId ,
192+ path : customTabPath ,
193+ icon : CustomTabIcon ,
194+ title : customTabTitle ,
195+ navButtonProps,
196+ } = customTab ;
197+
198+ const customTabButton = (
199+ < SidebarNavButton
200+ key = { customTabId }
201+ data-resin-target = { `sidebar${ customTabId } ` }
202+ data-target-id = { `SidebarNavButton-${ customTabId } ` }
203+ data-testid = { `sidebar${ customTabId } ` }
204+ { ...navButtonProps }
205+ isDisabled = { customTab . isDisabled }
206+ onClick = { handleSidebarNavButtonClick }
207+ sidebarView = { customTabPath }
208+ tooltip = { customTabTitle ?? customTabId }
209+ >
210+ { CustomTabIcon &&
211+ ( React . isValidElement ( CustomTabIcon ) ? (
212+ CustomTabIcon
213+ ) : (
214+ // $FlowFixMe: Flow doesn't understand dynamic component creation
215+ < CustomTabIcon className = "bcs-SidebarNav-icon" />
216+ ) ) }
217+ </ SidebarNavButton >
218+ ) ;
219+
220+ visibleTabs . push ( customTabButton ) ; // Add at the end
221+ } ) ;
222+ }
97223
98224 return (
99225 < div className = "bcs-SidebarNav" aria-label = { intl . formatMessage ( messages . sidebarNavLabel ) } >
@@ -106,83 +232,7 @@ const SidebarNav = ({
106232 onNavigate = { onNavigate }
107233 routerDisabled = { routerDisabled }
108234 >
109- { hasBoxAI && (
110- < SidebarNavButton
111- data-resin-target = { SIDEBAR_NAV_TARGETS . BOXAI }
112- data-target-id = "SidebarNavButton-boxAI"
113- data-testid = "sidebarboxai"
114- isDisabled = { showOnlyBoxAINavButton }
115- onClick = { handleSidebarNavButtonClick }
116- sidebarView = { SIDEBAR_VIEW_BOXAI }
117- tooltip = {
118- showOnlyBoxAINavButton
119- ? boxAIDisabledTooltip
120- : intl . formatMessage ( messages . sidebarBoxAITitle )
121- }
122- >
123- < BoxAiLogo height = { Size6 } width = { Size6 } />
124- </ SidebarNavButton >
125- ) }
126- { hasActivity && (
127- < SidebarNavButton
128- data-resin-target = { SIDEBAR_NAV_TARGETS . ACTIVITY }
129- data-target-id = "SidebarNavButton-activity"
130- data-testid = "sidebaractivity"
131- onClick = { handleSidebarNavButtonClick }
132- sidebarView = { SIDEBAR_VIEW_ACTIVITY }
133- tooltip = { intl . formatMessage ( messages . sidebarActivityTitle ) }
134- >
135- < IconChatRound className = "bcs-SidebarNav-icon" />
136- </ SidebarNavButton >
137- ) }
138- { hasDetails && (
139- < SidebarNavButton
140- data-resin-target = { SIDEBAR_NAV_TARGETS . DETAILS }
141- data-target-id = "SidebarNavButton-details"
142- data-testid = "sidebardetails"
143- onClick = { handleSidebarNavButtonClick }
144- sidebarView = { SIDEBAR_VIEW_DETAILS }
145- tooltip = { intl . formatMessage ( messages . sidebarDetailsTitle ) }
146- >
147- < IconDocInfo className = "bcs-SidebarNav-icon" />
148- </ SidebarNavButton >
149- ) }
150- { hasSkills && (
151- < SidebarNavButton
152- data-resin-target = { SIDEBAR_NAV_TARGETS . SKILLS }
153- data-target-id = "SidebarNavButton-skills"
154- data-testid = "sidebarskills"
155- onClick = { handleSidebarNavButtonClick }
156- sidebarView = { SIDEBAR_VIEW_SKILLS }
157- tooltip = { intl . formatMessage ( messages . sidebarSkillsTitle ) }
158- >
159- < IconMagicWand className = "bcs-SidebarNav-icon" />
160- </ SidebarNavButton >
161- ) }
162- { hasMetadata && (
163- < SidebarNavButton
164- data-resin-target = { SIDEBAR_NAV_TARGETS . METADATA }
165- data-target-id = "SidebarNavButton-metadata"
166- data-testid = "sidebarmetadata"
167- onClick = { handleSidebarNavButtonClick }
168- sidebarView = { SIDEBAR_VIEW_METADATA }
169- tooltip = { intl . formatMessage ( messages . sidebarMetadataTitle ) }
170- >
171- < IconMetadataThick className = "bcs-SidebarNav-icon" />
172- </ SidebarNavButton >
173- ) }
174- { hasDocGen && (
175- < SidebarNavButton
176- data-resin-target = { SIDEBAR_NAV_TARGETS . DOCGEN }
177- data-target-id = "SidebarNavButton-docGen"
178- data-testid = "sidebardocgen"
179- onClick = { handleSidebarNavButtonClick }
180- sidebarView = { SIDEBAR_VIEW_DOCGEN }
181- tooltip = { intl . formatMessage ( messages . sidebarDocGenTooltip ) }
182- >
183- < DocGenIcon className = "bcs-SidebarNav-icon" />
184- </ SidebarNavButton >
185- ) }
235+ { visibleTabs }
186236 </ SidebarNavTablist >
187237
188238 { hasBoxSign && (
0 commit comments