Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/elements/content-sidebar/ContentSidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import type { WithLoggerProps } from '../../common/types/logging';
import type { ElementsXhrError, RequestOptions, ErrorContextProps } from '../../common/types/api';
import type { MetadataEditor } from '../../common/types/metadata';
import type { StringMap, Token, User, BoxItem } from '../../common/types/core';
import type { AdditionalSidebarTab } from './flowTypes';
import type { AdditionalSidebarTab, CustomSidebarPanel } from './flowTypes';
import type { FeatureConfig } from '../common/feature-checking';
// $FlowFixMe TypeScript file
import type { Theme } from '../common/theming';
Expand All @@ -66,6 +66,7 @@ type Props = {
className: string,
clientName: string,
currentUser?: User,
customSidebarPanels?: Array<CustomSidebarPanel>,
defaultView: string,
detailsSidebarProps: DetailsSidebarProps,
docGenSidebarProps?: DocGenSidebarProps,
Expand Down Expand Up @@ -354,6 +355,7 @@ class ContentSidebar extends React.Component<Props, State> {
boxAISidebarProps,
className,
currentUser,
customSidebarPanels,
defaultView,
detailsSidebarProps,
docGenSidebarProps,
Expand Down Expand Up @@ -399,6 +401,7 @@ class ContentSidebar extends React.Component<Props, State> {
boxAISidebarProps={boxAISidebarProps}
className={className}
currentUser={currentUser}
customSidebarPanels={customSidebarPanels}
detailsSidebarProps={detailsSidebarProps}
docGenSidebarProps={docGenSidebarProps}
file={file}
Expand Down
14 changes: 9 additions & 5 deletions src/elements/content-sidebar/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ import type { DocGenSidebarProps } from './DocGenSidebar/DocGenSidebar';
import type { MetadataSidebarProps } from './MetadataSidebar';
import type { BoxAISidebarProps } from './BoxAISidebar';
import type { VersionsSidebarProps } from './versions';
import type { AdditionalSidebarTab } from './flowTypes';
import type { AdditionalSidebarTab, CustomSidebarPanel } from './flowTypes';
import type { MetadataEditor } from '../../common/types/metadata';
import type { BoxItem, User } from '../../common/types/core';
import type { SignSidebarProps } from './SidebarNavSign';
import type { Errors } from '../common/flowTypes';
// $FlowFixMe TypeScript file
import type { Theme } from '../common/theming';
import { SIDEBAR_VIEW_DOCGEN } from '../../constants';
import { SIDEBAR_VIEW_DOCGEN, SIDEBAR_VIEW_BOXAI } from '../../constants';
import API from '../../api';

type Props = {
Expand All @@ -46,6 +46,7 @@ type Props = {
className: string,
currentUser?: User,
currentUserError?: Errors,
customSidebarPanels?: Array<CustomSidebarPanel>,
detailsSidebarProps: DetailsSidebarProps,
docGenSidebarProps: DocGenSidebarProps,
features: FeatureConfig,
Expand Down Expand Up @@ -297,6 +298,7 @@ class Sidebar extends React.Component<Props, State> {
className,
currentUser,
currentUserError,
customSidebarPanels,
detailsSidebarProps,
docGenSidebarProps,
file,
Expand All @@ -316,12 +318,14 @@ class Sidebar extends React.Component<Props, State> {
versionsSidebarProps,
}: Props = this.props;
const isOpen = this.isOpen();
const hasBoxAI = SidebarUtils.canHaveBoxAISidebar(this.props);
const hasActivity = SidebarUtils.canHaveActivitySidebar(this.props);
const hasDetails = SidebarUtils.canHaveDetailsSidebar(this.props);
const hasMetadata = SidebarUtils.shouldRenderMetadataSidebar(this.props, metadataEditors);
const hasSkills = SidebarUtils.shouldRenderSkillsSidebar(this.props, file);
const onVersionHistoryClick = hasVersions ? this.handleVersionHistoryClick : this.props.onVersionHistoryClick;
const hasBoxAI = customSidebarPanels
? !!customSidebarPanels.find(panel => panel.id === SIDEBAR_VIEW_BOXAI)
: false;
const styleClassName = classNames('be bcs', className, {
'bcs-is-open': isOpen,
'bcs-is-wider': hasBoxAI,
Expand All @@ -340,11 +344,11 @@ class Sidebar extends React.Component<Props, State> {
{hasNav && (
<SidebarNav
additionalTabs={additionalTabs}
customTabs={customSidebarPanels}
elementId={this.id}
fileId={fileId}
hasActivity={hasActivity}
hasAdditionalTabs={hasAdditionalTabs}
hasBoxAI={hasBoxAI}
hasDetails={hasDetails}
hasMetadata={hasMetadata}
hasSkills={hasSkills}
Expand All @@ -359,6 +363,7 @@ class Sidebar extends React.Component<Props, State> {
boxAISidebarProps={boxAISidebarProps}
currentUser={currentUser}
currentUserError={currentUserError}
customPanels={customSidebarPanels}
elementId={this.id}
defaultPanel={defaultPanel}
detailsSidebarProps={detailsSidebarProps}
Expand All @@ -368,7 +373,6 @@ class Sidebar extends React.Component<Props, State> {
getPreview={getPreview}
getViewer={getViewer}
hasActivity={hasActivity}
hasBoxAI={hasBoxAI}
hasDetails={hasDetails}
hasDocGen={docGenSidebarProps.isDocGenTemplate}
hasMetadata={hasMetadata}
Expand Down
224 changes: 137 additions & 87 deletions src/elements/content-sidebar/SidebarNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import * as React from 'react';
import { injectIntl } from 'react-intl';
import type { IntlShape } from 'react-intl';
import noop from 'lodash/noop';
// $FlowFixMe
import { BoxAiLogo } from '@box/blueprint-web-assets/icons/Logo';
// $FlowFixMe
import { Size6 } from '@box/blueprint-web-assets/tokens/tokens';
import { usePromptFocus } from '@box/box-ai-content-answers';
import AdditionalTabs from './additional-tabs';
import DocGenIcon from '../../icon/fill/DocGenIcon';
Expand All @@ -33,19 +29,18 @@ import {
SIDEBAR_VIEW_METADATA,
SIDEBAR_VIEW_SKILLS,
} from '../../constants';
import { useFeatureConfig } from '../common/feature-checking';
import type { NavigateOptions, AdditionalSidebarTab } from './flowTypes';
import type { NavigateOptions, AdditionalSidebarTab, CustomSidebarPanel } from './flowTypes';
import type { InternalSidebarNavigation, InternalSidebarNavigationHandler } from '../common/types/SidebarNavigation';
import './SidebarNav.scss';
import type { SignSidebarProps } from './SidebarNavSign';

type Props = {
additionalTabs?: Array<AdditionalSidebarTab>,
customTabs?: Array<CustomSidebarPanel>,
elementId: string,
fileId: string,
hasActivity: boolean,
hasAdditionalTabs: boolean,
hasBoxAI: boolean,
hasDetails: boolean,
hasDocGen?: boolean,
hasMetadata: boolean,
Expand All @@ -62,11 +57,11 @@ type Props = {

const SidebarNav = ({
additionalTabs,
customTabs,
elementId,
fileId,
hasActivity,
hasAdditionalTabs,
hasBoxAI,
hasDetails,
hasMetadata,
hasSkills,
Expand All @@ -81,8 +76,6 @@ const SidebarNav = ({
signSidebarProps,
}: Props) => {
const { enabled: hasBoxSign } = signSidebarProps || {};
const { disabledTooltip: boxAIDisabledTooltip, showOnlyNavButton: showOnlyBoxAINavButton } =
useFeatureConfig('boxai.sidebar');

const { focusPrompt } = usePromptFocus('.be.bcs');

Expand All @@ -94,6 +87,139 @@ const SidebarNav = ({
focusPrompt();
}
};
const boxAiTab = customTabs ? customTabs.find(tab => tab.id === SIDEBAR_VIEW_BOXAI) : undefined;
const otherCustomTabs = customTabs ? customTabs.filter(tab => tab.id !== SIDEBAR_VIEW_BOXAI) : [];
const hasOtherCustomTabs = otherCustomTabs.length > 0;

const sidebarTabs = [
boxAiTab && (
<SidebarNavButton
key={boxAiTab.id}
data-target-id={`SidebarNavButton-${boxAiTab.id}`}
data-testid={`sidebar${boxAiTab.id}`}
{...boxAiTab.navButtonProps}
data-resin-target={SIDEBAR_NAV_TARGETS.BOXAI}
isDisabled={boxAiTab.isDisabled}
onClick={handleSidebarNavButtonClick}
sidebarView={boxAiTab.path}
tooltip={boxAiTab.title ?? boxAiTab.id}
>
{boxAiTab.icon &&
(React.isValidElement(boxAiTab.icon) ? (
boxAiTab.icon
) : (
// $FlowFixMe: Flow doesn't understand dynamic component creation
<boxAiTab.icon className="bcs-SidebarNav-icon" />
))}
</SidebarNavButton>
),
hasActivity && (
<SidebarNavButton
key={SIDEBAR_VIEW_ACTIVITY}
data-resin-target={SIDEBAR_NAV_TARGETS.ACTIVITY}
data-target-id="SidebarNavButton-activity"
data-testid="sidebaractivity"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_ACTIVITY}
tooltip={intl.formatMessage(messages.sidebarActivityTitle)}
>
<IconChatRound className="bcs-SidebarNav-icon" />
</SidebarNavButton>
),
hasDetails && (
<SidebarNavButton
key={SIDEBAR_VIEW_DETAILS}
data-resin-target={SIDEBAR_NAV_TARGETS.DETAILS}
data-target-id="SidebarNavButton-details"
data-testid="sidebardetails"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_DETAILS}
tooltip={intl.formatMessage(messages.sidebarDetailsTitle)}
>
<IconDocInfo className="bcs-SidebarNav-icon" />
</SidebarNavButton>
),
hasSkills && (
<SidebarNavButton
key={SIDEBAR_VIEW_SKILLS}
data-resin-target={SIDEBAR_NAV_TARGETS.SKILLS}
data-target-id="SidebarNavButton-skills"
data-testid="sidebarskills"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_SKILLS}
tooltip={intl.formatMessage(messages.sidebarSkillsTitle)}
>
<IconMagicWand className="bcs-SidebarNav-icon" />
</SidebarNavButton>
),
hasMetadata && (
<SidebarNavButton
key={SIDEBAR_VIEW_METADATA}
data-resin-target={SIDEBAR_NAV_TARGETS.METADATA}
data-target-id="SidebarNavButton-metadata"
data-testid="sidebarmetadata"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_METADATA}
tooltip={intl.formatMessage(messages.sidebarMetadataTitle)}
>
<IconMetadataThick className="bcs-SidebarNav-icon" />
</SidebarNavButton>
),
hasDocGen && (
<SidebarNavButton
key={SIDEBAR_VIEW_DOCGEN}
data-resin-target={SIDEBAR_NAV_TARGETS.DOCGEN}
data-target-id="SidebarNavButton-docgen"
data-testid="sidebardocgen"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_DOCGEN}
tooltip={intl.formatMessage(messages.sidebarDocGenTooltip)}
>
<DocGenIcon className="bcs-SidebarNav-icon" />
</SidebarNavButton>
),
];

// Filter out falsy values first
const visibleTabs = sidebarTabs.filter(Boolean);

// Insert custom tabs - box-ai goes at the top, others at the end
if (hasOtherCustomTabs) {
// Add other custom tabs at the end
otherCustomTabs.forEach(customTab => {
const {
id: customTabId,
path: customTabPath,
icon: CustomTabIcon,
title: customTabTitle,
navButtonProps,
} = customTab;

const customTabButton = (
<SidebarNavButton
key={customTabId}
data-resin-target={`sidebar${customTabId}`}
data-target-id={`SidebarNavButton-${customTabId}`}
data-testid={`sidebar${customTabId}`}
{...navButtonProps}
isDisabled={customTab.isDisabled}
onClick={handleSidebarNavButtonClick}
sidebarView={customTabPath}
tooltip={customTabTitle ?? customTabId}
>
{CustomTabIcon &&
(React.isValidElement(CustomTabIcon) ? (
CustomTabIcon
) : (
// $FlowFixMe: Flow doesn't understand dynamic component creation
<CustomTabIcon className="bcs-SidebarNav-icon" />
))}
</SidebarNavButton>
);

visibleTabs.push(customTabButton); // Add at the end
});
}

return (
<div className="bcs-SidebarNav" aria-label={intl.formatMessage(messages.sidebarNavLabel)}>
Expand All @@ -106,83 +232,7 @@ const SidebarNav = ({
onNavigate={onNavigate}
routerDisabled={routerDisabled}
>
{hasBoxAI && (
<SidebarNavButton
data-resin-target={SIDEBAR_NAV_TARGETS.BOXAI}
data-target-id="SidebarNavButton-boxAI"
data-testid="sidebarboxai"
isDisabled={showOnlyBoxAINavButton}
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_BOXAI}
tooltip={
showOnlyBoxAINavButton
? boxAIDisabledTooltip
: intl.formatMessage(messages.sidebarBoxAITitle)
}
>
<BoxAiLogo height={Size6} width={Size6} />
</SidebarNavButton>
)}
{hasActivity && (
<SidebarNavButton
data-resin-target={SIDEBAR_NAV_TARGETS.ACTIVITY}
data-target-id="SidebarNavButton-activity"
data-testid="sidebaractivity"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_ACTIVITY}
tooltip={intl.formatMessage(messages.sidebarActivityTitle)}
>
<IconChatRound className="bcs-SidebarNav-icon" />
</SidebarNavButton>
)}
{hasDetails && (
<SidebarNavButton
data-resin-target={SIDEBAR_NAV_TARGETS.DETAILS}
data-target-id="SidebarNavButton-details"
data-testid="sidebardetails"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_DETAILS}
tooltip={intl.formatMessage(messages.sidebarDetailsTitle)}
>
<IconDocInfo className="bcs-SidebarNav-icon" />
</SidebarNavButton>
)}
{hasSkills && (
<SidebarNavButton
data-resin-target={SIDEBAR_NAV_TARGETS.SKILLS}
data-target-id="SidebarNavButton-skills"
data-testid="sidebarskills"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_SKILLS}
tooltip={intl.formatMessage(messages.sidebarSkillsTitle)}
>
<IconMagicWand className="bcs-SidebarNav-icon" />
</SidebarNavButton>
)}
{hasMetadata && (
<SidebarNavButton
data-resin-target={SIDEBAR_NAV_TARGETS.METADATA}
data-target-id="SidebarNavButton-metadata"
data-testid="sidebarmetadata"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_METADATA}
tooltip={intl.formatMessage(messages.sidebarMetadataTitle)}
>
<IconMetadataThick className="bcs-SidebarNav-icon" />
</SidebarNavButton>
)}
{hasDocGen && (
<SidebarNavButton
data-resin-target={SIDEBAR_NAV_TARGETS.DOCGEN}
data-target-id="SidebarNavButton-docGen"
data-testid="sidebardocgen"
onClick={handleSidebarNavButtonClick}
sidebarView={SIDEBAR_VIEW_DOCGEN}
tooltip={intl.formatMessage(messages.sidebarDocGenTooltip)}
>
<DocGenIcon className="bcs-SidebarNav-icon" />
</SidebarNavButton>
)}
{visibleTabs}
</SidebarNavTablist>

{hasBoxSign && (
Expand Down
Loading