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

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 (61) hide show
  1. package/dist/explorer.js +1 -1
  2. package/dist/picker.js +1 -1
  3. package/dist/preview.js +1 -1
  4. package/dist/preview.js.LICENSE.txt +10 -0
  5. package/dist/sharing.js +1 -1
  6. package/dist/sidebar.js +1 -1
  7. package/dist/sidebar.js.LICENSE.txt +10 -0
  8. package/dist/uploader.js +1 -1
  9. package/es/elements/common/annotator-context/types.js.flow +2 -2
  10. package/es/elements/common/annotator-context/types.js.map +1 -1
  11. package/es/elements/common/annotator-context/withAnnotations.js +29 -10
  12. package/es/elements/common/annotator-context/withAnnotations.js.flow +10 -3
  13. package/es/elements/common/annotator-context/withAnnotations.js.map +1 -1
  14. package/es/elements/common/annotator-context/withAnnotatorContext.js +47 -23
  15. package/es/elements/common/annotator-context/withAnnotatorContext.js.flow +1 -0
  16. package/es/elements/common/annotator-context/withAnnotatorContext.js.map +1 -1
  17. package/es/elements/common/nav-button/BackButton.js +5 -8
  18. package/es/elements/common/nav-button/BackButton.js.flow +8 -18
  19. package/es/elements/common/nav-button/BackButton.js.map +1 -1
  20. package/es/elements/common/types/SidebarNavigation.flow.js +14 -0
  21. package/es/elements/common/types/SidebarNavigation.flow.js.flow +52 -0
  22. package/es/elements/common/types/SidebarNavigation.flow.js.map +1 -0
  23. package/es/elements/common/types/SidebarNavigation.js +16 -0
  24. package/es/elements/common/types/SidebarNavigation.js.map +1 -0
  25. package/es/elements/content-sidebar/stories/tests/MetadataSidebarRedesign-visual.stories.js +4 -13
  26. package/es/elements/content-sidebar/stories/tests/MetadataSidebarRedesign-visual.stories.js.map +1 -1
  27. package/es/elements/content-sidebar/versions/StaticVersionSidebar.js +6 -3
  28. package/es/elements/content-sidebar/versions/StaticVersionSidebar.js.flow +47 -42
  29. package/es/elements/content-sidebar/versions/StaticVersionSidebar.js.map +1 -1
  30. package/es/elements/content-sidebar/versions/VersionsSidebar.js +6 -3
  31. package/es/elements/content-sidebar/versions/VersionsSidebar.js.flow +48 -39
  32. package/es/elements/content-sidebar/versions/VersionsSidebar.js.map +1 -1
  33. package/es/src/elements/common/annotator-context/types.d.ts +2 -2
  34. package/es/src/elements/common/annotator-context/withAnnotations.d.ts +5 -0
  35. package/es/src/elements/common/annotator-context/withAnnotatorContext.d.ts +4 -1
  36. package/es/src/elements/common/types/SidebarNavigation.d.ts +37 -0
  37. package/es/src/test-utils/testing-library.d.ts +2 -1
  38. package/es/test-utils/testing-library.js +4 -1
  39. package/es/test-utils/testing-library.js.map +1 -1
  40. package/package.json +35 -37
  41. package/src/elements/common/annotator-context/__tests__/withAnnotations.test.tsx +46 -0
  42. package/src/elements/common/annotator-context/__tests__/withAnnotatorContext.test.tsx +116 -20
  43. package/src/elements/common/annotator-context/types.js.flow +2 -2
  44. package/src/elements/common/annotator-context/types.ts +2 -2
  45. package/src/elements/common/annotator-context/withAnnotations.js.flow +10 -3
  46. package/src/elements/common/annotator-context/withAnnotations.tsx +35 -6
  47. package/src/elements/common/annotator-context/withAnnotatorContext.js.flow +1 -0
  48. package/src/elements/common/annotator-context/withAnnotatorContext.tsx +61 -29
  49. package/src/elements/common/nav-button/BackButton.js +8 -18
  50. package/src/elements/common/nav-button/__tests__/BackButton.test.js +36 -27
  51. package/src/elements/common/types/SidebarNavigation.flow.js +52 -0
  52. package/src/elements/common/types/SidebarNavigation.ts +47 -0
  53. package/src/elements/content-sidebar/stories/tests/MetadataSidebarRedesign-visual.stories.tsx +5 -13
  54. package/src/elements/content-sidebar/versions/StaticVersionSidebar.js +47 -42
  55. package/src/elements/content-sidebar/versions/VersionsSidebar.js +48 -39
  56. package/src/elements/content-sidebar/versions/__tests__/StaticVersionSidebar.test.js +171 -0
  57. package/src/elements/content-sidebar/versions/__tests__/VersionsSidebar.test.js +147 -20
  58. package/src/test-utils/testing-library.tsx +4 -1
  59. package/i18n.config.js +0 -9
  60. package/src/elements/common/nav-button/__tests__/__snapshots__/BackButton.test.js.snap +0 -64
  61. package/src/elements/content-sidebar/versions/__tests__/__snapshots__/VersionsSidebar.test.js.snap +0 -92
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { shallow } from 'enzyme';
2
+ import { render } from '../../../../test-utils/testing-library';
3
3
  import withAnnotatorContext, { WithAnnotatorContextProps } from '../withAnnotatorContext';
