box-ui-elements 23.4.0-beta.17 → 23.4.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/explorer.css +1 -1
  2. package/dist/explorer.js +1 -1
  3. package/dist/preview.css +1 -1
  4. package/dist/preview.js +1 -1
  5. package/dist/sidebar.css +1 -1
  6. package/dist/sidebar.js +1 -1
  7. package/es/elements/common/back-button/BackButton.js.map +1 -0
  8. package/es/elements/common/back-button/index.js +3 -0
  9. package/es/elements/common/{nav-button → back-button}/index.js.flow +1 -1
  10. package/es/elements/common/back-button/index.js.map +1 -0
  11. package/{src/elements/common/types/SidebarNavigation.flow.js → es/elements/common/types/SidebarNavigation.js.flow} +9 -23
  12. package/es/elements/content-sidebar/SidebarNavButton.js +29 -22
  13. package/es/elements/content-sidebar/SidebarNavButton.js.flow +28 -21
  14. package/es/elements/content-sidebar/SidebarNavButton.js.map +1 -1
  15. package/es/elements/content-sidebar/SidebarNavButton.scss +13 -0
  16. package/es/elements/content-sidebar/versions/StaticVersionSidebar.js +1 -1
  17. package/es/elements/content-sidebar/versions/StaticVersionSidebar.js.flow +1 -1
  18. package/es/elements/content-sidebar/versions/StaticVersionSidebar.js.map +1 -1
  19. package/es/elements/content-sidebar/versions/VersionsGroup.js.flow +3 -0
  20. package/es/elements/content-sidebar/versions/VersionsGroup.js.map +1 -1
  21. package/es/elements/content-sidebar/versions/VersionsList.js +16 -8
  22. package/es/elements/content-sidebar/versions/VersionsList.js.flow +35 -17
  23. package/es/elements/content-sidebar/versions/VersionsList.js.map +1 -1
  24. package/es/elements/content-sidebar/versions/VersionsMenu.js.flow +3 -0
  25. package/es/elements/content-sidebar/versions/VersionsMenu.js.map +1 -1
  26. package/es/elements/content-sidebar/versions/VersionsSidebar.js +1 -1
  27. package/es/elements/content-sidebar/versions/VersionsSidebar.js.flow +1 -1
  28. package/es/elements/content-sidebar/versions/VersionsSidebar.js.map +1 -1
  29. package/package.json +3 -3
  30. package/src/elements/common/{nav-button → back-button}/__tests__/BackButton.test.js +1 -1
  31. package/src/elements/common/{nav-button → back-button}/index.js +1 -1
  32. package/{es/elements/common/types/SidebarNavigation.flow.js.flow → src/elements/common/types/SidebarNavigation.js.flow} +9 -23
  33. package/src/elements/content-sidebar/SidebarNavButton.js +28 -21
  34. package/src/elements/content-sidebar/SidebarNavButton.scss +13 -0
  35. package/src/elements/content-sidebar/__tests__/SidebarNavButton.test.js +208 -26
  36. package/src/elements/content-sidebar/versions/StaticVersionSidebar.js +1 -1
  37. package/src/elements/content-sidebar/versions/VersionsGroup.js +3 -0
  38. package/src/elements/content-sidebar/versions/VersionsList.js +35 -17
  39. package/src/elements/content-sidebar/versions/VersionsMenu.js +3 -0
  40. package/src/elements/content-sidebar/versions/VersionsSidebar.js +1 -1
  41. package/src/elements/content-sidebar/versions/__tests__/StaticVersionSidebar.test.js +5 -7
  42. package/src/elements/content-sidebar/versions/__tests__/VersionsList.test.js +138 -13
  43. package/src/elements/content-sidebar/versions/__tests__/VersionsMenu.test.js +75 -23
  44. package/src/elements/content-sidebar/versions/__tests__/VersionsSidebar.test.js +5 -7
  45. package/es/elements/common/nav-button/BackButton.js.map +0 -1
  46. package/es/elements/common/nav-button/NavButton.js +0 -63
  47. package/es/elements/common/nav-button/NavButton.js.flow +0 -80
  48. package/es/elements/common/nav-button/NavButton.js.map +0 -1
  49. package/es/elements/common/nav-button/index.js +0 -3
  50. package/es/elements/common/nav-button/index.js.map +0 -1
  51. package/es/elements/common/types/SidebarNavigation.flow.js +0 -14
  52. package/es/elements/common/types/SidebarNavigation.flow.js.map +0 -1
  53. package/src/elements/common/nav-button/NavButton.js +0 -80
  54. package/src/elements/common/nav-button/__tests__/NavButton.test.js +0 -265
  55. package/src/elements/content-sidebar/versions/__tests__/__snapshots__/VersionsList.test.js.snap +0 -45
  56. /package/es/elements/common/{nav-button → back-button}/BackButton.js +0 -0
  57. /package/es/elements/common/{nav-button → back-button}/BackButton.js.flow +0 -0
  58. /package/es/elements/common/{nav-button → back-button}/BackButton.scss +0 -0
  59. /package/src/elements/common/{nav-button → back-button}/BackButton.js +0 -0
  60. /package/src/elements/common/{nav-button → back-button}/BackButton.scss +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"VersionsSidebar.js","names":["React","FormattedMessage","Route","InlineError","messages","SidebarContent","VersionsMenu","BackButton","DEFAULT_FETCH_END","LoadingIndicatorWrapper","MAX_VERSIONS","VersionsSidebar","_ref","error","isLoading","parentName","versions","rest","_objectWithoutProperties","_excluded","showLimit","length","showVersions","showEmpty","showError","createElement","history","className","title","Fragment","onClick","push","versionsTitle","crawlerPosition","versionServerError","versionsEmpty","_extends","versionMaxEntries","values","maxVersions"],"sources":["../../../../src/elements/content-sidebar/versions/VersionsSidebar.js"],"sourcesContent":["/**\n * @flow\n * @file Versions Sidebar component\n * @author Box\n */\n\nimport * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport { Route } from 'react-router-dom';\nimport type { MessageDescriptor } from 'react-intl';\nimport InlineError from '../../../components/inline-error';\nimport messages from './messages';\nimport SidebarContent from '../SidebarContent';\nimport VersionsMenu from './VersionsMenu';\nimport { BackButton } from '../../common/nav-button';\nimport { DEFAULT_FETCH_END } from '../../../constants';\nimport { LoadingIndicatorWrapper } from '../../../components/loading-indicator';\nimport type { BoxItemVersion } from '../../../common/types/core';\nimport './VersionsSidebar.scss';\n\nconst MAX_VERSIONS = DEFAULT_FETCH_END;\n\ntype Props = {\n error?: MessageDescriptor,\n fileId: string,\n isLoading: boolean,\n parentName: string,\n versionCount: number,\n versionLimit: number,\n versions: Array<BoxItemVersion>,\n};\n\nconst VersionsSidebar = ({ error, isLoading, parentName, versions, ...rest }: Props) => {\n const showLimit = versions.length >= MAX_VERSIONS;\n const showVersions = !!versions.length;\n const showEmpty = !isLoading && !showVersions;\n const showError = !!error;\n\n return (\n <Route>\n {({ history }) => (\n <SidebarContent\n className=\"bcs-Versions\"\n data-resin-component=\"preview\"\n data-resin-feature=\"versions\"\n title={\n <>\n <BackButton data-resin-target=\"back\" onClick={() => history.push(`/${parentName}`)} />\n <FormattedMessage {...messages.versionsTitle} />\n </>\n }\n >\n <LoadingIndicatorWrapper\n className=\"bcs-Versions-content\"\n crawlerPosition=\"top\"\n isLoading={isLoading}\n >\n {showError && (\n <InlineError title={<FormattedMessage {...messages.versionServerError} />}>\n <FormattedMessage {...error} />\n </InlineError>\n )}\n\n {showEmpty && (\n <div className=\"bcs-Versions-empty\">\n <FormattedMessage {...messages.versionsEmpty} />\n </div>\n )}\n\n {showVersions && (\n <div className=\"bcs-Versions-menu\" data-testid=\"bcs-Versions-menu\">\n <VersionsMenu versions={versions} {...rest} />\n </div>\n )}\n {showLimit && (\n <div className=\"bcs-Versions-maxEntries\" data-testid=\"max-versions\">\n <FormattedMessage\n {...messages.versionMaxEntries}\n values={{\n maxVersions: MAX_VERSIONS,\n }}\n />\n </div>\n )}\n </LoadingIndicatorWrapper>\n </SidebarContent>\n )}\n </Route>\n );\n};\n\nexport default VersionsSidebar;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"VersionsSidebar.js","names":["React","FormattedMessage","Route","InlineError","messages","SidebarContent","VersionsMenu","BackButton","DEFAULT_FETCH_END","LoadingIndicatorWrapper","MAX_VERSIONS","VersionsSidebar","_ref","error","isLoading","parentName","versions","rest","_objectWithoutProperties","_excluded","showLimit","length","showVersions","showEmpty","showError","createElement","history","className","title","Fragment","onClick","push","versionsTitle","crawlerPosition","versionServerError","versionsEmpty","_extends","versionMaxEntries","values","maxVersions"],"sources":["../../../../src/elements/content-sidebar/versions/VersionsSidebar.js"],"sourcesContent":["/**\n * @flow\n * @file Versions Sidebar component\n * @author Box\n */\n\nimport * as React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport { Route } from 'react-router-dom';\nimport type { MessageDescriptor } from 'react-intl';\nimport InlineError from '../../../components/inline-error';\nimport messages from './messages';\nimport SidebarContent from '../SidebarContent';\nimport VersionsMenu from './VersionsMenu';\nimport BackButton from '../../common/back-button';\nimport { DEFAULT_FETCH_END } from '../../../constants';\nimport { LoadingIndicatorWrapper } from '../../../components/loading-indicator';\nimport type { BoxItemVersion } from '../../../common/types/core';\nimport './VersionsSidebar.scss';\n\nconst MAX_VERSIONS = DEFAULT_FETCH_END;\n\ntype Props = {\n error?: MessageDescriptor,\n fileId: string,\n isLoading: boolean,\n parentName: string,\n versionCount: number,\n versionLimit: number,\n versions: Array<BoxItemVersion>,\n};\n\nconst VersionsSidebar = ({ error, isLoading, parentName, versions, ...rest }: Props) => {\n const showLimit = versions.length >= MAX_VERSIONS;\n const showVersions = !!versions.length;\n const showEmpty = !isLoading && !showVersions;\n const showError = !!error;\n\n return (\n <Route>\n {({ history }) => (\n <SidebarContent\n className=\"bcs-Versions\"\n data-resin-component=\"preview\"\n data-resin-feature=\"versions\"\n title={\n <>\n <BackButton data-resin-target=\"back\" onClick={() => history.push(`/${parentName}`)} />\n <FormattedMessage {...messages.versionsTitle} />\n </>\n }\n >\n <LoadingIndicatorWrapper\n className=\"bcs-Versions-content\"\n crawlerPosition=\"top\"\n isLoading={isLoading}\n >\n {showError && (\n <InlineError title={<FormattedMessage {...messages.versionServerError} />}>\n <FormattedMessage {...error} />\n </InlineError>\n )}\n\n {showEmpty && (\n <div className=\"bcs-Versions-empty\">\n <FormattedMessage {...messages.versionsEmpty} />\n </div>\n )}\n\n {showVersions && (\n <div className=\"bcs-Versions-menu\" data-testid=\"bcs-Versions-menu\">\n <VersionsMenu versions={versions} {...rest} />\n </div>\n )}\n {showLimit && (\n <div className=\"bcs-Versions-maxEntries\" data-testid=\"max-versions\">\n <FormattedMessage\n {...messages.versionMaxEntries}\n values={{\n maxVersions: MAX_VERSIONS,\n }}\n />\n </div>\n )}\n </LoadingIndicatorWrapper>\n </SidebarContent>\n )}\n </Route>\n );\n};\n\nexport default VersionsSidebar;\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "box-ui-elements",
3
- "version": "23.4.0-beta.17",
3
+ "version": "23.4.0-beta.19",
4
4
  "description": "Box UI Elements",
5
5
  "author": "Box (https://www.box.com/)",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -137,7 +137,7 @@
137
137
  "@box/frontend": "^11.0.1",
138
138
  "@box/item-icon": "^0.9.83",
139
139
  "@box/languages": "^1.0.0",
140
- "@box/metadata-editor": "^0.112.0",
140
+ "@box/metadata-editor": "^0.115.0",
141
141
  "@box/react-virtualized": "9.22.3-rc-box.9",
142
142
  "@cfaester/enzyme-adapter-react-18": "^0.8.0",
143
143
  "@chromatic-com/storybook": "^1.6.1",
@@ -306,7 +306,7 @@
306
306
  "@box/cldr-data": ">=34.2.0",
307
307
  "@box/combobox-with-api": "^0.34.9",
308
308
  "@box/item-icon": "^0.9.83",
309
- "@box/metadata-editor": "^0.112.0",
309
+ "@box/metadata-editor": "^0.115.0",
310
310
  "@box/react-virtualized": "9.22.3-rc-box.9",
311
311
  "@hapi/address": "^2.1.4",
312
312
  "axios": "^0.30.0",
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
  import { render, screen, userEvent } from '../../../../test-utils/testing-library';
3
3
  import { BackButton } from '..';
4
4
 
5
- describe('elements/common/nav-button/BackButton', () => {
5
+ describe('elements/common/back-button/BackButton', () => {
6
6
  const mockOnClick = jest.fn();
7
7
 
8
8
  beforeEach(() => {
@@ -1,2 +1,2 @@
1
1
  export { default as BackButton } from './BackButton';
2
- export { default } from './NavButton';
2
+ export { default } from './BackButton';
@@ -1,5 +1,8 @@
1
1
  /* @flow */
2
2
 
3
+ // flow version is simplified compared to Type Script due to difficult to resolve problems with Union Types
4
+ // Type Script works better with Union Types
5
+
3
6
  export const ViewType = Object.freeze({
4
7
  BOXAI: 'boxai',
5
8
  SKILLS: 'skills',
@@ -18,30 +21,13 @@ export const FeedEntryType = Object.freeze({
18
21
  export type ViewTypeValues = $Values<typeof ViewType>;
19
22
  export type FeedEntryTypeValues = $Values<typeof FeedEntryType>;
20
23
 
21
- type VersionSidebarView = {
22
- sidebar: 'activity' | 'details',
23
- versionId: string,
24
- };
25
-
26
- export type ActivityAnnotationsSidebarView = {
27
- sidebar: 'activity',
28
- activeFeedEntryType: 'annotations',
29
- fileVersionId: string,
30
- activeFeedEntryId: string,
24
+ export type SidebarNavigation = {
25
+ sidebar: ViewTypeValues,
26
+ versionId?: string,
27
+ activeFeedEntryType?: FeedEntryTypeValues,
28
+ activeFeedEntryId?: string,
29
+ fileVersionId?: string,
31
30
  };
32
- type ActivityCommentsSidebarView = {
33
- sidebar: 'activity',
34
- activeFeedEntryType: 'comments' | 'tasks',
35
- activeFeedEntryId: string,
36
- };
37
-
38
- export type SidebarNavigation =
39
- | {|
40
- sidebar: ViewTypeValues,
41
- |}
42
- | VersionSidebarView
43
- | ActivityCommentsSidebarView
44
- | ActivityAnnotationsSidebarView;
45
31
 
46
32
  export type InternalSidebarNavigation = SidebarNavigation & {
47
33
  open: boolean,
@@ -7,8 +7,10 @@
7
7
  import * as React from 'react';
8
8
  import { Route } from 'react-router-dom';
9
9
  import noop from 'lodash/noop';
10
- import NavButton from '../common/nav-button';
10
+ import classNames from 'classnames';
11
+ import { Button } from '@box/blueprint-web';
11
12
  import Tooltip from '../../components/tooltip/Tooltip';
13
+ import { isLeftClick } from '../../utils/dom';
12
14
  import './SidebarNavButton.scss';
13
15
 
14
16
  type Props = {
@@ -37,45 +39,50 @@ const SidebarNavButton = React.forwardRef<Props, React.Ref<any>>((props: Props,
37
39
  } = props;
38
40
  const sidebarPath = `/${sidebarView}`;
39
41
 
40
- const handleNavButtonClick = () => {
41
- onClick(sidebarView);
42
- };
43
-
44
42
  return (
45
43
  <Route path={sidebarPath}>
46
- {({ match }) => {
44
+ {({ match, history }) => {
47
45
  const isMatch = !!match;
48
- const isActive = () => isMatch && !!isOpen;
49
- const isActiveValue = isActive();
46
+ const isActiveValue = isMatch && !!isOpen;
50
47
  const isExactMatch = isMatch && match.isExact;
51
48
  const id = `${elementId}${elementId === '' ? '' : '_'}${sidebarView}`;
52
49
 
50
+ const handleNavButtonClick = event => {
51
+ onClick(sidebarView);
52
+
53
+ if (!event.defaultPrevented && isLeftClick(event)) {
54
+ const method = isExactMatch ? history.replace : history.push;
55
+ method({
56
+ pathname: sidebarPath,
57
+ state: { open: true },
58
+ });
59
+ }
60
+ };
61
+
53
62
  return (
54
63
  <Tooltip position="middle-left" text={tooltip} isTabbable={false}>
55
- <NavButton
56
- activeClassName="bcs-is-selected"
57
- aria-selected={isActiveValue}
64
+ <Button
65
+ accessibleWhenDisabled={true}
58
66
  aria-controls={`${id}-content`}
59
67
  aria-label={tooltip}
60
- className="bcs-NavButton"
68
+ aria-selected={isActiveValue}
69
+ className={classNames('bcs-NavButton', {
70
+ 'bcs-is-selected': isActiveValue,
71
+ 'bdl-is-disabled': isDisabled,
72
+ })}
61
73
  data-resin-target={dataResinTarget}
62
74
  data-testid={dataTestId}
63
- getDOMRef={ref}
75
+ ref={ref}
64
76
  id={id}
65
- isActive={isActive}
66
- isDisabled={isDisabled}
77
+ disabled={isDisabled}
67
78
  onClick={handleNavButtonClick}
68
- replace={isExactMatch}
69
79
  role="tab"
70
80
  tabIndex={isActiveValue ? '0' : '-1'}
71
- to={{
72
- pathname: sidebarPath,
73
- state: { open: true },
74
- }}
75
81
  type="button"
82
+ variant="tertiary"
76
83
  >
77
84
  {children}
78
- </NavButton>
85
+ </Button>
79
86
  </Tooltip>
80
87
  );
81
88
  }}
@@ -3,6 +3,8 @@
3
3
 
4
4
  .bcs .bcs-NavButton {
5
5
  @include bdl-SidebarNavButton;
6
+
7
+ border-radius: 0;
6
8
 
7
9
  &::before {
8
10
  position: absolute;
@@ -21,6 +23,11 @@
21
23
  }
22
24
  }
23
25
 
26
+ // Style disabled buttons with proper visual feedback
27
+ &[aria-disabled="true"] {
28
+ cursor: default;
29
+ }
30
+
24
31
  &.bcs-is-selected {
25
32
  &::before {
26
33
  background-color: $blue;
@@ -30,6 +37,12 @@
30
37
  fill: $blue;
31
38
  }
32
39
  }
40
+
41
+ // Blueprint button focus override
42
+ &:not(:disabled)[data-focus-visible] {
43
+ border-radius: var(--radius-1);
44
+ box-shadow: 0 0 0 var(--border-2) var(--outline-focus-on-light);
45
+ }
33
46
  }
34
47
 
35
48
  @include breakpoint($medium-screen) {
@@ -1,26 +1,40 @@
1
1
  import * as React from 'react';
2
- import { MemoryRouter } from 'react-router-dom';
3
- import { mount } from 'enzyme';
4
- import { render, screen, fireEvent } from '@testing-library/react';
5
- import PlainButton from '../../../components/plain-button';
2
+ import { MemoryRouter, Router } from 'react-router-dom';
3
+ import { render, screen, fireEvent } from '../../../test-utils/testing-library';
6
4
  import SidebarNavButton from '../SidebarNavButton';
7
- import Tooltip from '../../../components/tooltip/Tooltip';
8
5
 
9
6
  describe('elements/content-sidebar/SidebarNavButton', () => {
10
- const getWrapper = ({ children, ...props }, path = '/') =>
11
- mount(
7
+ beforeEach(() => {
8
+ jest.clearAllMocks();
9
+ });
10
+
11
+ const renderWrapper = ({ children, ref, ...props }, path = '/') =>
12
+ render(
12
13
  <MemoryRouter initialEntries={[path]}>
13
- <SidebarNavButton {...props}>{children}</SidebarNavButton>
14
+ <SidebarNavButton ref={ref} {...props}>
15
+ {children}
16
+ </SidebarNavButton>
14
17
  </MemoryRouter>,
15
18
  );
16
- const getButton = wrapper => wrapper.find(PlainButton).first();
17
19
 
18
20
  test('should render nav button properly', () => {
19
- const wrapper = getWrapper({ tooltip: 'foo' });
20
- const button = getButton(wrapper);
21
+ renderWrapper({
22
+ tooltip: 'foo',
23
+ sidebarView: 'activity',
24
+ children: 'test button',
25
+ });
26
+ const button = screen.getByRole('tab');
21
27
 
22
- expect(wrapper.find(Tooltip).prop('text')).toBe('foo');
23
- expect(button.hasClass('bcs-is-selected')).toBe(false);
28
+ expect(button).toHaveAttribute('aria-label', 'foo');
29
+ expect(button).toHaveAttribute('aria-selected', 'false');
30
+ expect(button).toHaveAttribute('aria-controls', 'activity-content');
31
+ expect(button).toHaveAttribute('role', 'tab');
32
+ expect(button).toHaveAttribute('tabindex', '-1');
33
+ expect(button).toHaveAttribute('type', 'button');
34
+ expect(button).toHaveAttribute('id', 'activity');
35
+ expect(button).toHaveClass('bcs-NavButton');
36
+ expect(button).not.toHaveClass('bcs-is-selected');
37
+ expect(button).toHaveTextContent('test button');
24
38
  });
25
39
 
26
40
  test.each`
@@ -33,11 +47,20 @@ describe('elements/content-sidebar/SidebarNavButton', () => {
33
47
  isOpen,
34
48
  sidebarView: 'activity',
35
49
  tooltip: 'foo',
50
+ children: 'test button',
36
51
  };
37
- const wrapper = getWrapper(props, '/activity');
38
- const button = getButton(wrapper);
52
+ renderWrapper(props, '/activity');
53
+ const button = screen.getByRole('tab');
39
54
 
40
- expect(button.hasClass('bcs-is-selected')).toBe(expected);
55
+ if (expected) {
56
+ expect(button).toHaveClass('bcs-is-selected');
57
+ expect(button).toHaveAttribute('aria-selected', 'true');
58
+ expect(button).toHaveAttribute('tabindex', '0');
59
+ } else {
60
+ expect(button).not.toHaveClass('bcs-is-selected');
61
+ expect(button).toHaveAttribute('aria-selected', 'false');
62
+ expect(button).toHaveAttribute('tabindex', '-1');
63
+ }
41
64
  });
42
65
 
43
66
  test.each`
@@ -48,26 +71,185 @@ describe('elements/content-sidebar/SidebarNavButton', () => {
48
71
  ${'/activity/test'} | ${true}
49
72
  ${'/skills'} | ${false}
50
73
  `('should reflect active state ($expected) correctly based on active path', ({ expected, path }) => {
51
- const wrapper = getWrapper({ isOpen: true, sidebarView: 'activity' }, path);
52
- const button = getButton(wrapper);
74
+ renderWrapper(
75
+ {
76
+ isOpen: true,
77
+ sidebarView: 'activity',
78
+ tooltip: 'foo',
79
+ children: 'test button',
80
+ },
81
+ path,
82
+ );
83
+ const button = screen.getByRole('tab');
53
84
 
54
- expect(button.hasClass('bcs-is-selected')).toBe(expected);
85
+ if (expected) {
86
+ expect(button).toHaveClass('bcs-is-selected');
87
+ expect(button).toHaveAttribute('aria-selected', 'true');
88
+ expect(button).toHaveAttribute('tabindex', '0');
89
+ } else {
90
+ expect(button).not.toHaveClass('bcs-is-selected');
91
+ expect(button).toHaveAttribute('aria-selected', 'false');
92
+ expect(button).toHaveAttribute('tabindex', '-1');
93
+ }
55
94
  });
56
95
 
57
96
  test('should call onClick with sidebarView when clicked', () => {
58
97
  const mockOnClick = jest.fn();
59
98
  const mockSidebarView = 'activity';
60
99
 
61
- render(
62
- <MemoryRouter initialEntries={['/']}>
63
- <SidebarNavButton onClick={mockOnClick} sidebarView={mockSidebarView}>
64
- button
65
- </SidebarNavButton>
66
- </MemoryRouter>,
67
- );
100
+ renderWrapper({
101
+ onClick: mockOnClick,
102
+ sidebarView: mockSidebarView,
103
+ tooltip: 'test',
104
+ children: 'button',
105
+ });
68
106
  const button = screen.getByText('button');
69
107
 
70
108
  fireEvent.click(button);
71
109
  expect(mockOnClick).toBeCalledWith(mockSidebarView);
72
110
  });
111
+
112
+ test.each`
113
+ isDisabled | expected
114
+ ${true} | ${true}
115
+ ${false} | ${false}
116
+ ${undefined} | ${false}
117
+ `('should apply bdl-is-disabled class when isDisabled is $isDisabled', ({ isDisabled, expected }) => {
118
+ const content = 'Activity';
119
+ renderWrapper({
120
+ isDisabled,
121
+ sidebarView: 'activity',
122
+ tooltip: 'Activity',
123
+ children: content,
124
+ });
125
+
126
+ const button = screen.getByRole('tab');
127
+ if (expected) {
128
+ expect(button).toHaveClass('bdl-is-disabled');
129
+ } else {
130
+ expect(button).not.toHaveClass('bdl-is-disabled');
131
+ }
132
+ });
133
+
134
+ test.each`
135
+ elementId | sidebarView | expectedId | expectedAriaControls
136
+ ${undefined} | ${'activity'} | ${'activity'} | ${'activity-content'}
137
+ ${''} | ${'activity'} | ${'activity'} | ${'activity-content'}
138
+ ${'sidebar'} | ${'activity'} | ${'sidebar_activity'} | ${'sidebar_activity-content'}
139
+ ${'main'} | ${'skills'} | ${'main_skills'} | ${'main_skills-content'}
140
+ `(
141
+ 'should generate correct id and aria-controls with elementId=$elementId and sidebarView=$sidebarView',
142
+ ({ elementId, sidebarView, expectedId, expectedAriaControls }) => {
143
+ renderWrapper({
144
+ elementId,
145
+ sidebarView,
146
+ tooltip: 'test',
147
+ children: 'test button',
148
+ });
149
+
150
+ const button = screen.getByRole('tab');
151
+ expect(button).toHaveAttribute('id', expectedId);
152
+ expect(button).toHaveAttribute('aria-controls', expectedAriaControls);
153
+ },
154
+ );
155
+
156
+ test('should forward ref to the Button', () => {
157
+ const ref = React.createRef();
158
+
159
+ renderWrapper({
160
+ ref,
161
+ sidebarView: 'activity',
162
+ tooltip: 'test',
163
+ children: 'test button',
164
+ });
165
+
166
+ const button = screen.getByRole('tab');
167
+ expect(ref.current).toBe(button);
168
+ });
169
+
170
+ describe('navigation on click', () => {
171
+ const mockHistoryPush = jest.fn();
172
+ const mockHistoryReplace = jest.fn();
173
+ const mockHistory = {
174
+ push: mockHistoryPush,
175
+ replace: mockHistoryReplace,
176
+ listen: jest.fn(),
177
+ location: { pathname: '/activity' },
178
+ };
179
+
180
+ const renderWithRouter = (props, history = mockHistory) => {
181
+ return render(
182
+ <Router history={history}>
183
+ <SidebarNavButton sidebarView="activity" tooltip="test" {...props}>
184
+ Activity
185
+ </SidebarNavButton>
186
+ </Router>,
187
+ );
188
+ };
189
+
190
+ test('calls onClick handler and history.push on left click when not exact match', () => {
191
+ const mockOnClick = jest.fn();
192
+ const mockHistoryWithDifferentPath = {
193
+ ...mockHistory,
194
+ location: { pathname: '/activity/versions' },
195
+ };
196
+
197
+ renderWithRouter({ onClick: mockOnClick }, mockHistoryWithDifferentPath);
198
+
199
+ const button = screen.getByText('Activity');
200
+ fireEvent.click(button, { button: 0 });
201
+
202
+ expect(mockOnClick).toBeCalledWith('activity');
203
+ expect(mockHistoryPush).toBeCalledWith({
204
+ pathname: '/activity',
205
+ state: { open: true },
206
+ });
207
+ expect(mockHistoryReplace).not.toBeCalled();
208
+ });
209
+
210
+ test('calls history.replace on left click when exact match', () => {
211
+ const mockOnClick = jest.fn();
212
+
213
+ renderWithRouter({ onClick: mockOnClick });
214
+
215
+ const button = screen.getByText('Activity');
216
+ fireEvent.click(button, { button: 0 });
217
+
218
+ expect(mockOnClick).toBeCalledWith('activity');
219
+ expect(mockHistoryReplace).toBeCalledWith({
220
+ pathname: '/activity',
221
+ state: { open: true },
222
+ });
223
+ expect(mockHistoryPush).not.toBeCalled();
224
+ });
225
+
226
+ test('does not call history.push on right click', () => {
227
+ const mockOnClick = jest.fn();
228
+
229
+ renderWithRouter({ onClick: mockOnClick });
230
+
231
+ const button = screen.getByText('Activity');
232
+ fireEvent.click(button, { button: 1 });
233
+
234
+ expect(mockOnClick).toBeCalledWith('activity');
235
+ expect(mockHistoryPush).not.toBeCalled();
236
+ expect(mockHistoryReplace).not.toBeCalled();
237
+ });
238
+
239
+ test('does not call history.push on prevented event', () => {
240
+ const mockOnClick = jest.fn();
241
+
242
+ renderWithRouter({ onClick: mockOnClick });
243
+
244
+ const button = screen.getByText('Activity');
245
+
246
+ // Prevent default on the button click
247
+ button.addEventListener('click', e => e.preventDefault());
248
+ fireEvent.click(button, { button: 0 });
249
+
250
+ expect(mockOnClick).toBeCalledWith('activity');
251
+ expect(mockHistoryPush).not.toBeCalled();
252
+ expect(mockHistoryReplace).not.toBeCalled();
253
+ });
254
+ });
73
255
  });
@@ -10,7 +10,7 @@ import { Route } from 'react-router-dom';
10
10
 
11
11
  import BoxDrive140 from '../../../illustration/BoxDrive140';
12
12
 
13
- import { BackButton } from '../../common/nav-button';
13
+ import BackButton from '../../common/back-button';
14
14
  import PrimaryButton from '../../../components/primary-button';
15
15
  import { LoadingIndicatorWrapper } from '../../../components/loading-indicator';
16
16
  import VersionsMenu from './VersionsMenu';
@@ -7,11 +7,14 @@
7
7
  import * as React from 'react';
8
8
  import VersionsList from './VersionsList';
9
9
  import type { BoxItemVersion } from '../../../common/types/core';
10
+ import type { InternalSidebarNavigation } from '../../common/types/SidebarNavigation';
10
11
  import './VersionsGroup.scss';
11
12
 
12
13
  type Props = {
13
14
  fileId: string,
14
15
  heading: string,
16
+ internalSidebarNavigation?: InternalSidebarNavigation,
17
+ routerDisabled?: boolean,
15
18
  versionCount: number,
16
19
  versionLimit: number,
17
20
  versions: Array<BoxItemVersion>,
@@ -8,33 +8,51 @@ import * as React from 'react';
8
8
  import { Route } from 'react-router-dom';
9
9
  import VersionsItem from './VersionsItem';
10
10
  import type { BoxItemVersion } from '../../../common/types/core';
11
+ import type { InternalSidebarNavigation } from '../../common/types/SidebarNavigation';
11
12
  import './VersionsList.scss';
12
13
 
13
14
  type Props = {
14
15
  currentId?: string,
15
16
  fileId: string,
17
+ internalSidebarNavigation?: InternalSidebarNavigation,
18
+ routerDisabled?: boolean,
16
19
  versionCount: number,
17
20
  versionLimit: number,
18
21
  versions: Array<BoxItemVersion>,
19
22
  };
20
23
 
21
- const VersionsList = ({ currentId, versions, ...rest }: Props) => (
22
- <ul className="bcs-VersionsList">
23
- {versions.map(version => (
24
- <li className="bcs-VersionsList-item" key={version.id}>
25
- <Route
26
- render={({ match }) => (
27
- <VersionsItem
28
- isCurrent={currentId === version.id}
29
- isSelected={match.params.versionId === version.id}
30
- version={version}
31
- {...rest}
32
- />
33
- )}
24
+ const VersionsList = ({ currentId, internalSidebarNavigation, routerDisabled = false, versions, ...rest }: Props) => {
25
+ const renderVersionItemWithoutRouter = (version: BoxItemVersion) => (
26
+ <VersionsItem
27
+ isCurrent={currentId === version.id}
28
+ isSelected={internalSidebarNavigation?.versionId === version.id}
29
+ version={version}
30
+ {...rest}
31
+ />
32
+ );
33
+
34
+ const renderVersionItemWithRouter = (version: BoxItemVersion) => (
35
+ <Route
36
+ render={({ match }) => (
37
+ <VersionsItem
38
+ isCurrent={currentId === version.id}
39
+ isSelected={match.params.versionId === version.id}
40
+ version={version}
41
+ {...rest}
34
42
  />
35
- </li>
36
- ))}
37
- </ul>
38
- );
43
+ )}
44
+ />
45
+ );
46
+
47
+ return (
48
+ <ul className="bcs-VersionsList">
49
+ {versions.map(version => (
50
+ <li className="bcs-VersionsList-item" key={version.id}>
51
+ {routerDisabled ? renderVersionItemWithoutRouter(version) : renderVersionItemWithRouter(version)}
52
+ </li>
53
+ ))}
54
+ </ul>
55
+ );
56
+ };
39
57
 
40
58
  export default VersionsList;
@@ -11,11 +11,14 @@ import * as util from '../../../utils/datetime';
11
11
  import messages from './messages';
12
12
  import VersionsGroup from './VersionsGroup';
13
13
  import type { BoxItemVersion } from '../../../common/types/core';
14
+ import type { InternalSidebarNavigation } from '../../common/types/SidebarNavigation';
14
15
  import './VersionsMenu.scss';
15
16
 
16
17
  type Props = {
17
18
  fileId: string,
18
19
  intl: any,
20
+ internalSidebarNavigation?: InternalSidebarNavigation,
21
+ routerDisabled?: boolean,
19
22
  versionCount: number,
20
23
  versionLimit: number,
21
24
  versions: Array<BoxItemVersion>,
@@ -12,7 +12,7 @@ import InlineError from '../../../components/inline-error';
12
12
  import messages from './messages';
13
13
  import SidebarContent from '../SidebarContent';
14
14
  import VersionsMenu from './VersionsMenu';
15
- import { BackButton } from '../../common/nav-button';
15
+ import BackButton from '../../common/back-button';
16
16
  import { DEFAULT_FETCH_END } from '../../../constants';
17
17
  import { LoadingIndicatorWrapper } from '../../../components/loading-indicator';
18
18
  import type { BoxItemVersion } from '../../../common/types/core';
@@ -3,13 +3,11 @@ import { MemoryRouter, Route } from 'react-router-dom';
3
3
  import { render, screen, userEvent } from '../../../../test-utils/testing-library';
4
4
  import StaticVersionSidebar from '../StaticVersionSidebar';
5
5
 
6
- jest.mock('../../../common/nav-button', () => ({
7
- BackButton: ({ onClick, 'data-resin-target': dataResinTarget }) => (
8
- <button type="button" onClick={onClick} data-resin-target={dataResinTarget} data-testid="back-button">
9
- Back
10
- </button>
11
- ),
12
- }));
6
+ jest.mock('../../../common/back-button', () => ({ onClick, 'data-resin-target': dataResinTarget }) => (
7
+ <button type="button" onClick={onClick} data-resin-target={dataResinTarget} data-testid="back-button">
8
+ Back
9
+ </button>
10
+ ));
13
11
 
14
12
  jest.mock('../VersionsMenu', () => ({ versions, fileId, versionCount, versionLimit }) => (
15
13
  <div data-testid="versions-menu">