diff --git a/src/elements/common/types/SidebarNavigation.js.flow b/src/elements/common/types/SidebarNavigation.js.flow index 9f6fbece15..1dc910587e 100644 --- a/src/elements/common/types/SidebarNavigation.js.flow +++ b/src/elements/common/types/SidebarNavigation.js.flow @@ -5,10 +5,12 @@ export const ViewType = Object.freeze({ BOXAI: 'boxai', - SKILLS: 'skills', - ACTIVITY: 'activity', DETAILS: 'details', METADATA: 'metadata', + METADATA_REDESIGN: 'metadata_redesign', + SKILLS: 'skills', + ACTIVITY: 'activity', + VERSIONS: 'versions', DOCGEN: 'docgen', }); @@ -22,11 +24,12 @@ export type ViewTypeValues = $Values; export type FeedEntryTypeValues = $Values; export type SidebarNavigation = { - sidebar: ViewTypeValues, - versionId?: string, activeFeedEntryType?: FeedEntryTypeValues, activeFeedEntryId?: string, fileVersionId?: string, + filteredTemplateIds?: string, + sidebar: ViewTypeValues, + versionId?: string, }; export type InternalSidebarNavigation = SidebarNavigation & { diff --git a/src/elements/common/types/SidebarNavigation.ts b/src/elements/common/types/SidebarNavigation.ts index e42659ef66..c3a7170c73 100644 --- a/src/elements/common/types/SidebarNavigation.ts +++ b/src/elements/common/types/SidebarNavigation.ts @@ -1,9 +1,11 @@ export enum ViewType { - BOXAI = 'boxai', SKILLS = 'skills', - ACTIVITY = 'activity', DETAILS = 'details', METADATA = 'metadata', + METADATA_REDESIGN = 'metadata_redesign', + BOXAI = 'boxai', + ACTIVITY = 'activity', + VERSIONS = 'versions', DOCGEN = 'docgen', } @@ -18,6 +20,11 @@ type VersionSidebarView = { versionId: string; }; +export type MetadataSidebarView = { + sidebar: ViewType.METADATA | ViewType.METADATA_REDESIGN; + filteredTemplateIds?: string; +}; + export type ActivityAnnotationsSidebarView = { sidebar: ViewType.ACTIVITY; activeFeedEntryType: FeedEntryType.ANNOTATIONS; @@ -35,6 +42,7 @@ export type SidebarNavigation = sidebar: ViewType; } | VersionSidebarView + | MetadataSidebarView | ActivityCommentsSidebarView | ActivityAnnotationsSidebarView; diff --git a/src/elements/content-preview/PreviewNavigation.js b/src/elements/content-preview/PreviewNavigation.js index 3f333de427..46d10ddb34 100644 --- a/src/elements/content-preview/PreviewNavigation.js +++ b/src/elements/content-preview/PreviewNavigation.js @@ -14,16 +14,26 @@ import PlainButton from '../../components/plain-button/PlainButton'; import messages from '../common/messages'; import type { BoxItem } from '../../common/types/core'; import { SIDEBAR_VIEW_METADATA } from '../../constants'; +import type { InternalSidebarNavigation, InternalSidebarNavigationHandler } from '../common/types/SidebarNavigation'; type Props = { collection: Array, currentIndex: number, intl: IntlShape, + internalSidebarNavigation?: InternalSidebarNavigation, + internalSidebarNavigationHandler?: InternalSidebarNavigationHandler, onNavigateLeft: Function, onNavigateRight: Function, + routerDisabled?: boolean, }; -const PreviewNavigation = ({ collection = [], currentIndex, intl, onNavigateLeft, onNavigateRight }: Props) => { +const PreviewNavigationWithRouter = ({ + collection = [], + currentIndex, + intl, + onNavigateLeft, + onNavigateRight, +}: Props) => { const hasLeftNavigation = collection.length > 1 && currentIndex > 0 && currentIndex < collection.length; const hasRightNavigation = collection.length > 1 && currentIndex > -1 && currentIndex < collection.length - 1; @@ -48,6 +58,7 @@ const PreviewNavigation = ({ collection = [], currentIndex, intl, onNavigateLeft {hasLeftNavigation && ( { goToActiveSidebarTab(match.params, history); onNavigateLeft(); @@ -61,6 +72,7 @@ const PreviewNavigation = ({ collection = [], currentIndex, intl, onNavigateLeft {hasRightNavigation && ( { goToActiveSidebarTab(match.params, history); onNavigateRight(); @@ -77,5 +89,77 @@ const PreviewNavigation = ({ collection = [], currentIndex, intl, onNavigateLeft ); }; -export { PreviewNavigation as PreviewNavigationComponent }; +const PreviewNavigationWithoutRouter = ({ + collection = [], + currentIndex, + intl, + internalSidebarNavigation, + internalSidebarNavigationHandler, + onNavigateLeft, + onNavigateRight, +}: Props) => { + const hasLeftNavigation = collection.length > 1 && currentIndex > 0 && currentIndex < collection.length; + const hasRightNavigation = collection.length > 1 && currentIndex > -1 && currentIndex < collection.length - 1; + + if (!hasLeftNavigation && !hasRightNavigation) { + return null; + } + + const handleInternalNavigation = () => { + if (internalSidebarNavigationHandler && internalSidebarNavigation && internalSidebarNavigation.sidebar) { + const { sidebar, ...rest } = internalSidebarNavigation; + const hasDeeplink = Object.keys(rest).length > 0; + + if (hasDeeplink && sidebar === SIDEBAR_VIEW_METADATA) { + internalSidebarNavigationHandler(internalSidebarNavigation); + } else { + internalSidebarNavigationHandler({ sidebar }); + } + } + }; + + return ( + <> + {hasLeftNavigation && ( + { + handleInternalNavigation(); + onNavigateLeft(); + }} + title={intl.formatMessage(messages.previousFile)} + type="button" + > + + + )} + {hasRightNavigation && ( + { + handleInternalNavigation(); + onNavigateRight(); + }} + title={intl.formatMessage(messages.nextFile)} + type="button" + > + + + )} + + ); +}; + +const PreviewNavigation = (props: Props) => { + const { routerDisabled = false } = props; + + if (routerDisabled) { + return ; + } + + return ; +}; + export default injectIntl(PreviewNavigation); diff --git a/src/elements/content-preview/__tests__/PreviewNavigation.test.js b/src/elements/content-preview/__tests__/PreviewNavigation.test.js index 6032cac84a..bc53c7d2ce 100644 --- a/src/elements/content-preview/__tests__/PreviewNavigation.test.js +++ b/src/elements/content-preview/__tests__/PreviewNavigation.test.js @@ -1,8 +1,8 @@ import * as React from 'react'; import { Router } from 'react-router-dom'; -import noop from 'lodash/noop'; -import { mount } from 'enzyme'; -import { PreviewNavigationComponent as PreviewNavigation } from '../PreviewNavigation'; +import { render, screen, userEvent } from '../../../test-utils/testing-library'; +import PreviewNavigation from '../PreviewNavigation'; +import { ViewType, FeedEntryType } from '../../common/types/SidebarNavigation'; const historyMockDefault = { location: { pathname: '/activity/tasks/1234', hash: '' }, @@ -18,110 +18,250 @@ const deeplinkedMetadataHistoryMock = { entries: [{}], }; -const getWrapper = ({ - collection = ['a', 'b', 'c'], - historyMock = historyMockDefault, - onNavigateLeft = noop, - onNavigateRight = noop, - ...rest -}) => - mount( +const onNavigateLeftMock = jest.fn(); +const onNavigateRightMock = jest.fn(); +const mockNavigationHandler = jest.fn(); + +const renderComponentWithRouter = (props = {}) => { + const { + collection = ['a', 'b', 'c'], + historyMock = historyMockDefault, + onNavigateLeft = onNavigateLeftMock, + onNavigateRight = onNavigateRightMock, + internalSidebarNavigationHandler = mockNavigationHandler, + ...rest + } = props; + + return render( , ); +}; + +const renderComponentWithoutRouter = (props = {}) => { + const defaultProps = { + collection: ['a', 'b', 'c'], + currentIndex: 1, + onNavigateLeft: onNavigateLeftMock, + onNavigateRight: onNavigateRightMock, + internalSidebarNavigationHandler: mockNavigationHandler, + }; -afterEach(() => { + return render(); +}; + +beforeEach(() => { jest.resetAllMocks(); }); describe('elements/content-preview/PreviewNavigation', () => { describe('render()', () => { test('should render correctly with an empty collection', () => { - const wrapper = getWrapper({ collection: [], currentIndex: 0 }); - expect(wrapper).toMatchSnapshot(); + const { container } = renderComponentWithRouter({ collection: [], currentIndex: 0 }); + expect(container.firstChild).toBeNull(); }); - test.each([0, 1, 9])('should render correctly with a filled collection %i', ({ currentIndex }) => { - const collection = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']; - const wrapper = getWrapper({ collection, currentIndex }); - expect(wrapper).toMatchSnapshot(); - }); + test.each([ + { currentIndex: 0, description: 'first item', expectLeft: false, expectRight: true }, + { currentIndex: 1, description: 'middle item', expectLeft: true, expectRight: true }, + { currentIndex: 9, description: 'last item', expectLeft: true, expectRight: false }, + ])( + 'should render correctly with a filled collection - $description', + ({ currentIndex, expectLeft, expectRight }) => { + const collection = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']; + renderComponentWithRouter({ collection, currentIndex }); + + const leftButton = screen.queryByTestId('preview-navigation-left'); + const rightButton = screen.queryByTestId('preview-navigation-right'); + + if (expectLeft) { + expect(leftButton).toBeInTheDocument(); + } else { + expect(leftButton).not.toBeInTheDocument(); + } + + if (expectRight) { + expect(rightButton).toBeInTheDocument(); + } else { + expect(rightButton).not.toBeInTheDocument(); + } + }, + ); + + test('should render left navigation correctly from tasks deeplinked URL', async () => { + const user = userEvent(); + + renderComponentWithRouter({ + currentIndex: 2, + historyMock: historyMockDefault, + }); - test('should render left navigation correctly from tasks deeplinked URL', () => { - const onNavigateLeftMock = jest.fn(); - const wrapper = getWrapper({ currentIndex: 2, onNavigateLeft: onNavigateLeftMock }); + const leftButton = screen.getByTestId('preview-navigation-left'); + expect(leftButton).toBeInTheDocument(); - expect(wrapper.find('PlainButton')).toHaveLength(1); - wrapper.find('PlainButton').simulate('click'); + await user.click(leftButton); - expect(historyMockDefault.push).toBeCalledTimes(1); - expect(historyMockDefault.push).toBeCalledWith('/activity'); + expect(historyMockDefault.push).toHaveBeenCalledTimes(1); + expect(historyMockDefault.push).toHaveBeenCalledWith('/activity'); expect(onNavigateLeftMock).toHaveBeenCalled(); }); - test('should render right navigation correctly from tasks deeplinked URL ', () => { - const onNavigateRightMock = jest.fn(); - const wrapper = getWrapper({ currentIndex: 0, onNavigateRight: onNavigateRightMock }); + test('should render right navigation correctly from tasks deeplinked URL', async () => { + const user = userEvent(); - expect(wrapper.find('PlainButton')).toHaveLength(1); - wrapper.find('PlainButton').simulate('click'); + renderComponentWithRouter({ + currentIndex: 0, + historyMock: historyMockDefault, + }); - expect(historyMockDefault.push).toBeCalledTimes(1); - expect(historyMockDefault.push).toBeCalledWith('/activity'); - expect(onNavigateRightMock).toHaveBeenCalled(); - }); - test('should render navigation correctly from comments deeplinked URL ', () => { - const onNavigateRightMock = jest.fn(); - const wrapper = getWrapper({ currentIndex: 0, onNavigateRight: onNavigateRightMock }); + const rightButton = screen.getByTestId('preview-navigation-right'); + expect(rightButton).toBeInTheDocument(); - expect(wrapper.find('PlainButton')).toHaveLength(1); - wrapper.find('PlainButton').simulate('click'); + await user.click(rightButton); - expect(historyMockDefault.push).toBeCalledTimes(1); - expect(historyMockDefault.push).toBeCalledWith('/activity'); + expect(historyMockDefault.push).toHaveBeenCalledTimes(1); + expect(historyMockDefault.push).toHaveBeenCalledWith('/activity'); expect(onNavigateRightMock).toHaveBeenCalled(); }); - test('should render right navigation correctly from metadata deeplinked URL ', () => { - const onNavigateRightMock = jest.fn(); - const wrapper = getWrapper({ + test('should render right navigation correctly from metadata deeplinked URL', async () => { + const user = userEvent(); + + renderComponentWithRouter({ currentIndex: 0, historyMock: deeplinkedMetadataHistoryMock, - onNavigateRight: onNavigateRightMock, }); - expect(wrapper.find('PlainButton')).toHaveLength(1); - wrapper.find('PlainButton').simulate('click'); + const rightButton = screen.getByTestId('preview-navigation-right'); + expect(rightButton).toBeInTheDocument(); + + await user.click(rightButton); - expect(deeplinkedMetadataHistoryMock.push).toBeCalledTimes(1); - expect(deeplinkedMetadataHistoryMock.push).toBeCalledWith('/metadata/filteredTemplates/123,124'); + expect(deeplinkedMetadataHistoryMock.push).toHaveBeenCalledTimes(1); + expect(deeplinkedMetadataHistoryMock.push).toHaveBeenCalledWith('/metadata/filteredTemplates/123,124'); expect(onNavigateRightMock).toHaveBeenCalled(); }); - test('should render left navigation correctly from metadata deeplinked URL ', () => { - const onNavigateLeftMock = jest.fn(); - const wrapper = getWrapper({ + test('should render left navigation correctly from metadata deeplinked URL', async () => { + const user = userEvent(); + + renderComponentWithRouter({ currentIndex: 2, historyMock: deeplinkedMetadataHistoryMock, - onNavigateLeft: onNavigateLeftMock, }); - expect(wrapper.find('PlainButton')).toHaveLength(1); - wrapper.find('PlainButton').simulate('click'); + const leftButton = screen.getByTestId('preview-navigation-left'); + expect(leftButton).toBeInTheDocument(); + + await user.click(leftButton); - expect(deeplinkedMetadataHistoryMock.push).toBeCalledTimes(1); - expect(deeplinkedMetadataHistoryMock.push).toBeCalledWith('/metadata/filteredTemplates/123,124'); + expect(deeplinkedMetadataHistoryMock.push).toHaveBeenCalledTimes(1); + expect(deeplinkedMetadataHistoryMock.push).toHaveBeenCalledWith('/metadata/filteredTemplates/123,124'); expect(onNavigateLeftMock).toHaveBeenCalled(); }); }); + + describe('when routerDisabled is true', () => { + test('should render correctly without router', () => { + renderComponentWithoutRouter({ currentIndex: 1 }); + + expect(screen.getByTestId('preview-navigation-left')).toBeInTheDocument(); + expect(screen.getByTestId('preview-navigation-right')).toBeInTheDocument(); + }); + + test('should call internalSidebarNavigationHandler when left navigation button is clicked', async () => { + const mockInternalSidebarNavigation = { + sidebar: ViewType.ACTIVITY, + activeFeedEntryType: FeedEntryType.COMMENTS, + activeFeedEntryId: '123', + }; + const user = userEvent(); + + renderComponentWithoutRouter({ + internalSidebarNavigation: mockInternalSidebarNavigation, + }); + + const leftButton = screen.getByTestId('preview-navigation-left'); + await user.click(leftButton); + + expect(mockNavigationHandler).toHaveBeenCalledTimes(1); + expect(mockNavigationHandler).toHaveBeenCalledWith({ sidebar: ViewType.ACTIVITY }); + expect(onNavigateLeftMock).toHaveBeenCalledTimes(1); + expect(onNavigateRightMock).not.toHaveBeenCalled(); + }); + + test('should call internalSidebarNavigationHandler when right navigation button is clicked', async () => { + const mockInternalSidebarNavigation = { + sidebar: ViewType.ACTIVITY, + activeFeedEntryType: FeedEntryType.COMMENTS, + activeFeedEntryId: '123', + }; + const user = userEvent(); + + renderComponentWithoutRouter({ + internalSidebarNavigation: mockInternalSidebarNavigation, + }); + + const rightButton = screen.getByTestId('preview-navigation-right'); + await user.click(rightButton); + + expect(mockNavigationHandler).toHaveBeenCalledTimes(1); + expect(mockNavigationHandler).toHaveBeenCalledWith({ sidebar: ViewType.ACTIVITY }); + expect(onNavigateRightMock).toHaveBeenCalledTimes(1); + expect(onNavigateLeftMock).not.toHaveBeenCalled(); + }); + + test('should call navigation handler with metadata deeplinks when left navigation button is clicked', async () => { + const mockInternalSidebarNavigation = { + sidebar: ViewType.METADATA, + filteredTemplateIds: '123,124', + }; + const user = userEvent(); + + renderComponentWithoutRouter({ + internalSidebarNavigation: mockInternalSidebarNavigation, + }); + + const leftButton = screen.getByTestId('preview-navigation-left'); + await user.click(leftButton); + + expect(mockNavigationHandler).toHaveBeenCalledTimes(1); + expect(mockNavigationHandler).toHaveBeenCalledWith({ + sidebar: ViewType.METADATA, + filteredTemplateIds: '123,124', + }); + expect(onNavigateLeftMock).toHaveBeenCalledTimes(1); + expect(onNavigateRightMock).not.toHaveBeenCalled(); + }); + + test('should call navigation handler with metadata deeplinks when right navigation button is clicked', async () => { + const mockInternalSidebarNavigation = { + sidebar: ViewType.METADATA, + filteredTemplateIds: '123,124', + }; + const user = userEvent(); + + renderComponentWithoutRouter({ + internalSidebarNavigation: mockInternalSidebarNavigation, + }); + + const rightButton = screen.getByTestId('preview-navigation-right'); + await user.click(rightButton); + + expect(mockNavigationHandler).toHaveBeenCalledTimes(1); + expect(mockNavigationHandler).toHaveBeenCalledWith({ + sidebar: ViewType.METADATA, + filteredTemplateIds: '123,124', + }); + expect(onNavigateRightMock).toHaveBeenCalledTimes(1); + expect(onNavigateLeftMock).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/elements/content-preview/__tests__/__snapshots__/PreviewNavigation.test.js.snap b/src/elements/content-preview/__tests__/__snapshots__/PreviewNavigation.test.js.snap deleted file mode 100644 index c969196427..0000000000 --- a/src/elements/content-preview/__tests__/__snapshots__/PreviewNavigation.test.js.snap +++ /dev/null @@ -1,361 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`elements/content-preview/PreviewNavigation render() should render correctly with a filled collection 0 1`] = ` - - - -`; - -exports[`elements/content-preview/PreviewNavigation render() should render correctly with a filled collection 1 1`] = ` - - - -`; - -exports[`elements/content-preview/PreviewNavigation render() should render correctly with a filled collection 9 1`] = ` - - - -`; - -exports[`elements/content-preview/PreviewNavigation render() should render correctly with an empty collection 1`] = ` - - - -`; diff --git a/src/elements/content-sidebar/__tests__/AddTaskButton.test.js b/src/elements/content-sidebar/__tests__/AddTaskButton.test.js index 799d63a379..dbe5f97d34 100644 --- a/src/elements/content-sidebar/__tests__/AddTaskButton.test.js +++ b/src/elements/content-sidebar/__tests__/AddTaskButton.test.js @@ -1,6 +1,7 @@ import * as React from 'react'; import { render, screen, userEvent } from '../../../test-utils/testing-library'; import { AddTaskButtonComponent as AddTaskButton } from '../AddTaskButton'; +import { ViewType, FeedEntryType } from '../../common/types/SidebarNavigation'; jest.mock('../AddTaskMenu', () => ({ onMenuItemClick, isDisabled, setAddTaskButtonRef }) => (
@@ -51,7 +52,7 @@ describe('elements/content-sidebar/AddTaskButton', () => { }; beforeEach(() => { - jest.clearAllMocks(); + jest.resetAllMocks(); }); test('should call history.replace state with force open state when task menu items are clicked', async () => { @@ -109,8 +110,8 @@ describe('elements/content-sidebar/AddTaskButton', () => { test('should preserve internalSidebarNavigation state when using navigation handler', async () => { const mockNavigationHandler = jest.fn(); const mockInternalSidebarNavigation = { - sidebar: 'activity', - activeFeedEntryType: 'comments', + sidebar: ViewType.ACTIVITY, + activeFeedEntryType: FeedEntryType.COMMENTS, activeFeedEntryId: '123', }; const user = userEvent(); @@ -127,8 +128,8 @@ describe('elements/content-sidebar/AddTaskButton', () => { expect(mockNavigationHandler).toHaveBeenCalledTimes(1); expect(mockNavigationHandler).toHaveBeenCalledWith( { - sidebar: 'activity', - activeFeedEntryType: 'comments', + sidebar: ViewType.ACTIVITY, + activeFeedEntryType: FeedEntryType.COMMENTS, activeFeedEntryId: '123', open: true, }, diff --git a/src/elements/content-sidebar/versions/__tests__/StaticVersionSidebar.test.js b/src/elements/content-sidebar/versions/__tests__/StaticVersionSidebar.test.js index 8921bdc18b..8a4ddc4fdd 100644 --- a/src/elements/content-sidebar/versions/__tests__/StaticVersionSidebar.test.js +++ b/src/elements/content-sidebar/versions/__tests__/StaticVersionSidebar.test.js @@ -118,8 +118,8 @@ describe('elements/content-sidebar/versions/StaticVersionSidebar', () => { render( - @@ -191,7 +191,7 @@ describe('elements/content-sidebar/versions/StaticVersionSidebar', () => { test('should use internalSidebarNavigationHandler when BackButton is clicked', async () => { const mockNavigationHandler = jest.fn(); const user = userEvent(); - + renderComponentWithoutRouter({ internalSidebarNavigationHandler: mockNavigationHandler, parentName: 'details', @@ -218,6 +218,4 @@ describe('elements/content-sidebar/versions/StaticVersionSidebar', () => { expect(screen.getByTestId('versions-menu')).toBeInTheDocument(); }); }); - - });