4
4
  import { Action } from '../types';
5
5
 
@@ -13,18 +13,59 @@ describe('elements/common/annotator-context/withAnnotatorContext', () => {
13
13
  className?: string | undefined;
14
14
  };
15
15
 
16
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
17
- type WrappedComponentProps<T> = ComponentProps & WithAnnotatorContextProps; // T is supposed to be allowed component props
16
+ type WrappedComponentProps = ComponentProps & WithAnnotatorContextProps;
18
17
 
19
- const Component = (props: WrappedComponentProps<HTMLDivElement>) => <div {...props} />;
18
+ beforeEach(() => jest.resetAllMocks());
20
19
 
21
- const WrappedComponent = withAnnotatorContext(Component);
20
+ test('should apply the annotator context to the wrapped component as a prop', () => {
21
+ const annotatorState = {
22
+ annotation: { foo: 'bar' },
23
+ action: Action.CREATE_START,
24
+ };
25
+ const mockEmitActiveAnnotationChangeEvent = jest.fn();
26
+ const mockEmitAnnotationRemoveEvent = jest.fn();
27
+ const mockEmitAnnotationReplyCreateEvent = jest.fn();
28
+ const mockEmitAnnotationReplyDeleteEvent = jest.fn();
29
+ const mockMmitAnnotationReplyUpdateEvent = jest.fn();
30
+ const mockEmitAnnotationUpdateEvent = jest.fn();
31
+ const mockGetAnnotationsMatchPath = jest.fn();
32
+ const mockGetAnnotationsPath = jest.fn();
22
33
 
23
- const getWrapper = (props?: WrappedComponentProps<HTMLDivElement>) => shallow(<WrappedComponent {...props} />);
34
+ mockContext.mockReturnValue({
35
+ state: annotatorState,
36
+ emitActiveAnnotationChangeEvent: mockEmitActiveAnnotationChangeEvent,
37
+ emitAnnotationRemoveEvent: mockEmitAnnotationRemoveEvent,
38
+ emitAnnotationReplyCreateEvent: mockEmitAnnotationReplyCreateEvent,
39
+ emitAnnotationReplyDeleteEvent: mockEmitAnnotationReplyDeleteEvent,
40
+ emitAnnotationReplyUpdateEvent: mockMmitAnnotationReplyUpdateEvent,
41
+ emitAnnotationUpdateEvent: mockEmitAnnotationUpdateEvent,
42
+ getAnnotationsMatchPath: mockGetAnnotationsMatchPath,
43
+ getAnnotationsPath: mockGetAnnotationsPath,
44
+ });
24
45
 
25
- beforeEach(() => jest.resetAllMocks());
46
+ const MockComponent = jest.fn<JSX.Element | null, [WrappedComponentProps]>(() => null);
47
+ const WrappedWithMockComponent = withAnnotatorContext(MockComponent);
26
48
 
27
- test('should apply the annotator context to the wrapped component as a prop', () => {
49
+ render(<WrappedWithMockComponent />);
50
+
51
+ expect(MockComponent).toHaveBeenCalledTimes(1);
52
+ const props = MockComponent.mock.calls[0][0];
53
+
54
+ expect(props.annotatorState).toEqual({
55
+ annotation: { foo: 'bar' },
56
+ action: Action.CREATE_START,
57
+ });
58
+ expect(props.emitActiveAnnotationChangeEvent).toBe(mockEmitActiveAnnotationChangeEvent);
59
+ expect(props.emitAnnotationRemoveEvent).toBe(mockEmitAnnotationRemoveEvent);
60
+ expect(props.emitAnnotationReplyCreateEvent).toBe(mockEmitAnnotationReplyCreateEvent);
61
+ expect(props.emitAnnotationReplyDeleteEvent).toBe(mockEmitAnnotationReplyDeleteEvent);
62
+ expect(props.emitAnnotationReplyUpdateEvent).toBe(mockMmitAnnotationReplyUpdateEvent);
63
+ expect(props.emitAnnotationUpdateEvent).toBe(mockEmitAnnotationUpdateEvent);
64
+ expect(props.getAnnotationsMatchPath).toBe(mockGetAnnotationsMatchPath);
65
+ expect(props.getAnnotationsPath).toBe(mockGetAnnotationsPath);
66
+ });
67
+
68
+ test('should apply the annotator context to the wrapped component without router props when routerDisabled is true', () => {
28
69
  const annotatorState = {
29
70
  annotation: { foo: 'bar' },
30
71
  action: Action.CREATE_START,
@@ -50,22 +91,77 @@ describe('elements/common/annotator-context/withAnnotatorContext', () => {
50
91
  getAnnotationsPath: mockGetAnnotationsPath,
51
92
  });
52
93
 
53
- const wrapper = getWrapper();
54
- const wrappedComponent = wrapper.dive().find(Component);
55
- const props = wrappedComponent.props() as WrappedComponentProps<HTMLDivElement>;
94
+ const MockComponent = jest.fn<JSX.Element | null, [WrappedComponentProps]>(() => null);
95
+ const WrappedWithMockComponent = withAnnotatorContext(MockComponent);
96
+
97
+ render(<WrappedWithMockComponent routerDisabled={true} />);
98
+
99
+ expect(MockComponent).toHaveBeenCalledTimes(1);
100
+ const props = MockComponent.mock.calls[0][0];
56
101
 
57
- expect(wrappedComponent.exists()).toBeTruthy();
58
102
  expect(props.annotatorState).toEqual({
59
103
  annotation: { foo: 'bar' },
60
104
  action: Action.CREATE_START,
61
105
  });
62
- expect(props.emitActiveAnnotationChangeEvent).toEqual(mockEmitActiveAnnotationChangeEvent);
63
- expect(props.emitAnnotationRemoveEvent).toEqual(mockEmitAnnotationRemoveEvent);
64
- expect(props.emitAnnotationReplyCreateEvent).toEqual(mockEmitAnnotationReplyCreateEvent);
65
- expect(props.emitAnnotationReplyDeleteEvent).toEqual(mockEmitAnnotationReplyDeleteEvent);
66
- expect(props.emitAnnotationReplyUpdateEvent).toEqual(mockMmitAnnotationReplyUpdateEvent);
67
- expect(props.emitAnnotationUpdateEvent).toEqual(mockEmitAnnotationUpdateEvent);
68
- expect(props.getAnnotationsMatchPath).toEqual(mockGetAnnotationsMatchPath);
69
- expect(props.getAnnotationsPath).toEqual(mockGetAnnotationsPath);
106
+ expect(props.emitActiveAnnotationChangeEvent).toBe(mockEmitActiveAnnotationChangeEvent);
107
+ expect(props.emitAnnotationRemoveEvent).toBe(mockEmitAnnotationRemoveEvent);
108
+ expect(props.emitAnnotationReplyCreateEvent).toBe(mockEmitAnnotationReplyCreateEvent);
109
+ expect(props.emitAnnotationReplyDeleteEvent).toBe(mockEmitAnnotationReplyDeleteEvent);
110
+ expect(props.emitAnnotationReplyUpdateEvent).toBe(mockMmitAnnotationReplyUpdateEvent);
111
+ expect(props.emitAnnotationUpdateEvent).toBe(mockEmitAnnotationUpdateEvent);
112
+
113
+ // Router-related props should not be passed when routerDisabled is true
114
+ expect(props.getAnnotationsMatchPath).toBeUndefined();
115
+ expect(props.getAnnotationsPath).toBeUndefined();
116
+ });
117
+
118
+ test('should apply the annotator context to the wrapped component without router props when routerDisabled in feature flags is true', () => {
119
+ const annotatorState = {
120
+ annotation: { foo: 'bar' },
121
+ action: Action.CREATE_START,
122
+ };
123
+ const mockEmitActiveAnnotationChangeEvent = jest.fn();
124
+ const mockEmitAnnotationRemoveEvent = jest.fn();
125
+ const mockEmitAnnotationReplyCreateEvent = jest.fn();
126
+ const mockEmitAnnotationReplyDeleteEvent = jest.fn();
127
+ const mockMmitAnnotationReplyUpdateEvent = jest.fn();
128
+ const mockEmitAnnotationUpdateEvent = jest.fn();
129
+ const mockGetAnnotationsMatchPath = jest.fn();
130
+ const mockGetAnnotationsPath = jest.fn();
131
+
132
+ mockContext.mockReturnValue({
133
+ state: annotatorState,
134
+ emitActiveAnnotationChangeEvent: mockEmitActiveAnnotationChangeEvent,
135
+ emitAnnotationRemoveEvent: mockEmitAnnotationRemoveEvent,
136
+ emitAnnotationReplyCreateEvent: mockEmitAnnotationReplyCreateEvent,
137
+ emitAnnotationReplyDeleteEvent: mockEmitAnnotationReplyDeleteEvent,
138
+ emitAnnotationReplyUpdateEvent: mockMmitAnnotationReplyUpdateEvent,
139
+ emitAnnotationUpdateEvent: mockEmitAnnotationUpdateEvent,
140
+ getAnnotationsMatchPath: mockGetAnnotationsMatchPath,
141
+ getAnnotationsPath: mockGetAnnotationsPath,
142
+ });
143
+
144
+ const MockComponent = jest.fn<JSX.Element | null, [WrappedComponentProps]>(() => null);
145
+ const WrappedWithMockComponent = withAnnotatorContext(MockComponent);
146
+
147
+ render(<WrappedWithMockComponent features={{ routerDisabled: { value: true } }} />);
148
+
149
+ expect(MockComponent).toHaveBeenCalledTimes(1);
150
+ const props = MockComponent.mock.calls[0][0];
151
+
152
+ expect(props.annotatorState).toEqual({
153
+ annotation: { foo: 'bar' },
154
+ action: Action.CREATE_START,
155
+ });
156
+ expect(props.emitActiveAnnotationChangeEvent).toBe(mockEmitActiveAnnotationChangeEvent);
157
+ expect(props.emitAnnotationRemoveEvent).toBe(mockEmitAnnotationRemoveEvent);
158
+ expect(props.emitAnnotationReplyCreateEvent).toBe(mockEmitAnnotationReplyCreateEvent);
159
+ expect(props.emitAnnotationReplyDeleteEvent).toBe(mockEmitAnnotationReplyDeleteEvent);
160
+ expect(props.emitAnnotationReplyUpdateEvent).toBe(mockMmitAnnotationReplyUpdateEvent);
161
+ expect(props.emitAnnotationUpdateEvent).toBe(mockEmitAnnotationUpdateEvent);
162
+
163
+ // Router-related props should not be passed when routerDisabled is true
164
+ expect(props.getAnnotationsMatchPath).toBeUndefined();
165
+ expect(props.getAnnotationsPath).toBeUndefined();
70
166
  });
71
167
  });
@@ -74,8 +74,8 @@ export interface AnnotatorContext {
74
74
  annotation: Object,
75
75
  isStartEvent?: boolean
76
76
  ) => void;
77
- getAnnotationsMatchPath: GetMatchPath;
78
- getAnnotationsPath: (fileVersionId?: string, annotationId?: string) => string;
77
+ getAnnotationsMatchPath?: GetMatchPath;
78
+ getAnnotationsPath?: (fileVersionId?: string, annotationId?: string) => string;
79
79
  state: AnnotatorState;
80
80
  }
81
81
  declare export var Status: {|
@@ -53,8 +53,8 @@ export interface AnnotatorContext {
53
53
  emitAnnotationReplyDeleteEvent?: (id: string, annotationId: string, isStartEvent?: boolean) => void;
54
54
  emitAnnotationReplyUpdateEvent?: (reply: Object, annotationId: string, isStartEvent?: boolean) => void;
55
55
  emitAnnotationUpdateEvent?: (annotation: Object, isStartEvent?: boolean) => void;
56
- getAnnotationsMatchPath: GetMatchPath;
57
- getAnnotationsPath: (fileVersionId?: string, annotationId?: string) => string;
56
+ getAnnotationsMatchPath?: GetMatchPath;
57
+ getAnnotationsPath?: (fileVersionId?: string, annotationId?: string) => string;
58
58
  state: AnnotatorState;
59
59
  }
60
60
 
@@ -18,6 +18,10 @@ import {
18
18
  MatchParams,
19
19
  Status
20
20
  } from "./types";
21
+ import { SidebarNavigation } from '../types/SidebarNavigation';
22
+ import { type FeatureConfig } from '../feature-checking';
23
+
24
+
21
25
  export type ActiveChangeEvent = {
22
26
  annotationId: string | null,
23
27
  fileVersionId: string,
@@ -48,11 +52,11 @@ export type ComponentWithAnnotations = {
48
52
  isStartEvent?: boolean
49
53
  ) => void,
50
54
  getAction: (eventData: AnnotationActionEvent) => Action,
51
- getAnnotationsPath: (
55
+ getAnnotationsPath?: (
52
56
  fileVersionId?: string,
53
57
  annotationId?: string | null
54
58
  ) => string,
55
- getMatchPath: GetMatchPath,
59
+ getMatchPath?: GetMatchPath,
56
60
  handleActiveChange: ActiveChangeEventHandler,
57
61
  handleAnnotationChangeEvent: (id: string | null) => void,
58
62
  handleAnnotationCreate: (eventData: AnnotationActionEvent) => void,
@@ -70,6 +74,7 @@ export type ComponentWithAnnotations = {
70
74
  ...
71
75
  };
72
76
  export type WithAnnotationsProps = {
77
+ features?: FeatureConfig;
73
78
  location?: Location,
74
79
  onAnnotator: (annotator: Annotator) => void,
75
80
  onError?: (
@@ -78,7 +83,9 @@ export type WithAnnotationsProps = {
78
83
  contextInfo?: { [key: string]: mixed, ... }
79
84
  ) => void,
80
85
  onPreviewDestroy: (shouldReset?: boolean) => void,
81
- ...
86
+ routerDisabled?: boolean;
87
+ sidebarNavigation?: SidebarNavigation;
88
+ ...
82
89
  };
83
90
  export type WithAnnotationsComponent<P> = React.ComponentClass<
84
91
  P & WithAnnotationsProps
@@ -3,7 +3,9 @@ import getProp from 'lodash/get';
3
3
  import { generatePath, match as matchType, matchPath } from 'react-router-dom';
4
4
  import { Location } from 'history';
5
5
  import AnnotatorContext from './AnnotatorContext';
6
+ import { isFeatureEnabled, type FeatureConfig } from '../feature-checking';
6
7
  import { Action, Annotator, AnnotationActionEvent, AnnotatorState, GetMatchPath, MatchParams, Status } from './types';
8
+ import { FeedEntryType, SidebarNavigation } from '../types/SidebarNavigation';
7
9
 
8
10
  export type ActiveChangeEvent = {
9
11
  annotationId: string | null;
@@ -41,10 +43,13 @@ export type ComponentWithAnnotations = {
41
43
  };
42
44
 
43
45
  export type WithAnnotationsProps = {
46
+ features?: FeatureConfig;
44
47
  location?: Location;
45
48
  onAnnotator: (annotator: Annotator) => void;
46
49
  onError?: (error: Error, code: string, contextInfo?: Record<string, unknown>) => void;
47
50
  onPreviewDestroy: (shouldReset?: boolean) => void;
51
+ routerDisabled?: boolean;
52
+ sidebarNavigation?: SidebarNavigation;
48
53
  };
49
54
 
50
55
  export type WithAnnotationsComponent<P> = React.ComponentClass<P & WithAnnotationsProps>;
@@ -71,10 +76,26 @@ export default function withAnnotations<P extends object>(
71
76
  constructor(props: P & WithAnnotationsProps) {
72
77
  super(props);
73
78
 
74
- // Determine by url if there is already a deeply linked annotation
75
- const { location } = props;
76
- const match = this.getMatchPath(location);
77
- const activeAnnotationId = getProp(match, 'params.annotationId', null);
79
+ const { routerDisabled, sidebarNavigation } = props;
80
+ let activeAnnotationId = null;
81
+
82
+ const isRouterDisabled = routerDisabled || isFeatureEnabled(props?.features, 'routerDisabled.value');
83
+
84
+ if (isRouterDisabled) {
85
+ if (
86
+ sidebarNavigation &&
87
+ 'activeFeedEntryType' in sidebarNavigation &&
88
+ sidebarNavigation.activeFeedEntryType === FeedEntryType.ANNOTATIONS &&
89
+ 'activeFeedEntryId' in sidebarNavigation
90
+ ) {
91
+ activeAnnotationId = sidebarNavigation.activeFeedEntryId;
92
+ }
93
+ } else {
94
+ // Determine by url if there is already a deeply linked annotation
95
+ const { location } = props;
96
+ const match = this.getMatchPath(location);
97
+ activeAnnotationId = getProp(match, 'params.annotationId', null);
98
+ }
78
99
 
79
100
  // Seed the initial state with the activeAnnotationId if any from the location path
80
101
  this.state = { ...defaultState, activeAnnotationId };
@@ -192,6 +213,7 @@ export default function withAnnotations<P extends object>(
192
213
  });
193
214
  }
194
215
 
216
+ // remove this method with routerDisabled switch
195
217
  getMatchPath(location?: Location): matchType<MatchParams> | null {
196
218
  const pathname = getProp(location, 'pathname', '');
197
219
  return matchPath<MatchParams>(pathname, {
@@ -319,6 +341,14 @@ export default function withAnnotations<P extends object>(
319
341
  };
320
342
 
321
343
  render(): JSX.Element {
344
+ const isRouterDisabled =
345
+ this.props?.routerDisabled || isFeatureEnabled(this.props?.features, 'routerDisabled.value');
346
+ const annotationsRouterProps = isRouterDisabled
347
+ ? {}
348
+ : {
349
+ getAnnotationsMatchPath: this.getMatchPath,
350
+ getAnnotationsPath: this.getAnnotationsPath,
351
+ };
322
352
  return (
323
353
  <AnnotatorContext.Provider
324
354
  value={{
@@ -328,8 +358,7 @@ export default function withAnnotations<P extends object>(
328
358
  emitAnnotationReplyDeleteEvent: this.emitAnnotationReplyDeleteEvent,
329
359
  emitAnnotationReplyUpdateEvent: this.emitAnnotationReplyUpdateEvent,
330
360
  emitAnnotationUpdateEvent: this.emitAnnotationUpdateEvent,
331
- getAnnotationsMatchPath: this.getMatchPath,
332
- getAnnotationsPath: this.getAnnotationsPath,
361
+ ...annotationsRouterProps,
333
362
  state: this.state,
334
363
  }}
335
364
  >
@@ -7,6 +7,7 @@
7
7
  import * as React from "react";
8
8
  import AnnotatorContext from "./AnnotatorContext";
9
9
  import { AnnotatorState, GetMatchPath } from "./types";
10
+
10
11
  export interface WithAnnotatorContextProps {
11
12
  annotatorState?: AnnotatorState;
12
13
  emitActiveAnnotationChangeEvent?: (id: string) => void;
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import AnnotatorContext from './AnnotatorContext';
3
+ import { isFeatureEnabled, type FeatureConfig } from '../feature-checking';
3
4
  import { AnnotatorState, GetMatchPath } from './types';
4
5
 
5
6
  export interface WithAnnotatorContextProps {
@@ -20,33 +21,64 @@ export interface WithAnnotatorContextProps {
20
21
  }
21
22
 
22
23
  export default function withAnnotatorContext<P extends {}>(WrappedComponent: React.ComponentType<P>) {
23
- return React.forwardRef<React.ComponentType<P>, P>((props, ref) => (
24
- <AnnotatorContext.Consumer>
25
- {({
26
- emitActiveAnnotationChangeEvent,
27
- emitAnnotationRemoveEvent,
28
- emitAnnotationReplyCreateEvent,
29
- emitAnnotationReplyDeleteEvent,
30
- emitAnnotationReplyUpdateEvent,
31
- emitAnnotationUpdateEvent,
32
- getAnnotationsMatchPath,
33
- getAnnotationsPath,
34
- state,
35
- }) => (
36
- <WrappedComponent
37
- ref={ref}
38
- {...props}
39
- annotatorState={state}
40
- emitActiveAnnotationChangeEvent={emitActiveAnnotationChangeEvent}
41
- emitAnnotationRemoveEvent={emitAnnotationRemoveEvent}
42
- emitAnnotationReplyCreateEvent={emitAnnotationReplyCreateEvent}
43
- emitAnnotationReplyDeleteEvent={emitAnnotationReplyDeleteEvent}
44
- emitAnnotationReplyUpdateEvent={emitAnnotationReplyUpdateEvent}
45
- emitAnnotationUpdateEvent={emitAnnotationUpdateEvent}
46
- getAnnotationsMatchPath={getAnnotationsMatchPath}
47
- getAnnotationsPath={getAnnotationsPath}
48
- />
49
- )}
50
- </AnnotatorContext.Consumer>
51
- ));
24
+ return React.forwardRef<React.ComponentType<P>, P & { routerDisabled?: boolean; features?: FeatureConfig }>(
25
+ (props, ref) => {
26
+ if (props?.routerDisabled === true || isFeatureEnabled(props?.features, 'routerDisabled.value')) {
27
+ return (
28
+ <AnnotatorContext.Consumer>
29
+ {({
30
+ emitActiveAnnotationChangeEvent,
31
+ emitAnnotationRemoveEvent,
32
+ emitAnnotationReplyCreateEvent,
33
+ emitAnnotationReplyDeleteEvent,
34
+ emitAnnotationReplyUpdateEvent,
35
+ emitAnnotationUpdateEvent,
36
+ state,
37
+ }) => (
38
+ <WrappedComponent
39
+ ref={ref}
40
+ {...props}
41
+ annotatorState={state}
42
+ emitActiveAnnotationChangeEvent={emitActiveAnnotationChangeEvent}
43
+ emitAnnotationRemoveEvent={emitAnnotationRemoveEvent}
44
+ emitAnnotationReplyCreateEvent={emitAnnotationReplyCreateEvent}
45
+ emitAnnotationReplyDeleteEvent={emitAnnotationReplyDeleteEvent}
46
+ emitAnnotationReplyUpdateEvent={emitAnnotationReplyUpdateEvent}
47
+ emitAnnotationUpdateEvent={emitAnnotationUpdateEvent}
48
+ />
49
+ )}
50
+ </AnnotatorContext.Consumer>
51
+ );
52
+ }
53
+ return (
54
+ <AnnotatorContext.Consumer>
55
+ {({
56
+ emitActiveAnnotationChangeEvent,
57
+ emitAnnotationRemoveEvent,
58
+ emitAnnotationReplyCreateEvent,
59
+ emitAnnotationReplyDeleteEvent,
60
+ emitAnnotationReplyUpdateEvent,
61
+ emitAnnotationUpdateEvent,
62
+ getAnnotationsMatchPath,
63
+ getAnnotationsPath,
64
+ state,
65
+ }) => (
66
+ <WrappedComponent
67
+ ref={ref}
68
+ {...props}
69
+ annotatorState={state}
70
+ emitActiveAnnotationChangeEvent={emitActiveAnnotationChangeEvent}
71
+ emitAnnotationRemoveEvent={emitAnnotationRemoveEvent}
72
+ emitAnnotationReplyCreateEvent={emitAnnotationReplyCreateEvent}
73
+ emitAnnotationReplyDeleteEvent={emitAnnotationReplyDeleteEvent}
74
+ emitAnnotationReplyUpdateEvent={emitAnnotationReplyUpdateEvent}
75
+ emitAnnotationUpdateEvent={emitAnnotationUpdateEvent}
76
+ getAnnotationsMatchPath={getAnnotationsMatchPath}
77
+ getAnnotationsPath={getAnnotationsPath}
78
+ />
79
+ )}
80
+ </AnnotatorContext.Consumer>
81
+ );
82
+ },
83
+ );
52
84
  }
@@ -7,7 +7,6 @@
7
7
  import * as React from 'react';
8
8
  import classNames from 'classnames';
9
9
  import { FormattedMessage } from 'react-intl';
10
- import { Route, type Location } from 'react-router-dom';
11
10
  import IconNavigateLeft from '../../../icons/general/IconNavigateLeft';
12
11
  import messages from '../messages';
13
12
  import PlainButton from '../../../components/plain-button';
@@ -15,25 +14,16 @@ import './BackButton.scss';
15
14
 
16
15
  type Props = {
17
16
  className?: string,
18
- to?: Location,
17
+ onClick: () => void,
19
18
  };
20
19
 
21
- const BackButton = ({ className, to, ...rest }: Props) => (
22
- <Route>
23
- {({ history }) => (
24
- <PlainButton
25
- className={classNames('bdl-BackButton', className)}
26
- onClick={() => (to ? history.push(to) : history.goBack())}
27
- type="button"
28
- {...rest}
29
- >
30
- <IconNavigateLeft height={24} width={24} />
31
- <span className="accessibility-hidden">
32
- <FormattedMessage {...messages.back} />
33
- </span>
34
- </PlainButton>
35
- )}
36
- </Route>
20
+ const BackButton = ({ className, onClick, ...rest }: Props) => (
21
+ <PlainButton className={classNames('bdl-BackButton', className)} onClick={onClick} type="button" {...rest}>
22
+ <IconNavigateLeft height={24} width={24} />
23
+ <span className="accessibility-hidden">
24
+ <FormattedMessage {...messages.back} />
25
+ </span>
26
+ </PlainButton>
37
27
  );
38
28
 
39
29
  export default BackButton;
@@ -1,43 +1,52 @@
1
1
  import * as React from 'react';
2
- import { MemoryRouter, Router } from 'react-router-dom';
3
- import { mount } from 'enzyme';
2
+ import { render, screen, userEvent } from '../../../../test-utils/testing-library';
4
3
  import { BackButton } from '..';
5
4
 
6
5
  describe('elements/common/nav-button/BackButton', () => {
7
- const getWrapper = (props = {}) =>
8
- mount(
9
- <MemoryRouter initialEntries={['/start', '/test']}>
10
- <BackButton {...props} />
11
- </MemoryRouter>,
12
- );
13
- const getHistory = wrapper => wrapper.find(Router).prop('history');
14
-
15
- test('should match its snapshot', () => {
16
- const wrapper = getWrapper();
17
- const button = wrapper.find(BackButton).first();
18
-
19
- expect(button).toMatchSnapshot();
6
+ const mockOnClick = jest.fn();
7
+
8
+ beforeEach(() => {
9
+ mockOnClick.mockClear();
20
10
  });
21
11
 
22
- test('should call history back on click if no path is defined', () => {
23
- const wrapper = getWrapper();
24
- const history = getHistory(wrapper);
12
+ test('should render back button with navigation icon and accessible text', () => {
13
+ render(<BackButton onClick={mockOnClick} />);
25
14
 
26
- history.goBack = jest.fn();
15
+ const button = screen.getByRole('button');
16
+ expect(button).toBeInTheDocument();
17
+ expect(button).toHaveClass('bdl-BackButton');
27
18
 
28
- wrapper.simulate('click');
19
+ expect(screen.getByText('Back')).toBeInTheDocument();
29
20
 
30
- expect(history.goBack).toHaveBeenCalled();
21
+ const icon = button.querySelector('svg');
22
+ expect(icon).toBeInTheDocument();
23
+ expect(icon).toHaveClass('icon-navigate-left');
31
24
  });
32
25
 
33
- test('should call history.push on click if a path is defined', () => {
34
- const wrapper = getWrapper({ to: '/new' });
35
- const history = getHistory(wrapper);
26
+ test('should call onClick handler when clicked', async () => {
27
+ const user = userEvent();
28
+
29
+ render(<BackButton onClick={mockOnClick} />);
36
30
 
37
- history.push = jest.fn();
31
+ const button = screen.getByRole('button');
32
+ await user.click(button);
33
+
34
+ expect(mockOnClick).toHaveBeenCalledTimes(1);
35
+ });
36
+
37
+ test('should pass through additional props', () => {
38
+ render(<BackButton onClick={mockOnClick} data-testid="test-back-button" data-resin-target="back" />);
39
+
40
+ const button = screen.getByTestId('test-back-button');
41
+ expect(button).toBeInTheDocument();
42
+ expect(button).toHaveAttribute('data-resin-target', 'back');
43
+ });
38
44
 
39
- wrapper.simulate('click');
45
+ test('should apply custom className alongside default class', () => {
46
+ render(<BackButton onClick={mockOnClick} className="custom-class" />);
40
47
 
41
- expect(history.push).toHaveBeenCalledWith('/new');
48
+ const button = screen.getByRole('button');
49
+ expect(button).toHaveClass('bdl-BackButton');
50
+ expect(button).toHaveClass('custom-class');
42
51
  });
43
52
  });
@@ -0,0 +1,52 @@
1
+ /* @flow */
2
+
3
+ export const ViewType = Object.freeze({
4
+ BOXAI: 'boxai',
5
+ SKILLS: 'skills',
6
+ ACTIVITY: 'activity',
7
+ DETAILS: 'details',
8
+ METADATA: 'metadata',
9
+ DOCGEN: 'docgen',
10
+ });
11
+
12
+ export const FeedEntryType = Object.freeze({
13
+ ANNOTATIONS: 'annotations',
14
+ COMMENTS: 'comments',
15
+ TASKS: 'tasks',
16
+ });
17
+
18
+ export type ViewTypeValues = $Values<typeof ViewType>;
19
+ export type FeedEntryTypeValues = $Values<typeof FeedEntryType>;
20
+
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,
31
+ };
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
+
46
+ export type InternalSidebarNavigation = SidebarNavigation & {
47
+ open: boolean,
48
+ };
49
+
50
+ export type SidebarNavigationHandler = (sidebar: SidebarNavigation, replace?: boolean) => void;
51
+
52
+ export type InternalSidebarNavigationHandler = (sidebar: InternalSidebarNavigation, replace?: boolean) => void;
@@ -0,0 +1,47 @@
1
+ export enum ViewType {
2
+ BOXAI = 'boxai',
3
+ SKILLS = 'skills',
4
+ ACTIVITY = 'activity',
5
+ DETAILS = 'details',
6
+ METADATA = 'metadata',
7
+ DOCGEN = 'docgen',
8
+ }
9
+
10
+ export enum FeedEntryType {
11
+ ANNOTATIONS = 'annotations',
12
+ COMMENTS = 'comments',
13
+ TASKS = 'tasks',
14
+ }
15
+
16
+ type VersionSidebarView = {
17
+ sidebar: ViewType.ACTIVITY | ViewType.DETAILS;
18
+ versionId: string;
19
+ };
20
+
21
+ export type ActivityAnnotationsSidebarView = {
22
+ sidebar: ViewType.ACTIVITY;
23
+ activeFeedEntryType: FeedEntryType.ANNOTATIONS;
24
+ fileVersionId: string;
25
+ activeFeedEntryId: string;
26
+ };
27
+ type ActivityCommentsSidebarView = {
28
+ sidebar: ViewType.ACTIVITY;
29
+ activeFeedEntryType: FeedEntryType.COMMENTS | FeedEntryType.TASKS;
30
+ activeFeedEntryId: string;
31
+ };
32
+
33
+ export type SidebarNavigation =
34
+ | {
35
+ sidebar: ViewType;
36
+ }
37
+ | VersionSidebarView
38
+ | ActivityCommentsSidebarView
39
+ | ActivityAnnotationsSidebarView;
40
+
41
+ export type InternalSidebarNavigation = SidebarNavigation & {
42
+ open: boolean;
43
+ };
44
+
45
+ export type SidebarNavigationHandler = (sidebar: SidebarNavigation, replace?: boolean) => void;
46
+
47
+ export type InternalSidebarNavigationHandler = (sidebar: InternalSidebarNavigation, replace?: boolean) => void;