From fc9710eba6ab3091c38300c65d65f4c75810ef2e Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Mon, 23 Sep 2024 12:36:43 +0200 Subject: [PATCH 01/14] feat(boxai-sidebar): PR fixes and Storybook test added --- src/constants.js | 2 + src/elements/common/flowTypes.js | 2 + src/elements/common/interactionTargets.js | 1 + src/elements/common/messages.js | 5 ++ src/elements/content-sidebar/BoxAISidebar.js | 53 +++++++++++++++++++ .../content-sidebar/ContentSidebar.js | 9 ++++ src/elements/content-sidebar/Sidebar.js | 8 +++ src/elements/content-sidebar/SidebarNav.js | 15 ++++++ src/elements/content-sidebar/SidebarPanels.js | 40 +++++++++++++- src/elements/content-sidebar/SidebarUtils.js | 19 +++++++ .../content-sidebar/__mocks__/SidebarUtils.js | 5 ++ .../__tests__/SidebarNav.test.js | 22 +++++++- .../__tests__/SidebarPanels.test.js | 16 +++--- .../__tests__/SidebarUtils.test.js | 25 +++++++++ .../__snapshots__/SidebarUtils.test.js.snap | 7 +++ .../content-sidebar/stories/BoxAISideBar.mdx | 5 ++ .../stories/BoxAISidebar.stories.tsx | 15 ++++++ .../stories/ContentSidebar.mdx | 1 + .../stories/ContentSidebar.stories.js | 1 + .../tests/BoxAISidebar-visual.stories.tsx | 23 ++++++++ 20 files changed, 265 insertions(+), 9 deletions(-) create mode 100644 src/elements/content-sidebar/BoxAISidebar.js create mode 100644 src/elements/content-sidebar/stories/BoxAISideBar.mdx create mode 100644 src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx create mode 100644 src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx diff --git a/src/constants.js b/src/constants.js index 1561373151..322e2a6d89 100644 --- a/src/constants.js +++ b/src/constants.js @@ -318,6 +318,7 @@ export const ERROR_CODE_UNKNOWN = 'unknown_error'; export const ORIGIN_CONTENT_PREVIEW: 'content_preview' = 'content_preview'; export const ORIGIN_CONTENT_SIDEBAR: 'content_sidebar' = 'content_sidebar'; export const ORIGIN_ACTIVITY_SIDEBAR: 'activity_sidebar' = 'activity_sidebar'; +export const ORIGIN_BOXAI_SIDEBAR: 'boxai_sidebar' = 'boxai_sidebar'; export const ORIGIN_DETAILS_SIDEBAR: 'details_sidebar' = 'details_sidebar'; export const ORIGIN_DOCGEN_SIDEBAR: 'docgen_sidebar' = 'docgen_sidebar'; export const ORIGIN_METADATA_SIDEBAR: 'metadata_sidebar' = 'metadata_sidebar'; @@ -404,6 +405,7 @@ export const SIDEBAR_VIEW_SKILLS: 'skills' = 'skills'; export const SIDEBAR_VIEW_DETAILS: 'details' = 'details'; export const SIDEBAR_VIEW_METADATA: 'metadata' = 'metadata'; export const SIDEBAR_VIEW_METADATA_REDESIGN: 'metadata_redesign' = 'metadata_redesign'; +export const SIDEBAR_VIEW_BOXAI: 'boxai' = 'boxai'; export const SIDEBAR_VIEW_ACTIVITY: 'activity' = 'activity'; export const SIDEBAR_VIEW_VERSIONS: 'versions' = 'versions'; export const SIDEBAR_VIEW_DOCGEN: 'docgen' = 'docgen'; diff --git a/src/elements/common/flowTypes.js b/src/elements/common/flowTypes.js index 0a52f7c3fc..b24bb84a8c 100644 --- a/src/elements/common/flowTypes.js +++ b/src/elements/common/flowTypes.js @@ -7,6 +7,7 @@ import { ORIGIN_PREVIEW, ORIGIN_CONTENT_PREVIEW, ORIGIN_DETAILS_SIDEBAR, + ORIGIN_BOXAI_SIDEBAR, ORIGIN_ACTIVITY_SIDEBAR, ORIGIN_SKILLS_SIDEBAR, ORIGIN_METADATA_SIDEBAR, @@ -42,6 +43,7 @@ type ElementOrigin = | typeof ORIGIN_CONTENT_PREVIEW | typeof ORIGIN_PREVIEW | typeof ORIGIN_DETAILS_SIDEBAR + | typeof ORIGIN_BOXAI_SIDEBAR | typeof ORIGIN_ACTIVITY_SIDEBAR | typeof ORIGIN_SKILLS_SIDEBAR | typeof ORIGIN_METADATA_SIDEBAR diff --git a/src/elements/common/interactionTargets.js b/src/elements/common/interactionTargets.js index 82112c9027..01fa90a978 100644 --- a/src/elements/common/interactionTargets.js +++ b/src/elements/common/interactionTargets.js @@ -1,5 +1,6 @@ // @flow strict export const SIDEBAR_NAV_TARGETS = { + BOXAI: 'sidebarboxai', ACTIVITY: 'sidebaractivity', DETAILS: 'sidebardetails', SIGN: 'sidebarsign', diff --git a/src/elements/common/messages.js b/src/elements/common/messages.js index f975e96f29..329eb1b589 100644 --- a/src/elements/common/messages.js +++ b/src/elements/common/messages.js @@ -452,6 +452,11 @@ const messages = defineMessages({ description: 'Generic error content for skills editing.', defaultMessage: 'An error has occurred while updating skills. Please refresh the page and try again.', }, + sidebarBoxAITitle: { + id: 'be.sidebarBoxAITitle', + description: 'Title for the preview Box AI feed.', + defaultMessage: 'Box AI', + }, sidebarActivityTitle: { id: 'be.sidebarActivityTitle', description: 'Title for the preview activity feed.', diff --git a/src/elements/content-sidebar/BoxAISidebar.js b/src/elements/content-sidebar/BoxAISidebar.js new file mode 100644 index 0000000000..c78e614f7b --- /dev/null +++ b/src/elements/content-sidebar/BoxAISidebar.js @@ -0,0 +1,53 @@ +/** + * @flow + * @file BoxAI sidebar component + */ + +import * as React from 'react'; +import flow from 'lodash/flow'; +import { FormattedMessage } from 'react-intl'; +import messages from '../common/messages'; +import SidebarContent from './SidebarContent'; +import { EVENT_JS_READY } from '../common/logger/constants'; +import { mark } from '../../utils/performance'; +import { withAPIContext } from '../common/api-context'; +import { withErrorBoundary } from '../common/error-boundary'; +import { withLogger } from '../common/logger'; +import { ORIGIN_BOXAI_SIDEBAR, SIDEBAR_VIEW_BOXAI } from '../../constants'; + +type Props = { + onExpandPressed: () => void, + logger: any, +}; + +const MARK_NAME_JS_READY = `${ORIGIN_BOXAI_SIDEBAR}_${EVENT_JS_READY}`; + +mark(MARK_NAME_JS_READY); + +class BoxAISidebar extends React.PureComponent { + constructor(props: Props) { + super(props); + const { logger } = this.props; + logger.onReadyMetric({ + endMarkName: MARK_NAME_JS_READY, + }); + } + + render() { + return ( + } + > +
+
+ ); + } +} + +export type BoxAISidebarProps = Props; +export { BoxAISidebar as BoxAISidebarComponent }; +export default flow([withLogger(ORIGIN_BOXAI_SIDEBAR), withErrorBoundary(ORIGIN_BOXAI_SIDEBAR), withAPIContext])( + BoxAISidebar, +); diff --git a/src/elements/content-sidebar/ContentSidebar.js b/src/elements/content-sidebar/ContentSidebar.js index a2858a13c4..4cc1f998ef 100644 --- a/src/elements/content-sidebar/ContentSidebar.js +++ b/src/elements/content-sidebar/ContentSidebar.js @@ -45,6 +45,7 @@ import '../common/fonts.scss'; import '../common/base.scss'; import '../common/modal.scss'; import './ContentSidebar.scss'; +import type { BoxAISidebarProps } from './BoxAISidebar'; type Props = { activitySidebarProps: ActivitySidebarProps, @@ -61,6 +62,8 @@ type Props = { fileId?: string, getPreview: Function, getViewer: Function, + hasBoxAI: boolean, + boxAISidebarProps: BoxAISidebarProps, hasActivityFeed: boolean, hasAdditionalTabs: boolean, hasMetadata: boolean, @@ -114,6 +117,8 @@ class ContentSidebar extends React.Component { docGenSidebarProps: { enabled: false }, getPreview: noop, getViewer: noop, + hasBoxAI: false, + boxAISidebarProps: {}, hasActivityFeed: false, hasAdditionalTabs: false, hasMetadata: false, @@ -342,6 +347,8 @@ class ContentSidebar extends React.Component { getPreview, getViewer, hasAdditionalTabs, + hasBoxAI, + boxAISidebarProps, hasActivityFeed, hasMetadata, hasNav, @@ -380,6 +387,8 @@ class ContentSidebar extends React.Component { fileId={fileId} getPreview={getPreview} getViewer={getViewer} + hasBoxAI={hasBoxAI} + boxAISidebarProps={boxAISidebarProps} hasActivityFeed={hasActivityFeed} hasAdditionalTabs={hasAdditionalTabs} hasNav={hasNav} diff --git a/src/elements/content-sidebar/Sidebar.js b/src/elements/content-sidebar/Sidebar.js index 09d2ac7a27..d66f3e282c 100644 --- a/src/elements/content-sidebar/Sidebar.js +++ b/src/elements/content-sidebar/Sidebar.js @@ -24,6 +24,7 @@ import type { ActivitySidebarProps } from './ActivitySidebar'; import type { DetailsSidebarProps } from './DetailsSidebar'; import type { DocGenSidebarProps } from './DocGenSidebar/DocGenSidebar'; import type { MetadataSidebarProps } from './MetadataSidebar'; +import type { BoxAISidebarProps } from './BoxAISidebarProps'; import type { VersionsSidebarProps } from './versions'; import type { AdditionalSidebarTab } from './flowTypes'; import type { MetadataEditor } from '../../common/types/metadata'; @@ -46,6 +47,7 @@ type Props = { fileId: string, getPreview: Function, getViewer: Function, + hasBoxAI: boolean, hasActivityFeed: boolean, hasAdditionalTabs: boolean, hasMetadata: boolean, @@ -57,6 +59,7 @@ type Props = { isLoading?: boolean, location: Location, metadataEditors?: Array, + boxAISidebarProps: BoxAISidebarProps, metadataSidebarProps: MetadataSidebarProps, onAnnotationSelect?: Function, onVersionChange?: Function, @@ -257,6 +260,7 @@ class Sidebar extends React.Component { getPreview, getViewer, hasAdditionalTabs, + boxAISidebarProps, hasNav, hasVersions, isDefaultOpen, @@ -268,6 +272,7 @@ class Sidebar extends React.Component { versionsSidebarProps, }: Props = this.props; const isOpen = this.isForcedSet() ? this.isForcedOpen() : !!isDefaultOpen; + 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); @@ -290,6 +295,7 @@ class Sidebar extends React.Component { additionalTabs={additionalTabs} elementId={this.id} fileId={fileId} + hasBoxAI={hasBoxAI} hasActivity={hasActivity} hasAdditionalTabs={hasAdditionalTabs} hasDetails={hasDetails} @@ -310,6 +316,7 @@ class Sidebar extends React.Component { fileId={fileId} getPreview={getPreview} getViewer={getViewer} + hasBoxAI={hasBoxAI} hasActivity={hasActivity} hasDetails={hasDetails} hasDocGen={docGenSidebarProps.isDocGenTemplate} @@ -319,6 +326,7 @@ class Sidebar extends React.Component { isOpen={isOpen} key={file.id} metadataSidebarProps={metadataSidebarProps} + boxAISidebarProps={boxAISidebarProps} onAnnotationSelect={onAnnotationSelect} onVersionChange={onVersionChange} onVersionHistoryClick={onVersionHistoryClick} diff --git a/src/elements/content-sidebar/SidebarNav.js b/src/elements/content-sidebar/SidebarNav.js index f69aa32dc9..0c4c005709 100644 --- a/src/elements/content-sidebar/SidebarNav.js +++ b/src/elements/content-sidebar/SidebarNav.js @@ -7,6 +7,8 @@ import * as React from 'react'; import { injectIntl } from 'react-intl'; import type { IntlShape } from 'react-intl'; +import { BoxAiLogo } from '@box/blueprint-web-assets/icons/Logo'; +import { Size5 } from '@box/blueprint-web-assets/tokens/tokens'; import AdditionalTabs from './additional-tabs'; import DocGenIcon from '../../icon/fill/DocGenIcon'; import IconChatRound from '../../icons/general/IconChatRound'; @@ -25,6 +27,7 @@ import { SIDEBAR_VIEW_DETAILS, SIDEBAR_VIEW_METADATA, SIDEBAR_VIEW_SKILLS, + SIDEBAR_VIEW_BOXAI, } from '../../constants'; import { useFeatureConfig } from '../common/feature-checking'; import type { NavigateOptions, AdditionalSidebarTab } from './flowTypes'; @@ -34,6 +37,7 @@ type Props = { additionalTabs?: Array, elementId: string, fileId: string, + hasBoxAI: boolean, hasActivity: boolean, hasAdditionalTabs: boolean, hasDetails: boolean, @@ -49,6 +53,7 @@ const SidebarNav = ({ additionalTabs, elementId, fileId, + hasBoxAI, hasActivity, hasAdditionalTabs, hasDetails, @@ -65,6 +70,16 @@ const SidebarNav = ({
+ {hasBoxAI && ( + + + + )} {hasActivity && ( { + boxAISidebar: ElementRefType = React.createRef(); + activitySidebar: ElementRefType = React.createRef(); detailsSidebar: ElementRefType = React.createRef(); @@ -142,11 +151,16 @@ class SidebarPanels extends React.Component { * @returns {void} */ refresh(shouldRefreshCache: boolean = true): void { + const { current: boxAISidebar } = this.boxAISidebar; const { current: activitySidebar } = this.activitySidebar; const { current: detailsSidebar } = this.detailsSidebar; const { current: metadataSidebar } = this.metadataSidebar; const { current: versionsSidebar } = this.versionsSidebar; + if (boxAISidebar) { + boxAISidebar.refresh(); + } + if (activitySidebar) { activitySidebar.refresh(shouldRefreshCache); } @@ -177,6 +191,7 @@ class SidebarPanels extends React.Component { fileId, getPreview, getViewer, + hasBoxAI, hasActivity, hasDetails, hasDocGen, @@ -185,6 +200,7 @@ class SidebarPanels extends React.Component { hasVersions, isOpen, metadataSidebarProps, + boxAISidebarProps, onAnnotationSelect, onVersionChange, onVersionHistoryClick, @@ -195,12 +211,30 @@ class SidebarPanels extends React.Component { const isMetadataSidebarRedesignEnabled = isFeatureEnabled(features, 'metadata.redesign.enabled'); - if (!isOpen || (!hasActivity && !hasDetails && !hasMetadata && !hasSkills && !hasVersions)) { + if (!isOpen || (!hasBoxAI && !hasActivity && !hasDetails && !hasMetadata && !hasSkills && !hasVersions)) { return null; } return ( + {hasBoxAI && ( + { + return ( + + ); + }} + /> + )} {hasSkills && ( { render={() => { let redirect = ''; - if (hasDocGen) { + if (hasBoxAI) { + redirect = SIDEBAR_VIEW_BOXAI; + } else if (hasDocGen) { redirect = SIDEBAR_VIEW_DOCGEN; } else if (hasSkills) { redirect = SIDEBAR_VIEW_SKILLS; diff --git a/src/elements/content-sidebar/SidebarUtils.js b/src/elements/content-sidebar/SidebarUtils.js index e229044772..bf377956f4 100644 --- a/src/elements/content-sidebar/SidebarUtils.js +++ b/src/elements/content-sidebar/SidebarUtils.js @@ -19,6 +19,7 @@ import { SIDEBAR_VIEW_VERSIONS, SIDEBAR_VIEW_DOCGEN, SIDEBAR_VIEW_METADATA_REDESIGN, + SIDEBAR_VIEW_BOXAI, } from '../../constants'; import type { MetadataSidebarProps } from './MetadataSidebar'; import type { MetadataEditor } from '../../common/types/metadata'; @@ -48,6 +49,17 @@ class SidebarUtils { return !!props.hasMetadata; } + /** + * Determines if we can render the Box AI sidebar. + * Only relies on props. + * + * @param {ContentSidebarProps} props - User passed in props + * @return {Boolean} true if we should render + */ + static canHaveBoxAISidebar(props: ContentSidebarProps): boolean { + return !!props.hasBoxAI; + } + /** * Determines if we can render the activity sidebar. * Only relies on props. @@ -81,6 +93,7 @@ class SidebarUtils { return ( SidebarUtils.canHaveDetailsSidebar(props) || SidebarUtils.canHaveActivitySidebar(props) || + SidebarUtils.canHaveBoxAISidebar(props) || SidebarUtils.canHaveSkillsSidebar(props) || SidebarUtils.canHaveMetadataSidebar(props) ); @@ -133,6 +146,7 @@ class SidebarUtils { (SidebarUtils.canHaveDetailsSidebar(props) || SidebarUtils.shouldRenderSkillsSidebar(props, file) || SidebarUtils.canHaveActivitySidebar(props) || + SidebarUtils.canHaveBoxAISidebar(props) || SidebarUtils.shouldRenderMetadataSidebar(props, editors)) ); } @@ -151,6 +165,8 @@ class SidebarUtils { return ; case SIDEBAR_VIEW_METADATA: return ; + case SIDEBAR_VIEW_BOXAI: + return ; case SIDEBAR_VIEW_ACTIVITY: return ; case SIDEBAR_VIEW_DOCGEN: @@ -188,6 +204,9 @@ class SidebarUtils { case SIDEBAR_VIEW_ACTIVITY: importFn = import(/* webpackMode: "lazy", webpackChunkName: "activity-sidebar" */ './ActivitySidebar'); break; + case SIDEBAR_VIEW_BOXAI: + importFn = import(/* webpackMode: "lazy", webpackChunkName: "boxai-sidebar" */ './BoxAISidebar'); + break; case SIDEBAR_VIEW_VERSIONS: importFn = import(/* webpackMode: "lazy", webpackChunkName: "versions-sidebar" */ './versions'); break; diff --git a/src/elements/content-sidebar/__mocks__/SidebarUtils.js b/src/elements/content-sidebar/__mocks__/SidebarUtils.js index 98ba636313..3d5ce552cb 100644 --- a/src/elements/content-sidebar/__mocks__/SidebarUtils.js +++ b/src/elements/content-sidebar/__mocks__/SidebarUtils.js @@ -12,6 +12,11 @@ export default { return
; } }, + boxai: class BoxAISidebar extends React.Component { + render() { + return
; + } + }, metadata: class MetadataSidebar extends React.Component { render() { return
; diff --git a/src/elements/content-sidebar/__tests__/SidebarNav.test.js b/src/elements/content-sidebar/__tests__/SidebarNav.test.js index df14ac03f0..b873b21b9e 100644 --- a/src/elements/content-sidebar/__tests__/SidebarNav.test.js +++ b/src/elements/content-sidebar/__tests__/SidebarNav.test.js @@ -1,6 +1,7 @@ import * as React from 'react'; import { MemoryRouter } from 'react-router-dom'; import { mount } from 'enzyme'; +import { BoxAiLogo } from '@box/blueprint-web-assets/icons/Logo'; import AdditionalTabPlaceholder from '../additional-tabs/AdditionalTabPlaceholder'; import AdditionalTabs from '../additional-tabs'; import AdditionalTabsLoading from '../additional-tabs/AdditionalTabsLoading'; @@ -31,6 +32,7 @@ describe('elements/content-sidebar/SidebarNav', () => { hasSkills: true, }; const wrapper = getWrapper(props); + expect(wrapper.find(BoxAiLogo)).toHaveLength(0); expect(wrapper.find(IconMagicWand)).toHaveLength(1); expect(wrapper.find(IconMetadataThick)).toHaveLength(0); expect(wrapper.find(IconDocInfo)).toHaveLength(0); @@ -42,6 +44,7 @@ describe('elements/content-sidebar/SidebarNav', () => { hasDetails: true, }; const wrapper = getWrapper(props); + expect(wrapper.find(BoxAiLogo)).toHaveLength(0); expect(wrapper.find(IconMagicWand)).toHaveLength(0); expect(wrapper.find(IconMetadataThick)).toHaveLength(0); expect(wrapper.find(IconDocInfo)).toHaveLength(1); @@ -53,6 +56,7 @@ describe('elements/content-sidebar/SidebarNav', () => { hasActivity: true, }; const wrapper = getWrapper(props); + expect(wrapper.find(BoxAiLogo)).toHaveLength(0); expect(wrapper.find(IconMagicWand)).toHaveLength(0); expect(wrapper.find(IconMetadataThick)).toHaveLength(0); expect(wrapper.find(IconDocInfo)).toHaveLength(0); @@ -64,24 +68,39 @@ describe('elements/content-sidebar/SidebarNav', () => { hasMetadata: true, }; const wrapper = getWrapper(props); + expect(wrapper.find(BoxAiLogo)).toHaveLength(0); expect(wrapper.find(IconMagicWand)).toHaveLength(0); expect(wrapper.find(IconMetadataThick)).toHaveLength(1); expect(wrapper.find(IconDocInfo)).toHaveLength(0); expect(wrapper.find(IconChatRound)).toHaveLength(0); }); + test('should render box ai tab', () => { + const props = { + hasBoxAI: true, + }; + const wrapper = getWrapper(props); + expect(wrapper.find(BoxAiLogo)).toHaveLength(1); + expect(wrapper.find(IconMagicWand)).toHaveLength(0); + expect(wrapper.find(IconMetadataThick)).toHaveLength(0); + expect(wrapper.find(IconDocInfo)).toHaveLength(0); + expect(wrapper.find(IconChatRound)).toHaveLength(0); + }); + test('should have multiple tabs', () => { const props = { hasActivity: true, hasMetadata: true, hasSkills: true, + hasBoxAI: true, }; const wrapper = getWrapper(props, 'activity'); expect(wrapper.find(IconMagicWand)).toHaveLength(1); expect(wrapper.find(IconMetadataThick)).toHaveLength(1); expect(wrapper.find(IconDocInfo)).toHaveLength(0); expect(wrapper.find(IconChatRound)).toHaveLength(1); - expect(wrapper.find(SidebarNavButton)).toHaveLength(3); + expect(wrapper.find(BoxAiLogo)).toHaveLength(1); + expect(wrapper.find(SidebarNavButton)).toHaveLength(4); }); test('should render the additional tabs loading state', () => { @@ -119,6 +138,7 @@ describe('elements/content-sidebar/SidebarNav', () => { expect(wrapper.find(IconMetadataThick)).toHaveLength(0); expect(wrapper.find(IconDocInfo)).toHaveLength(0); expect(wrapper.find(IconChatRound)).toHaveLength(0); + expect(wrapper.find(BoxAiLogo)).toHaveLength(0); expect(wrapper.find(DocGenIcon)).toHaveLength(1); }); }); diff --git a/src/elements/content-sidebar/__tests__/SidebarPanels.test.js b/src/elements/content-sidebar/__tests__/SidebarPanels.test.js index 006433d8d9..488ad978ad 100644 --- a/src/elements/content-sidebar/__tests__/SidebarPanels.test.js +++ b/src/elements/content-sidebar/__tests__/SidebarPanels.test.js @@ -13,6 +13,7 @@ describe('elements/content-sidebar/SidebarPanels', () => { mount( { ${'/details/versions/1234'} | ${'VersionsSidebar'} ${'/metadata'} | ${'MetadataSidebar'} ${'/skills'} | ${'SkillsSidebar'} + ${'/boxai'} | ${'BoxAISidebar'} ${'/nonsense'} | ${'SkillsSidebar'} ${'/'} | ${'SkillsSidebar'} `('should render $sidebar given the path $path', ({ path, sidebar }) => { @@ -68,6 +70,7 @@ describe('elements/content-sidebar/SidebarPanels', () => { test('should render nothing if all sidebars are disabled', () => { const wrapper = getWrapper({ + hasBoxAI: false, hasActivity: false, hasDetails: false, hasMetadata: false, @@ -138,17 +141,18 @@ describe('elements/content-sidebar/SidebarPanels', () => { describe('refresh()', () => { test.each([true, false])('should call the sidebars with the appropriate argument', shouldRefreshCache => { - const instance = getWrapper() - .find(SidebarPanels) - .instance(); + const instance = getWrapper().find(SidebarPanels).instance(); - ['activitySidebar', 'detailsSidebar', 'metadataSidebar', 'versionsSidebar'].forEach(sidebar => { - instance[sidebar] = { current: { refresh: jest.fn() } }; - }); + ['boxAISidebar', 'activitySidebar', 'detailsSidebar', 'metadataSidebar', 'versionsSidebar'].forEach( + sidebar => { + instance[sidebar] = { current: { refresh: jest.fn() } }; + }, + ); instance.refresh(shouldRefreshCache); expect(instance.activitySidebar.current.refresh).toHaveBeenCalledWith(shouldRefreshCache); + expect(instance.boxAISidebar.current.refresh).toHaveBeenCalledWith(); expect(instance.detailsSidebar.current.refresh).toHaveBeenCalledWith(); expect(instance.metadataSidebar.current.refresh).toHaveBeenCalledWith(); expect(instance.versionsSidebar.current.refresh).toHaveBeenCalledWith(); diff --git a/src/elements/content-sidebar/__tests__/SidebarUtils.test.js b/src/elements/content-sidebar/__tests__/SidebarUtils.test.js index da66db170d..89ea650d46 100644 --- a/src/elements/content-sidebar/__tests__/SidebarUtils.test.js +++ b/src/elements/content-sidebar/__tests__/SidebarUtils.test.js @@ -8,6 +8,7 @@ import { SIDEBAR_VIEW_METADATA, SIDEBAR_VIEW_DETAILS, SIDEBAR_VIEW_DOCGEN, + SIDEBAR_VIEW_BOXAI, } from '../../../constants'; jest.mock('../../common/async-load', () => () => 'LoadableComponent'); @@ -48,6 +49,9 @@ describe('elements/content-sidebar/SidebarUtil', () => { test('should return true when activity feed should render', () => { expect(SidebarUtils.canHaveSidebar({ hasActivityFeed: true })).toBeTruthy(); }); + test('should return true when box ai feed should render', () => { + expect(SidebarUtils.canHaveSidebar({ hasBoxAI: true })).toBeTruthy(); + }); test('should return true when notices should render', () => { expect( SidebarUtils.canHaveSidebar({ @@ -118,6 +122,14 @@ describe('elements/content-sidebar/SidebarUtil', () => { expect(SidebarUtils.canHaveActivitySidebar({ hasActivityFeed: true }, {})).toBeTruthy(); }); }); + describe('canHaveBoxAISidebar()', () => { + test('should return false when hasBoxAI is false', () => { + expect(SidebarUtils.canHaveBoxAISidebar({ hasBoxAI: false })).toBeFalsy(); + }); + test('should return true when hasBoxAI is true', () => { + expect(SidebarUtils.canHaveBoxAISidebar({ hasBoxAI: true }, {})).toBeTruthy(); + }); + }); describe('canHaveMetadataSidebar()', () => { test('should return false when hasMetadata is false', () => { expect(SidebarUtils.canHaveMetadataSidebar({ hasMetadata: false })).toBeFalsy(); @@ -196,10 +208,19 @@ describe('elements/content-sidebar/SidebarUtil', () => { test('should return false when no file', () => { expect(SidebarUtils.shouldRenderSidebar({ hasSkills: true })).toBeFalsy(); }); + test('should return true when we can render box ai sidebar', () => { + SidebarUtils.canHaveDetailsSidebar = jest.fn().mockReturnValueOnce(false); + SidebarUtils.shouldRenderSkillsSidebar = jest.fn().mockReturnValueOnce(false); + SidebarUtils.canHaveActivitySidebar = jest.fn().mockReturnValueOnce(false); + SidebarUtils.canHaveBoxAISidebar = jest.fn().mockReturnValueOnce(true); + SidebarUtils.shouldRenderMetadataSidebar = jest.fn().mockReturnValueOnce(false); + expect(SidebarUtils.shouldRenderSidebar('props', 'file')).toBeTruthy(); + }); test('should return true when we can render details sidebar', () => { SidebarUtils.canHaveDetailsSidebar = jest.fn().mockReturnValueOnce(true); SidebarUtils.shouldRenderSkillsSidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.canHaveActivitySidebar = jest.fn().mockReturnValueOnce(false); + SidebarUtils.canHaveBoxAISidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.shouldRenderMetadataSidebar = jest.fn().mockReturnValueOnce(false); expect(SidebarUtils.shouldRenderSidebar('props', 'file')).toBeTruthy(); expect(SidebarUtils.canHaveDetailsSidebar).toHaveBeenCalledWith('props'); @@ -208,6 +229,7 @@ describe('elements/content-sidebar/SidebarUtil', () => { SidebarUtils.canHaveDetailsSidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.shouldRenderSkillsSidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.canHaveActivitySidebar = jest.fn().mockReturnValueOnce(false); + SidebarUtils.canHaveBoxAISidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.shouldRenderMetadataSidebar = jest.fn().mockReturnValueOnce(true); expect(SidebarUtils.shouldRenderSidebar('props', 'file', 'editors')).toBeTruthy(); expect(SidebarUtils.shouldRenderMetadataSidebar).toHaveBeenCalledWith('props', 'editors'); @@ -216,6 +238,7 @@ describe('elements/content-sidebar/SidebarUtil', () => { SidebarUtils.canHaveDetailsSidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.shouldRenderSkillsSidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.canHaveActivitySidebar = jest.fn().mockReturnValueOnce(true); + SidebarUtils.canHaveBoxAISidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.shouldRenderMetadataSidebar = jest.fn().mockReturnValueOnce(false); expect(SidebarUtils.shouldRenderSidebar('props', 'file')).toBeTruthy(); expect(SidebarUtils.canHaveActivitySidebar).toHaveBeenCalledWith('props'); @@ -224,6 +247,7 @@ describe('elements/content-sidebar/SidebarUtil', () => { SidebarUtils.canHaveDetailsSidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.shouldRenderSkillsSidebar = jest.fn().mockReturnValueOnce(true); SidebarUtils.canHaveActivitySidebar = jest.fn().mockReturnValueOnce(false); + SidebarUtils.canHaveBoxAISidebar = jest.fn().mockReturnValueOnce(false); SidebarUtils.shouldRenderMetadataSidebar = jest.fn().mockReturnValueOnce(false); expect(SidebarUtils.shouldRenderSidebar('props', 'file')).toBeTruthy(); expect(SidebarUtils.shouldRenderSkillsSidebar).toHaveBeenCalledWith('props', 'file'); @@ -236,6 +260,7 @@ describe('elements/content-sidebar/SidebarUtil', () => { SIDEBAR_VIEW_DETAILS, SIDEBAR_VIEW_METADATA, SIDEBAR_VIEW_ACTIVITY, + SIDEBAR_VIEW_BOXAI, SIDEBAR_VIEW_DOCGEN, ])('should return the title for %s', view => { const title = SidebarUtils.getTitleForView(view); diff --git a/src/elements/content-sidebar/__tests__/__snapshots__/SidebarUtils.test.js.snap b/src/elements/content-sidebar/__tests__/__snapshots__/SidebarUtils.test.js.snap index 490dd3c2df..efb7398896 100644 --- a/src/elements/content-sidebar/__tests__/__snapshots__/SidebarUtils.test.js.snap +++ b/src/elements/content-sidebar/__tests__/__snapshots__/SidebarUtils.test.js.snap @@ -11,6 +11,13 @@ exports[`elements/content-sidebar/SidebarUtil getTitleForView() should return th /> `; +exports[`elements/content-sidebar/SidebarUtil getTitleForView() should return the title for boxai 1`] = ` + +`; + exports[`elements/content-sidebar/SidebarUtil getTitleForView() should return the title for details 1`] = ` diff --git a/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx b/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx new file mode 100644 index 0000000000..baa5b1e6d3 --- /dev/null +++ b/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx @@ -0,0 +1,15 @@ +// @flow +import ContentSidebar from '../ContentSidebar'; + +export const basic = {}; + +export default { + title: 'Elements/ContentSidebar/BoxAISidebar', + component: ContentSidebar, + args: { + features: global.FEATURES, + fileId: global.FILE_ID, + hasBoxAI: true, + token: global.TOKEN, + }, +}; diff --git a/src/elements/content-sidebar/stories/ContentSidebar.mdx b/src/elements/content-sidebar/stories/ContentSidebar.mdx index 384bc31ad2..487a828457 100644 --- a/src/elements/content-sidebar/stories/ContentSidebar.mdx +++ b/src/elements/content-sidebar/stories/ContentSidebar.mdx @@ -21,6 +21,7 @@ var ContentSidebar = require('./ContentSidebar').default; features={FEATURES} fileId={FILE_ID} hasActivityFeed + hasBoxAI hasMetadata hasSkills hasVersions diff --git a/src/elements/content-sidebar/stories/ContentSidebar.stories.js b/src/elements/content-sidebar/stories/ContentSidebar.stories.js index f208526c76..b8e36cd8b9 100644 --- a/src/elements/content-sidebar/stories/ContentSidebar.stories.js +++ b/src/elements/content-sidebar/stories/ContentSidebar.stories.js @@ -17,6 +17,7 @@ export default { features: global.FEATURES, fileId: global.FILE_ID, hasActivityFeed: true, + hasBoxAI: true, hasMetadata: true, hasSkills: true, hasVersions: true, diff --git a/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx b/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx new file mode 100644 index 0000000000..e071ba329c --- /dev/null +++ b/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx @@ -0,0 +1,23 @@ +import { expect, within } from '@storybook/test'; +import { type StoryObj } from '@storybook/react'; +import ContentSidebar from '../../ContentSidebar'; +import BoxAISidebar from '../../BoxAISidebar'; + +export default { + title: 'Elements/ContentSidebar/BoxAISidebar/tests/visual-regression-tests', + component: ContentSidebar, + args: { + features: global.FEATURES, + fileId: global.FILE_ID, + hasBoxAI: true, + token: global.TOKEN, + }, +}; + +export const SimpleBoxAISidebarCheck: StoryObj = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const sidebar = await canvas.findByRole('heading', { name: 'Box AI' }, { timeout: 5000 }); + expect(sidebar).toBeInTheDocument(); + }, +}; From af1787783b462aa9c6f5f36c199cfc464eaf2b7a Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Wed, 18 Sep 2024 15:21:32 +0200 Subject: [PATCH 02/14] feat(boxai-sidebar): Empty Sidebar for Box AI --- i18n/en-US.properties | 6 +-- src/elements/content-sidebar/BoxAISidebar.tsx | 41 +++++++++++++++++++ .../__tests__/ContentSidebar.test.js | 15 +++++++ 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/elements/content-sidebar/BoxAISidebar.tsx diff --git a/i18n/en-US.properties b/i18n/en-US.properties index b21e5317e9..7650d61e93 100644 --- a/i18n/en-US.properties +++ b/i18n/en-US.properties @@ -517,7 +517,7 @@ be.messageCenter.events = Events # Displayed when there are no posts to display be.messageCenter.noPosts = There are no posts for this category at the moment. # Error message for preview not loading an image -be.messageCenter.previewError = Sorry, we're having trouble showing this image. +be.messageCenter.previewError = Sorry, we're having trouble showing this image. # Title for product category be.messageCenter.product = Product # Title for the message center modal @@ -1005,9 +1005,9 @@ boxui.contentExplorer.name = Name # Text shown on button used to create a new folder boxui.contentExplorer.newFolder = New Folder # Text shown to indicate the number of folders selected -boxui.contentExplorer.numFoldersSelected = {numSelected, plural, =0 {0 folders selected} one {1 folder selected} other {# folders selected} } +boxui.contentExplorer.numFoldersSelected = {numSelected, plural, =0 {0 folders selected} one {1 folder selected} other {# folders selected} } # Text shown to indicate the number of items selected with Include Subfolders feature -boxui.contentExplorer.numItemsSelected = {numSelected, plural, =0 {0 items selected} one {1 item selected} other {# items selected} } +boxui.contentExplorer.numItemsSelected = {numSelected, plural, =0 {0 items selected} one {1 item selected} other {# items selected} } # Text shown to indicate the number of items selected boxui.contentExplorer.numSelected = {numSelected} Selected # Results label for number of items on list when it's just 1 diff --git a/src/elements/content-sidebar/BoxAISidebar.tsx b/src/elements/content-sidebar/BoxAISidebar.tsx new file mode 100644 index 0000000000..913f836041 --- /dev/null +++ b/src/elements/content-sidebar/BoxAISidebar.tsx @@ -0,0 +1,41 @@ +/** + * @file Redesigned Metadata sidebar component + * @author Box + */ +import * as React from 'react'; +import flow from 'lodash/flow'; +import { useIntl } from 'react-intl'; + +import SidebarContent from './SidebarContent'; +import { withAPIContext } from '../common/api-context'; +import { withErrorBoundary } from '../common/error-boundary'; +import { withLogger } from '../common/logger'; +import { ORIGIN_BOXAI_SIDEBAR, SIDEBAR_VIEW_BOXAI } from '../../constants'; +import { EVENT_JS_READY } from '../common/logger/constants'; +import { mark } from '../../utils/performance'; + +import messages from '../common/messages'; +import './MetadataSidebarRedesign.scss'; + +const MARK_NAME_JS_READY = `${ORIGIN_BOXAI_SIDEBAR}_${EVENT_JS_READY}`; + +mark(MARK_NAME_JS_READY); + +function BoxAISidebar() { + const { formatMessage } = useIntl(); + + return ( + +
+
+ ); +} + +export { BoxAISidebar as BoxAISideBarComponent }; +export default flow([withLogger(ORIGIN_BOXAI_SIDEBAR), withErrorBoundary(ORIGIN_BOXAI_SIDEBAR), withAPIContext])( + BoxAISidebar, +); diff --git a/src/elements/content-sidebar/__tests__/ContentSidebar.test.js b/src/elements/content-sidebar/__tests__/ContentSidebar.test.js index a6ccf692d7..393e9a718a 100644 --- a/src/elements/content-sidebar/__tests__/ContentSidebar.test.js +++ b/src/elements/content-sidebar/__tests__/ContentSidebar.test.js @@ -78,6 +78,21 @@ describe('elements/content-sidebar/ContentSidebar', () => { expect(instance.fetchFile).not.toBeCalled(); expect(instance.setState).not.toBeCalled(); }); + + test('should not fetch the file data if the id has not changed', () => { + const wrapper = getWrapper({ fileId: '123' }); + const instance = wrapper.instance(); + const newProps = { fileId: '123' }; + + instance.fetchFile = jest.fn(); + instance.setState({ view: 'activityFeed' }); + instance.setState = jest.fn(); + + instance.componentDidUpdate(newProps); + + expect(instance.fetchFile).not.toBeCalled(); + expect(instance.setState).not.toBeCalled(); + }); }); describe('fetchFile()', () => { From 7fb0cf3186ba862f3eb018c6a61ef2c05d14638f Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Mon, 23 Sep 2024 14:14:52 +0200 Subject: [PATCH 03/14] feat(boxai-sidebar): BoxAISidebar.tsx deleted --- src/elements/content-sidebar/BoxAISidebar.tsx | 41 ------------------- 1 file changed, 41 deletions(-) delete mode 100644 src/elements/content-sidebar/BoxAISidebar.tsx diff --git a/src/elements/content-sidebar/BoxAISidebar.tsx b/src/elements/content-sidebar/BoxAISidebar.tsx deleted file mode 100644 index 913f836041..0000000000 --- a/src/elements/content-sidebar/BoxAISidebar.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file Redesigned Metadata sidebar component - * @author Box - */ -import * as React from 'react'; -import flow from 'lodash/flow'; -import { useIntl } from 'react-intl'; - -import SidebarContent from './SidebarContent'; -import { withAPIContext } from '../common/api-context'; -import { withErrorBoundary } from '../common/error-boundary'; -import { withLogger } from '../common/logger'; -import { ORIGIN_BOXAI_SIDEBAR, SIDEBAR_VIEW_BOXAI } from '../../constants'; -import { EVENT_JS_READY } from '../common/logger/constants'; -import { mark } from '../../utils/performance'; - -import messages from '../common/messages'; -import './MetadataSidebarRedesign.scss'; - -const MARK_NAME_JS_READY = `${ORIGIN_BOXAI_SIDEBAR}_${EVENT_JS_READY}`; - -mark(MARK_NAME_JS_READY); - -function BoxAISidebar() { - const { formatMessage } = useIntl(); - - return ( - -
-
- ); -} - -export { BoxAISidebar as BoxAISideBarComponent }; -export default flow([withLogger(ORIGIN_BOXAI_SIDEBAR), withErrorBoundary(ORIGIN_BOXAI_SIDEBAR), withAPIContext])( - BoxAISidebar, -); From 3d276430eef5f31cb15f27828586e573b0cd6672 Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Tue, 24 Sep 2024 14:57:35 +0200 Subject: [PATCH 04/14] feat(boxai-sidebar): SidebarPanels.test.js test fixed --- i18n/en-US.properties | 2 ++ src/elements/content-sidebar/Sidebar.js | 3 +-- src/elements/content-sidebar/SidebarUtils.js | 4 +++- src/elements/content-sidebar/__tests__/SidebarPanels.test.js | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/i18n/en-US.properties b/i18n/en-US.properties index 7650d61e93..4bc2922237 100644 --- a/i18n/en-US.properties +++ b/i18n/en-US.properties @@ -646,6 +646,8 @@ be.shareDialogText = Shared Link: be.sidebarAccessStats = Access Stats # Title for the preview activity feed. be.sidebarActivityTitle = Activity +# Title for the preview Box AI feed. +be.sidebarBoxAITitle = Box AI # Title for the sidebar content insights. be.sidebarContentInsights = Content Insights # Title for the preview details sidebar. diff --git a/src/elements/content-sidebar/Sidebar.js b/src/elements/content-sidebar/Sidebar.js index d66f3e282c..422fd343df 100644 --- a/src/elements/content-sidebar/Sidebar.js +++ b/src/elements/content-sidebar/Sidebar.js @@ -24,7 +24,7 @@ import type { ActivitySidebarProps } from './ActivitySidebar'; import type { DetailsSidebarProps } from './DetailsSidebar'; import type { DocGenSidebarProps } from './DocGenSidebar/DocGenSidebar'; import type { MetadataSidebarProps } from './MetadataSidebar'; -import type { BoxAISidebarProps } from './BoxAISidebarProps'; +import type { BoxAISidebarProps } from './BoxAISidebar'; import type { VersionsSidebarProps } from './versions'; import type { AdditionalSidebarTab } from './flowTypes'; import type { MetadataEditor } from '../../common/types/metadata'; @@ -47,7 +47,6 @@ type Props = { fileId: string, getPreview: Function, getViewer: Function, - hasBoxAI: boolean, hasActivityFeed: boolean, hasAdditionalTabs: boolean, hasMetadata: boolean, diff --git a/src/elements/content-sidebar/SidebarUtils.js b/src/elements/content-sidebar/SidebarUtils.js index bf377956f4..cef924901e 100644 --- a/src/elements/content-sidebar/SidebarUtils.js +++ b/src/elements/content-sidebar/SidebarUtils.js @@ -24,6 +24,7 @@ import { import type { MetadataSidebarProps } from './MetadataSidebar'; import type { MetadataEditor } from '../../common/types/metadata'; import type { BoxItem } from '../../common/types/core'; +import { isFeatureEnabled } from '../common/feature-checking'; class SidebarUtils { /** @@ -57,7 +58,7 @@ class SidebarUtils { * @return {Boolean} true if we should render */ static canHaveBoxAISidebar(props: ContentSidebarProps): boolean { - return !!props.hasBoxAI; + return isFeatureEnabled(props.features, 'boxai.sidebar.enabled'); } /** @@ -123,6 +124,7 @@ class SidebarUtils { */ static shouldRenderMetadataSidebar(props: ContentSidebarProps, editors?: Array): boolean { const { metadataSidebarProps = {} }: ContentSidebarProps = props; + // eslint-disable-next-line no-shadow const { isFeatureEnabled = true }: MetadataSidebarProps = metadataSidebarProps; return ( diff --git a/src/elements/content-sidebar/__tests__/SidebarPanels.test.js b/src/elements/content-sidebar/__tests__/SidebarPanels.test.js index 488ad978ad..0a53705876 100644 --- a/src/elements/content-sidebar/__tests__/SidebarPanels.test.js +++ b/src/elements/content-sidebar/__tests__/SidebarPanels.test.js @@ -49,8 +49,8 @@ describe('elements/content-sidebar/SidebarPanels', () => { ${'/metadata'} | ${'MetadataSidebar'} ${'/skills'} | ${'SkillsSidebar'} ${'/boxai'} | ${'BoxAISidebar'} - ${'/nonsense'} | ${'SkillsSidebar'} - ${'/'} | ${'SkillsSidebar'} + ${'/nonsense'} | ${'BoxAISidebar'} + ${'/'} | ${'BoxAISidebar'} `('should render $sidebar given the path $path', ({ path, sidebar }) => { const wrapper = getWrapper({ path }); expect(wrapper.exists(sidebar)).toBe(true); From 9166a046325040ea43d9e6bda7ad587c0fd3c463 Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Wed, 25 Sep 2024 19:32:07 +0200 Subject: [PATCH 05/14] feat(boxai-sidebar): PR fixes --- .../content-sidebar/ContentSidebar.js | 15 +++++----- src/elements/content-sidebar/Sidebar.js | 11 +++---- src/elements/content-sidebar/SidebarNav.js | 4 +-- src/elements/content-sidebar/SidebarPanels.js | 8 ++--- src/elements/content-sidebar/SidebarUtils.js | 3 +- .../__tests__/BoxAISidebar.test.js | 29 +++++++++++++++++++ .../stories/BoxAISidebar.stories.tsx | 7 +++-- .../tests/BoxAISidebar-visual.stories.tsx | 7 +++-- 8 files changed, 59 insertions(+), 25 deletions(-) create mode 100644 src/elements/content-sidebar/__tests__/BoxAISidebar.test.js diff --git a/src/elements/content-sidebar/ContentSidebar.js b/src/elements/content-sidebar/ContentSidebar.js index 4cc1f998ef..2891a764b7 100644 --- a/src/elements/content-sidebar/ContentSidebar.js +++ b/src/elements/content-sidebar/ContentSidebar.js @@ -51,6 +51,7 @@ type Props = { activitySidebarProps: ActivitySidebarProps, additionalTabs?: Array, apiHost: string, + boxAISidebarProps: BoxAISidebarProps, cache?: APICache, className: string, clientName: string, @@ -62,8 +63,6 @@ type Props = { fileId?: string, getPreview: Function, getViewer: Function, - hasBoxAI: boolean, - boxAISidebarProps: BoxAISidebarProps, hasActivityFeed: boolean, hasAdditionalTabs: boolean, hasMetadata: boolean, @@ -110,6 +109,7 @@ class ContentSidebar extends React.Component { static defaultProps = { activitySidebarProps: {}, apiHost: DEFAULT_HOSTNAME_API, + boxAISidebarProps: {}, className: '', clientName: CLIENT_NAME_CONTENT_SIDEBAR, defaultView: '', @@ -117,10 +117,9 @@ class ContentSidebar extends React.Component { docGenSidebarProps: { enabled: false }, getPreview: noop, getViewer: noop, - hasBoxAI: false, - boxAISidebarProps: {}, hasActivityFeed: false, hasAdditionalTabs: false, + hasBoxAI: false, hasMetadata: false, hasNav: true, hasSkills: false, @@ -259,6 +258,7 @@ class ContentSidebar extends React.Component { fetchMetadata(): void { const { file }: State = this.state; const { metadataSidebarProps }: Props = this.props; + // eslint-disable-next-line no-shadow const { isFeatureEnabled = true }: MetadataSidebarProps = metadataSidebarProps; // Only fetch metadata if we think that the file may have metadata on it @@ -338,6 +338,7 @@ class ContentSidebar extends React.Component { const { activitySidebarProps, additionalTabs, + boxAISidebarProps, className, currentUser, defaultView, @@ -347,8 +348,6 @@ class ContentSidebar extends React.Component { getPreview, getViewer, hasAdditionalTabs, - hasBoxAI, - boxAISidebarProps, hasActivityFeed, hasMetadata, hasNav, @@ -379,6 +378,7 @@ class ContentSidebar extends React.Component { { fileId={fileId} getPreview={getPreview} getViewer={getViewer} - hasBoxAI={hasBoxAI} - boxAISidebarProps={boxAISidebarProps} hasActivityFeed={hasActivityFeed} hasAdditionalTabs={hasAdditionalTabs} + hasBoxAI={isFeatureEnabled(props.features, 'boxai.sidebar.enabled')} hasNav={hasNav} hasMetadata={hasMetadata} hasSkills={hasSkills} diff --git a/src/elements/content-sidebar/Sidebar.js b/src/elements/content-sidebar/Sidebar.js index 422fd343df..b7bc240ea4 100644 --- a/src/elements/content-sidebar/Sidebar.js +++ b/src/elements/content-sidebar/Sidebar.js @@ -37,6 +37,7 @@ type Props = { activitySidebarProps: ActivitySidebarProps, additionalTabs?: Array, api: API, + boxAISidebarProps: BoxAISidebarProps, className: string, currentUser?: User, currentUserError?: Errors, @@ -49,6 +50,7 @@ type Props = { getViewer: Function, hasActivityFeed: boolean, hasAdditionalTabs: boolean, + hasBoxAI: boolean, hasMetadata: boolean, hasNav: boolean, hasSkills: boolean, @@ -58,7 +60,6 @@ type Props = { isLoading?: boolean, location: Location, metadataEditors?: Array, - boxAISidebarProps: BoxAISidebarProps, metadataSidebarProps: MetadataSidebarProps, onAnnotationSelect?: Function, onVersionChange?: Function, @@ -249,6 +250,7 @@ class Sidebar extends React.Component { const { activitySidebarProps, additionalTabs, + boxAISidebarProps, className, currentUser, currentUserError, @@ -259,7 +261,6 @@ class Sidebar extends React.Component { getPreview, getViewer, hasAdditionalTabs, - boxAISidebarProps, hasNav, hasVersions, isDefaultOpen, @@ -294,9 +295,9 @@ class Sidebar extends React.Component { additionalTabs={additionalTabs} elementId={this.id} fileId={fileId} - hasBoxAI={hasBoxAI} hasActivity={hasActivity} hasAdditionalTabs={hasAdditionalTabs} + hasBoxAI={hasBoxAI} hasDetails={hasDetails} hasMetadata={hasMetadata} hasSkills={hasSkills} @@ -306,6 +307,7 @@ class Sidebar extends React.Component { )} { fileId={fileId} getPreview={getPreview} getViewer={getViewer} - hasBoxAI={hasBoxAI} hasActivity={hasActivity} + hasBoxAI={hasBoxAI} hasDetails={hasDetails} hasDocGen={docGenSidebarProps.isDocGenTemplate} hasMetadata={hasMetadata} @@ -325,7 +327,6 @@ class Sidebar extends React.Component { isOpen={isOpen} key={file.id} metadataSidebarProps={metadataSidebarProps} - boxAISidebarProps={boxAISidebarProps} onAnnotationSelect={onAnnotationSelect} onVersionChange={onVersionChange} onVersionHistoryClick={onVersionHistoryClick} diff --git a/src/elements/content-sidebar/SidebarNav.js b/src/elements/content-sidebar/SidebarNav.js index 0c4c005709..72764c1da7 100644 --- a/src/elements/content-sidebar/SidebarNav.js +++ b/src/elements/content-sidebar/SidebarNav.js @@ -37,9 +37,9 @@ type Props = { additionalTabs?: Array, elementId: string, fileId: string, - hasBoxAI: boolean, hasActivity: boolean, hasAdditionalTabs: boolean, + hasBoxAI: boolean, hasDetails: boolean, hasDocGen?: boolean, hasMetadata: boolean, @@ -53,9 +53,9 @@ const SidebarNav = ({ additionalTabs, elementId, fileId, - hasBoxAI, hasActivity, hasAdditionalTabs, + hasBoxAI, hasDetails, hasMetadata, hasSkills, diff --git a/src/elements/content-sidebar/SidebarPanels.js b/src/elements/content-sidebar/SidebarPanels.js index 44fe58ceb7..e5eacd14d8 100644 --- a/src/elements/content-sidebar/SidebarPanels.js +++ b/src/elements/content-sidebar/SidebarPanels.js @@ -43,6 +43,7 @@ import type { FeatureConfig } from '../common/feature-checking'; type Props = { activitySidebarProps: ActivitySidebarProps, + boxAISidebarProps: BoxAISidebarProps, currentUser?: User, currentUserError?: Errors, detailsSidebarProps: DetailsSidebarProps, @@ -53,8 +54,8 @@ type Props = { fileId: string, getPreview: Function, getViewer: Function, - hasBoxAI: boolean, hasActivity: boolean, + hasBoxAI: boolean, hasDetails: boolean, hasDocGen: boolean, hasMetadata: boolean, @@ -62,7 +63,6 @@ type Props = { hasVersions: boolean, isOpen: boolean, location: Location, - boxAISidebarProps: BoxAISidebarProps, metadataSidebarProps: MetadataSidebarProps, onAnnotationSelect?: Function, onVersionChange?: Function, @@ -181,6 +181,7 @@ class SidebarPanels extends React.Component { render() { const { activitySidebarProps, + boxAISidebarProps, currentUser, currentUserError, detailsSidebarProps, @@ -191,8 +192,8 @@ class SidebarPanels extends React.Component { fileId, getPreview, getViewer, - hasBoxAI, hasActivity, + hasBoxAI, hasDetails, hasDocGen, hasMetadata, @@ -200,7 +201,6 @@ class SidebarPanels extends React.Component { hasVersions, isOpen, metadataSidebarProps, - boxAISidebarProps, onAnnotationSelect, onVersionChange, onVersionHistoryClick, diff --git a/src/elements/content-sidebar/SidebarUtils.js b/src/elements/content-sidebar/SidebarUtils.js index cef924901e..d28926cf61 100644 --- a/src/elements/content-sidebar/SidebarUtils.js +++ b/src/elements/content-sidebar/SidebarUtils.js @@ -24,7 +24,6 @@ import { import type { MetadataSidebarProps } from './MetadataSidebar'; import type { MetadataEditor } from '../../common/types/metadata'; import type { BoxItem } from '../../common/types/core'; -import { isFeatureEnabled } from '../common/feature-checking'; class SidebarUtils { /** @@ -58,7 +57,7 @@ class SidebarUtils { * @return {Boolean} true if we should render */ static canHaveBoxAISidebar(props: ContentSidebarProps): boolean { - return isFeatureEnabled(props.features, 'boxai.sidebar.enabled'); + return !!props.hasBoxAI; } /** diff --git a/src/elements/content-sidebar/__tests__/BoxAISidebar.test.js b/src/elements/content-sidebar/__tests__/BoxAISidebar.test.js new file mode 100644 index 0000000000..307e208f8b --- /dev/null +++ b/src/elements/content-sidebar/__tests__/BoxAISidebar.test.js @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { BoxAISidebarComponent } from '../BoxAISidebar'; + +describe('elements/content-sidebar/BoxAISidebar', () => { + const getWrapper = (props = {}) => + shallow(); + + describe('constructor()', () => { + let onReadyMetric; + beforeEach(() => { + const wrapper = getWrapper(); + ({ onReadyMetric } = wrapper.instance().props.logger); + }); + + test('should emit when js loaded', () => { + expect(onReadyMetric).toHaveBeenCalledWith({ + endMarkName: expect.any(String), + }); + }); + }); + + describe('render()', () => { + test('should render the boxai sidebar', () => { + const wrapper = getWrapper(); + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx b/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx index baa5b1e6d3..f61657e3af 100644 --- a/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx +++ b/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx @@ -1,15 +1,18 @@ // @flow import ContentSidebar from '../ContentSidebar'; +const mockFeatures = { + 'boxai.sidebar.enabled': true, +}; + export const basic = {}; export default { title: 'Elements/ContentSidebar/BoxAISidebar', component: ContentSidebar, args: { - features: global.FEATURES, + features: mockFeatures, fileId: global.FILE_ID, - hasBoxAI: true, token: global.TOKEN, }, }; diff --git a/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx b/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx index e071ba329c..d5c4eda186 100644 --- a/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx +++ b/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx @@ -3,13 +3,16 @@ import { type StoryObj } from '@storybook/react'; import ContentSidebar from '../../ContentSidebar'; import BoxAISidebar from '../../BoxAISidebar'; +const mockFeatures = { + 'boxai.sidebar.enabled': true, +}; + export default { title: 'Elements/ContentSidebar/BoxAISidebar/tests/visual-regression-tests', component: ContentSidebar, args: { - features: global.FEATURES, + features: mockFeatures, fileId: global.FILE_ID, - hasBoxAI: true, token: global.TOKEN, }, }; From e5fb1dd43fc0ca38a45262191527bd741718ca99 Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Wed, 25 Sep 2024 23:37:47 +0200 Subject: [PATCH 06/14] feat(boxai-sidebar): PR fixes --- src/elements/content-sidebar/BoxAISidebar.js | 53 ------------------- src/elements/content-sidebar/BoxAISidebar.tsx | 44 +++++++++++++++ .../content-sidebar/ContentSidebar.js | 1 - .../__tests__/BoxAISidebar.test.js | 29 ---------- .../__tests__/BoxAISidebar.test.tsx | 19 +++++++ .../stories/ContentSidebar.stories.js | 1 - 6 files changed, 63 insertions(+), 84 deletions(-) delete mode 100644 src/elements/content-sidebar/BoxAISidebar.js create mode 100644 src/elements/content-sidebar/BoxAISidebar.tsx delete mode 100644 src/elements/content-sidebar/__tests__/BoxAISidebar.test.js create mode 100644 src/elements/content-sidebar/__tests__/BoxAISidebar.test.tsx diff --git a/src/elements/content-sidebar/BoxAISidebar.js b/src/elements/content-sidebar/BoxAISidebar.js deleted file mode 100644 index c78e614f7b..0000000000 --- a/src/elements/content-sidebar/BoxAISidebar.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @flow - * @file BoxAI sidebar component - */ - -import * as React from 'react'; -import flow from 'lodash/flow'; -import { FormattedMessage } from 'react-intl'; -import messages from '../common/messages'; -import SidebarContent from './SidebarContent'; -import { EVENT_JS_READY } from '../common/logger/constants'; -import { mark } from '../../utils/performance'; -import { withAPIContext } from '../common/api-context'; -import { withErrorBoundary } from '../common/error-boundary'; -import { withLogger } from '../common/logger'; -import { ORIGIN_BOXAI_SIDEBAR, SIDEBAR_VIEW_BOXAI } from '../../constants'; - -type Props = { - onExpandPressed: () => void, - logger: any, -}; - -const MARK_NAME_JS_READY = `${ORIGIN_BOXAI_SIDEBAR}_${EVENT_JS_READY}`; - -mark(MARK_NAME_JS_READY); - -class BoxAISidebar extends React.PureComponent { - constructor(props: Props) { - super(props); - const { logger } = this.props; - logger.onReadyMetric({ - endMarkName: MARK_NAME_JS_READY, - }); - } - - render() { - return ( - } - > -
-
- ); - } -} - -export type BoxAISidebarProps = Props; -export { BoxAISidebar as BoxAISidebarComponent }; -export default flow([withLogger(ORIGIN_BOXAI_SIDEBAR), withErrorBoundary(ORIGIN_BOXAI_SIDEBAR), withAPIContext])( - BoxAISidebar, -); diff --git a/src/elements/content-sidebar/BoxAISidebar.tsx b/src/elements/content-sidebar/BoxAISidebar.tsx new file mode 100644 index 0000000000..16073c1460 --- /dev/null +++ b/src/elements/content-sidebar/BoxAISidebar.tsx @@ -0,0 +1,44 @@ +/** + * @file Redesigned Metadata sidebar component + * @author Box + */ +import * as React from 'react'; +import flow from 'lodash/flow'; +import { useIntl } from 'react-intl'; + +import SidebarContent from './SidebarContent'; +import { withAPIContext } from '../common/api-context'; +import { withErrorBoundary } from '../common/error-boundary'; +import { withLogger } from '../common/logger'; +import { ORIGIN_BOXAI_SIDEBAR, SIDEBAR_VIEW_BOXAI } from '../../constants'; +import { EVENT_JS_READY } from '../common/logger/constants'; +import { mark } from '../../utils/performance'; + +import messages from '../common/messages'; + +const MARK_NAME_JS_READY = `${ORIGIN_BOXAI_SIDEBAR}_${EVENT_JS_READY}`; + +mark(MARK_NAME_JS_READY); + +export interface BoxAISidebarProps { + onExpandPressed: () => void; +} + +function BoxAISideBar() { + const { formatMessage } = useIntl(); + + return ( + +
+ + ); +} + +export { BoxAISideBar as BoxAISidebarComponent }; +export default flow([withLogger(ORIGIN_BOXAI_SIDEBAR), withErrorBoundary(ORIGIN_BOXAI_SIDEBAR), withAPIContext])( + BoxAISideBar, +); diff --git a/src/elements/content-sidebar/ContentSidebar.js b/src/elements/content-sidebar/ContentSidebar.js index 2891a764b7..0c8bbaa07f 100644 --- a/src/elements/content-sidebar/ContentSidebar.js +++ b/src/elements/content-sidebar/ContentSidebar.js @@ -119,7 +119,6 @@ class ContentSidebar extends React.Component { getViewer: noop, hasActivityFeed: false, hasAdditionalTabs: false, - hasBoxAI: false, hasMetadata: false, hasNav: true, hasSkills: false, diff --git a/src/elements/content-sidebar/__tests__/BoxAISidebar.test.js b/src/elements/content-sidebar/__tests__/BoxAISidebar.test.js deleted file mode 100644 index 307e208f8b..0000000000 --- a/src/elements/content-sidebar/__tests__/BoxAISidebar.test.js +++ /dev/null @@ -1,29 +0,0 @@ -import * as React from 'react'; -import { shallow } from 'enzyme'; -import { BoxAISidebarComponent } from '../BoxAISidebar'; - -describe('elements/content-sidebar/BoxAISidebar', () => { - const getWrapper = (props = {}) => - shallow(); - - describe('constructor()', () => { - let onReadyMetric; - beforeEach(() => { - const wrapper = getWrapper(); - ({ onReadyMetric } = wrapper.instance().props.logger); - }); - - test('should emit when js loaded', () => { - expect(onReadyMetric).toHaveBeenCalledWith({ - endMarkName: expect.any(String), - }); - }); - }); - - describe('render()', () => { - test('should render the boxai sidebar', () => { - const wrapper = getWrapper(); - expect(wrapper).toMatchSnapshot(); - }); - }); -}); diff --git a/src/elements/content-sidebar/__tests__/BoxAISidebar.test.tsx b/src/elements/content-sidebar/__tests__/BoxAISidebar.test.tsx new file mode 100644 index 0000000000..18b69db22a --- /dev/null +++ b/src/elements/content-sidebar/__tests__/BoxAISidebar.test.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { screen, render } from '../../../test-utils/testing-library'; +import BoxAISidebarComponent, { BoxAISidebarProps } from '../BoxAISidebar'; + +describe('elements/content-sidebar/BoxAISidebar', () => { + const renderComponent = (props = {}) => { + const defaultProps = { + onExpandPressed: jest.fn(), + } satisfies BoxAISidebarProps; + + render(); + }; + + test('should render title', () => { + renderComponent(); + + expect(screen.getByRole('heading', { level: 3, name: 'Box AI' })).toBeInTheDocument(); + }); +}); diff --git a/src/elements/content-sidebar/stories/ContentSidebar.stories.js b/src/elements/content-sidebar/stories/ContentSidebar.stories.js index b8e36cd8b9..f208526c76 100644 --- a/src/elements/content-sidebar/stories/ContentSidebar.stories.js +++ b/src/elements/content-sidebar/stories/ContentSidebar.stories.js @@ -17,7 +17,6 @@ export default { features: global.FEATURES, fileId: global.FILE_ID, hasActivityFeed: true, - hasBoxAI: true, hasMetadata: true, hasSkills: true, hasVersions: true, From bc720b92ff329a595016a56a370453c1748d229e Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Sun, 29 Sep 2024 16:59:40 +0200 Subject: [PATCH 07/14] feat(boxai-sidebar): Storybook test fixes --- src/elements/content-sidebar/BoxAISidebar.tsx | 6 +++--- src/elements/content-sidebar/ContentSidebar.js | 2 +- src/elements/content-sidebar/SidebarUtils.js | 3 ++- src/elements/content-sidebar/stories/ContentSidebar.mdx | 1 - .../stories/tests/BoxAISidebar-visual.stories.tsx | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/elements/content-sidebar/BoxAISidebar.tsx b/src/elements/content-sidebar/BoxAISidebar.tsx index 16073c1460..1ddf3a6a5f 100644 --- a/src/elements/content-sidebar/BoxAISidebar.tsx +++ b/src/elements/content-sidebar/BoxAISidebar.tsx @@ -24,7 +24,7 @@ export interface BoxAISidebarProps { onExpandPressed: () => void; } -function BoxAISideBar() { +function BoxAISidebar() { const { formatMessage } = useIntl(); return ( @@ -38,7 +38,7 @@ function BoxAISideBar() { ); } -export { BoxAISideBar as BoxAISidebarComponent }; +export { BoxAISidebar as BoxAISideBarComponent }; export default flow([withLogger(ORIGIN_BOXAI_SIDEBAR), withErrorBoundary(ORIGIN_BOXAI_SIDEBAR), withAPIContext])( - BoxAISideBar, + BoxAISidebar, ); diff --git a/src/elements/content-sidebar/ContentSidebar.js b/src/elements/content-sidebar/ContentSidebar.js index 0c8bbaa07f..c934d5483f 100644 --- a/src/elements/content-sidebar/ContentSidebar.js +++ b/src/elements/content-sidebar/ContentSidebar.js @@ -388,7 +388,7 @@ class ContentSidebar extends React.Component { getViewer={getViewer} hasActivityFeed={hasActivityFeed} hasAdditionalTabs={hasAdditionalTabs} - hasBoxAI={isFeatureEnabled(props.features, 'boxai.sidebar.enabled')} + hasBoxAI={isFeatureEnabled(this.props.features, 'boxai.sidebar.enabled')} hasNav={hasNav} hasMetadata={hasMetadata} hasSkills={hasSkills} diff --git a/src/elements/content-sidebar/SidebarUtils.js b/src/elements/content-sidebar/SidebarUtils.js index d28926cf61..cef924901e 100644 --- a/src/elements/content-sidebar/SidebarUtils.js +++ b/src/elements/content-sidebar/SidebarUtils.js @@ -24,6 +24,7 @@ import { import type { MetadataSidebarProps } from './MetadataSidebar'; import type { MetadataEditor } from '../../common/types/metadata'; import type { BoxItem } from '../../common/types/core'; +import { isFeatureEnabled } from '../common/feature-checking'; class SidebarUtils { /** @@ -57,7 +58,7 @@ class SidebarUtils { * @return {Boolean} true if we should render */ static canHaveBoxAISidebar(props: ContentSidebarProps): boolean { - return !!props.hasBoxAI; + return isFeatureEnabled(props.features, 'boxai.sidebar.enabled'); } /** diff --git a/src/elements/content-sidebar/stories/ContentSidebar.mdx b/src/elements/content-sidebar/stories/ContentSidebar.mdx index 487a828457..384bc31ad2 100644 --- a/src/elements/content-sidebar/stories/ContentSidebar.mdx +++ b/src/elements/content-sidebar/stories/ContentSidebar.mdx @@ -21,7 +21,6 @@ var ContentSidebar = require('./ContentSidebar').default; features={FEATURES} fileId={FILE_ID} hasActivityFeed - hasBoxAI hasMetadata hasSkills hasVersions diff --git a/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx b/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx index d5c4eda186..3ad83e972b 100644 --- a/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx +++ b/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx @@ -17,7 +17,7 @@ export default { }, }; -export const SimpleBoxAISidebarCheck: StoryObj = { +export const BoxAIInSidebar: StoryObj = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const sidebar = await canvas.findByRole('heading', { name: 'Box AI' }, { timeout: 5000 }); From 48053d1a4402cacc48c3917d0c20b28be0466b8f Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Mon, 30 Sep 2024 21:27:16 +0200 Subject: [PATCH 08/14] feat(boxai-sidebar): Fixed UT --- .../content-sidebar/__tests__/SidebarUtils.test.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/elements/content-sidebar/__tests__/SidebarUtils.test.js b/src/elements/content-sidebar/__tests__/SidebarUtils.test.js index 89ea650d46..1aba678580 100644 --- a/src/elements/content-sidebar/__tests__/SidebarUtils.test.js +++ b/src/elements/content-sidebar/__tests__/SidebarUtils.test.js @@ -10,9 +10,11 @@ import { SIDEBAR_VIEW_DOCGEN, SIDEBAR_VIEW_BOXAI, } from '../../../constants'; +import { isFeatureEnabled } from '../../common/feature-checking'; jest.mock('../../common/async-load', () => () => 'LoadableComponent'); jest.mock('../SidebarLoadingError', () => 'sidebar-loading-error'); +jest.mock('../../common/feature-checking'); describe('elements/content-sidebar/SidebarUtil', () => { describe('canHaveSidebar()', () => { @@ -50,7 +52,8 @@ describe('elements/content-sidebar/SidebarUtil', () => { expect(SidebarUtils.canHaveSidebar({ hasActivityFeed: true })).toBeTruthy(); }); test('should return true when box ai feed should render', () => { - expect(SidebarUtils.canHaveSidebar({ hasBoxAI: true })).toBeTruthy(); + isFeatureEnabled.mockReturnValueOnce(true); + expect(SidebarUtils.canHaveSidebar({})).toBeTruthy(); }); test('should return true when notices should render', () => { expect( @@ -126,8 +129,9 @@ describe('elements/content-sidebar/SidebarUtil', () => { test('should return false when hasBoxAI is false', () => { expect(SidebarUtils.canHaveBoxAISidebar({ hasBoxAI: false })).toBeFalsy(); }); - test('should return true when hasBoxAI is true', () => { - expect(SidebarUtils.canHaveBoxAISidebar({ hasBoxAI: true }, {})).toBeTruthy(); + test('should return true when isFeatureEnabled returns true', () => { + isFeatureEnabled.mockReturnValueOnce(true); + expect(SidebarUtils.canHaveBoxAISidebar({})).toBeTruthy(); }); }); describe('canHaveMetadataSidebar()', () => { From 70a2bc8ef43c1b6f159d1dc6b183acc7dee5bd1a Mon Sep 17 00:00:00 2001 From: Greg Wong Date: Mon, 30 Sep 2024 16:18:36 -0400 Subject: [PATCH 09/14] chore(i18n): translations --- i18n/en-US.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/i18n/en-US.properties b/i18n/en-US.properties index 4bc2922237..28bfa5c58d 100644 --- a/i18n/en-US.properties +++ b/i18n/en-US.properties @@ -517,7 +517,7 @@ be.messageCenter.events = Events # Displayed when there are no posts to display be.messageCenter.noPosts = There are no posts for this category at the moment. # Error message for preview not loading an image -be.messageCenter.previewError = Sorry, we're having trouble showing this image. +be.messageCenter.previewError = Sorry, we're having trouble showing this image. # Title for product category be.messageCenter.product = Product # Title for the message center modal @@ -1007,9 +1007,9 @@ boxui.contentExplorer.name = Name # Text shown on button used to create a new folder boxui.contentExplorer.newFolder = New Folder # Text shown to indicate the number of folders selected -boxui.contentExplorer.numFoldersSelected = {numSelected, plural, =0 {0 folders selected} one {1 folder selected} other {# folders selected} } +boxui.contentExplorer.numFoldersSelected = {numSelected, plural, =0 {0 folders selected} one {1 folder selected} other {# folders selected} } # Text shown to indicate the number of items selected with Include Subfolders feature -boxui.contentExplorer.numItemsSelected = {numSelected, plural, =0 {0 items selected} one {1 item selected} other {# items selected} } +boxui.contentExplorer.numItemsSelected = {numSelected, plural, =0 {0 items selected} one {1 item selected} other {# items selected} } # Text shown to indicate the number of items selected boxui.contentExplorer.numSelected = {numSelected} Selected # Results label for number of items on list when it's just 1 From 4cb09b2b64016ed582ea2afe3204ab0587aa3461 Mon Sep 17 00:00:00 2001 From: kkuliczkowski Date: Wed, 2 Oct 2024 13:14:32 +0200 Subject: [PATCH 10/14] Resolved issue with flow check --- .../content-sidebar/BoxAISidebar.js.flow | 25 +++++++++++++++++++ src/elements/content-sidebar/BoxAISidebar.tsx | 13 +++++++--- .../content-sidebar/ContentSidebar.js | 2 +- 3 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 src/elements/content-sidebar/BoxAISidebar.js.flow diff --git a/src/elements/content-sidebar/BoxAISidebar.js.flow b/src/elements/content-sidebar/BoxAISidebar.js.flow new file mode 100644 index 0000000000..2ff3fc3b84 --- /dev/null +++ b/src/elements/content-sidebar/BoxAISidebar.js.flow @@ -0,0 +1,25 @@ +/** + * Flowtype definitions for BoxAISidebar.tsx + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.21.0 + */ + +import * as React from "react"; +import flow from "lodash/flow"; +import { useIntl } from "react-intl"; +import SidebarContent from "./SidebarContent"; +import { withAPIContext } from "../common/api-context"; +import { withErrorBoundary } from "../common/error-boundary"; +import { withLogger } from "../common/logger"; +import { ORIGIN_BOXAI_SIDEBAR, SIDEBAR_VIEW_BOXAI } from "../../constants"; +import { EVENT_JS_READY } from "../common/logger/constants"; +import { mark } from "../../utils/performance"; +import messages from "../common/messages"; +declare var MARK_NAME_JS_READY: string; +export interface BoxAISidebarProps { + onExpandPressed: () => void; +} +declare function BoxAISidebar(): void; +declare export { BoxAISidebar as BoxAISideBarComponent }; +declare var flowFn: Function; +declare export default typeof flowFn; diff --git a/src/elements/content-sidebar/BoxAISidebar.tsx b/src/elements/content-sidebar/BoxAISidebar.tsx index 1ddf3a6a5f..f222dc67e4 100644 --- a/src/elements/content-sidebar/BoxAISidebar.tsx +++ b/src/elements/content-sidebar/BoxAISidebar.tsx @@ -16,7 +16,7 @@ import { mark } from '../../utils/performance'; import messages from '../common/messages'; -const MARK_NAME_JS_READY = `${ORIGIN_BOXAI_SIDEBAR}_${EVENT_JS_READY}`; +const MARK_NAME_JS_READY: string = `${ORIGIN_BOXAI_SIDEBAR}_${EVENT_JS_READY}`; mark(MARK_NAME_JS_READY); @@ -39,6 +39,11 @@ function BoxAISidebar() { } export { BoxAISidebar as BoxAISideBarComponent }; -export default flow([withLogger(ORIGIN_BOXAI_SIDEBAR), withErrorBoundary(ORIGIN_BOXAI_SIDEBAR), withAPIContext])( - BoxAISidebar, -); + +const flowFn: Function = flow([ + withLogger(ORIGIN_BOXAI_SIDEBAR), + withErrorBoundary(ORIGIN_BOXAI_SIDEBAR), + withAPIContext, +]); + +export default flowFn; diff --git a/src/elements/content-sidebar/ContentSidebar.js b/src/elements/content-sidebar/ContentSidebar.js index c934d5483f..1b4d238895 100644 --- a/src/elements/content-sidebar/ContentSidebar.js +++ b/src/elements/content-sidebar/ContentSidebar.js @@ -388,7 +388,7 @@ class ContentSidebar extends React.Component { getViewer={getViewer} hasActivityFeed={hasActivityFeed} hasAdditionalTabs={hasAdditionalTabs} - hasBoxAI={isFeatureEnabled(this.props.features, 'boxai.sidebar.enabled')} + hasBoxAI={isFeatureEnabledInContext(this.props.features, 'boxai.sidebar.enabled')} hasNav={hasNav} hasMetadata={hasMetadata} hasSkills={hasSkills} From a1914819bf3083dbdb3730ad505dfdc8efdbe69c Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Wed, 2 Oct 2024 16:09:46 +0200 Subject: [PATCH 11/14] feat(boxai-sidebar): flow check fixes --- src/elements/content-sidebar/BoxAISidebar.js.flow | 6 +++--- src/elements/content-sidebar/BoxAISidebar.tsx | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/elements/content-sidebar/BoxAISidebar.js.flow b/src/elements/content-sidebar/BoxAISidebar.js.flow index 2ff3fc3b84..ad0a0b6c00 100644 --- a/src/elements/content-sidebar/BoxAISidebar.js.flow +++ b/src/elements/content-sidebar/BoxAISidebar.js.flow @@ -20,6 +20,6 @@ export interface BoxAISidebarProps { onExpandPressed: () => void; } declare function BoxAISidebar(): void; -declare export { BoxAISidebar as BoxAISideBarComponent }; -declare var flowFn: Function; -declare export default typeof flowFn; +declare export { BoxAISidebar as BoxAISidebarComponent }; +declare var BoxAISidebarDefaultExport: typeof withAPIContext; +declare export default typeof BoxAISidebarDefaultExport; diff --git a/src/elements/content-sidebar/BoxAISidebar.tsx b/src/elements/content-sidebar/BoxAISidebar.tsx index f222dc67e4..659cd5a87b 100644 --- a/src/elements/content-sidebar/BoxAISidebar.tsx +++ b/src/elements/content-sidebar/BoxAISidebar.tsx @@ -38,12 +38,12 @@ function BoxAISidebar() { ); } -export { BoxAISidebar as BoxAISideBarComponent }; +export { BoxAISidebar as BoxAISidebarComponent }; -const flowFn: Function = flow([ +const BoxAISidebarDefaultExport: typeof withAPIContext = flow([ withLogger(ORIGIN_BOXAI_SIDEBAR), withErrorBoundary(ORIGIN_BOXAI_SIDEBAR), withAPIContext, -]); +])(BoxAISidebar); -export default flowFn; +export default BoxAISidebarDefaultExport; From 69072b098d00af8609175913a3eaac6af69ac6dd Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Wed, 2 Oct 2024 16:45:53 +0200 Subject: [PATCH 12/14] chore(boxai-sidebar): BoxAISidebar.stories.tsx small fix --- src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx b/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx index f61657e3af..bd667b93e8 100644 --- a/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx +++ b/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx @@ -1,4 +1,3 @@ -// @flow import ContentSidebar from '../ContentSidebar'; const mockFeatures = { From 87854f82f80386b763abbf4e929bbe65243e26f7 Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Thu, 3 Oct 2024 16:45:52 +0200 Subject: [PATCH 13/14] chore(boxai-sidebar): PR fixes --- src/elements/content-sidebar/ContentSidebar.js | 3 +-- src/elements/content-sidebar/SidebarNav.js | 4 ++-- src/elements/content-sidebar/SidebarUtils.js | 8 ++++---- .../__tests__/ContentSidebar.test.js | 15 --------------- .../content-sidebar/__tests__/SidebarNav.test.js | 2 +- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/elements/content-sidebar/ContentSidebar.js b/src/elements/content-sidebar/ContentSidebar.js index 1b4d238895..1217f6d0e6 100644 --- a/src/elements/content-sidebar/ContentSidebar.js +++ b/src/elements/content-sidebar/ContentSidebar.js @@ -29,6 +29,7 @@ import { import { withLogger } from '../common/logger'; import type { ActivitySidebarProps } from './ActivitySidebar'; +import type { BoxAISidebarProps } from './BoxAISidebar'; import type { DetailsSidebarProps } from './DetailsSidebar'; import type { DocGenSidebarProps } from './DocGenSidebar/DocGenSidebar'; import type { MetadataSidebarProps } from './MetadataSidebar'; @@ -45,7 +46,6 @@ import '../common/fonts.scss'; import '../common/base.scss'; import '../common/modal.scss'; import './ContentSidebar.scss'; -import type { BoxAISidebarProps } from './BoxAISidebar'; type Props = { activitySidebarProps: ActivitySidebarProps, @@ -257,7 +257,6 @@ class ContentSidebar extends React.Component { fetchMetadata(): void { const { file }: State = this.state; const { metadataSidebarProps }: Props = this.props; - // eslint-disable-next-line no-shadow const { isFeatureEnabled = true }: MetadataSidebarProps = metadataSidebarProps; // Only fetch metadata if we think that the file may have metadata on it diff --git a/src/elements/content-sidebar/SidebarNav.js b/src/elements/content-sidebar/SidebarNav.js index 72764c1da7..14972ca38c 100644 --- a/src/elements/content-sidebar/SidebarNav.js +++ b/src/elements/content-sidebar/SidebarNav.js @@ -22,12 +22,12 @@ import SidebarToggle from './SidebarToggle'; import messages from '../common/messages'; import { SIDEBAR_NAV_TARGETS } from '../common/interactionTargets'; import { - SIDEBAR_VIEW_DOCGEN, SIDEBAR_VIEW_ACTIVITY, + SIDEBAR_VIEW_BOXAI, SIDEBAR_VIEW_DETAILS, + SIDEBAR_VIEW_DOCGEN, SIDEBAR_VIEW_METADATA, SIDEBAR_VIEW_SKILLS, - SIDEBAR_VIEW_BOXAI, } from '../../constants'; import { useFeatureConfig } from '../common/feature-checking'; import type { NavigateOptions, AdditionalSidebarTab } from './flowTypes'; diff --git a/src/elements/content-sidebar/SidebarUtils.js b/src/elements/content-sidebar/SidebarUtils.js index cef924901e..054e0abe75 100644 --- a/src/elements/content-sidebar/SidebarUtils.js +++ b/src/elements/content-sidebar/SidebarUtils.js @@ -21,10 +21,10 @@ import { SIDEBAR_VIEW_METADATA_REDESIGN, SIDEBAR_VIEW_BOXAI, } from '../../constants'; +import { isFeatureEnabled } from '../common/feature-checking'; import type { MetadataSidebarProps } from './MetadataSidebar'; import type { MetadataEditor } from '../../common/types/metadata'; import type { BoxItem } from '../../common/types/core'; -import { isFeatureEnabled } from '../common/feature-checking'; class SidebarUtils { /** @@ -124,12 +124,12 @@ class SidebarUtils { */ static shouldRenderMetadataSidebar(props: ContentSidebarProps, editors?: Array): boolean { const { metadataSidebarProps = {} }: ContentSidebarProps = props; - // eslint-disable-next-line no-shadow - const { isFeatureEnabled = true }: MetadataSidebarProps = metadataSidebarProps; + const { isFeatureEnabled: isFeatureEnabledMetadataSidebarProp = true }: MetadataSidebarProps = + metadataSidebarProps; return ( SidebarUtils.canHaveMetadataSidebar(props) && - (isFeatureEnabled || (Array.isArray(editors) && editors.length > 0)) + (isFeatureEnabledMetadataSidebarProp || (Array.isArray(editors) && editors.length > 0)) ); } diff --git a/src/elements/content-sidebar/__tests__/ContentSidebar.test.js b/src/elements/content-sidebar/__tests__/ContentSidebar.test.js index 393e9a718a..a6ccf692d7 100644 --- a/src/elements/content-sidebar/__tests__/ContentSidebar.test.js +++ b/src/elements/content-sidebar/__tests__/ContentSidebar.test.js @@ -78,21 +78,6 @@ describe('elements/content-sidebar/ContentSidebar', () => { expect(instance.fetchFile).not.toBeCalled(); expect(instance.setState).not.toBeCalled(); }); - - test('should not fetch the file data if the id has not changed', () => { - const wrapper = getWrapper({ fileId: '123' }); - const instance = wrapper.instance(); - const newProps = { fileId: '123' }; - - instance.fetchFile = jest.fn(); - instance.setState({ view: 'activityFeed' }); - instance.setState = jest.fn(); - - instance.componentDidUpdate(newProps); - - expect(instance.fetchFile).not.toBeCalled(); - expect(instance.setState).not.toBeCalled(); - }); }); describe('fetchFile()', () => { diff --git a/src/elements/content-sidebar/__tests__/SidebarNav.test.js b/src/elements/content-sidebar/__tests__/SidebarNav.test.js index b873b21b9e..b9ffe0b07f 100644 --- a/src/elements/content-sidebar/__tests__/SidebarNav.test.js +++ b/src/elements/content-sidebar/__tests__/SidebarNav.test.js @@ -90,9 +90,9 @@ describe('elements/content-sidebar/SidebarNav', () => { test('should have multiple tabs', () => { const props = { hasActivity: true, + hasBoxAI: true, hasMetadata: true, hasSkills: true, - hasBoxAI: true, }; const wrapper = getWrapper(props, 'activity'); expect(wrapper.find(IconMagicWand)).toHaveLength(1); From f2bd75da78363e9dca81c7903a88923f18ca2d9f Mon Sep 17 00:00:00 2001 From: Danila Rubleuski Date: Thu, 3 Oct 2024 23:41:25 +0200 Subject: [PATCH 14/14] chore(boxai-sidebar): PR fixes --- src/elements/content-sidebar/ContentSidebar.js | 1 - src/elements/content-sidebar/Sidebar.js | 1 - 2 files changed, 2 deletions(-) diff --git a/src/elements/content-sidebar/ContentSidebar.js b/src/elements/content-sidebar/ContentSidebar.js index 1217f6d0e6..4593e22804 100644 --- a/src/elements/content-sidebar/ContentSidebar.js +++ b/src/elements/content-sidebar/ContentSidebar.js @@ -387,7 +387,6 @@ class ContentSidebar extends React.Component { getViewer={getViewer} hasActivityFeed={hasActivityFeed} hasAdditionalTabs={hasAdditionalTabs} - hasBoxAI={isFeatureEnabledInContext(this.props.features, 'boxai.sidebar.enabled')} hasNav={hasNav} hasMetadata={hasMetadata} hasSkills={hasSkills} diff --git a/src/elements/content-sidebar/Sidebar.js b/src/elements/content-sidebar/Sidebar.js index b7bc240ea4..658522db2d 100644 --- a/src/elements/content-sidebar/Sidebar.js +++ b/src/elements/content-sidebar/Sidebar.js @@ -50,7 +50,6 @@ type Props = { getViewer: Function, hasActivityFeed: boolean, hasAdditionalTabs: boolean, - hasBoxAI: boolean, hasMetadata: boolean, hasNav: boolean, hasSkills: boolean,