box-ui-elements 25.1.0-beta.2 → 25.1.0-beta.4
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.
- package/dist/explorer.js +1 -1
- package/dist/openwith.js +1 -1
- package/dist/picker.js +1 -1
- package/dist/preview.js +1 -1
- package/dist/sharing.js +1 -1
- package/dist/sidebar.js +1 -1
- package/dist/uploader.js +1 -1
- package/es/constants.js +1 -1
- package/es/elements/content-sidebar/activity-feed/annotations/AnnotationActivity.js +6 -1
- package/es/elements/content-sidebar/activity-feed/annotations/AnnotationActivity.js.flow +9 -1
- package/es/elements/content-sidebar/activity-feed/annotations/AnnotationActivity.js.map +1 -1
- package/es/elements/content-sidebar/activity-feed/common/activity-message/ActivityMessage.js +4 -2
- package/es/elements/content-sidebar/activity-feed/common/activity-message/ActivityMessage.js.map +1 -1
- package/es/elements/content-sidebar/activity-feed/utils/formatTaggedMessage.js +17 -8
- package/es/elements/content-sidebar/activity-feed/utils/formatTaggedMessage.js.flow +29 -11
- package/es/elements/content-sidebar/activity-feed/utils/formatTaggedMessage.js.map +1 -1
- package/es/features/security-cloud-game/DragCloud.js +3 -0
- package/es/features/security-cloud-game/DragCloud.js.flow +17 -2
- package/es/features/security-cloud-game/DragCloud.js.map +1 -1
- package/es/src/elements/content-sidebar/activity-feed/common/activity-message/ActivityMessage.d.ts +2 -0
- package/package.json +3 -3
- package/src/elements/content-sidebar/activity-feed/annotations/AnnotationActivity.js +9 -1
- package/src/elements/content-sidebar/activity-feed/annotations/__tests__/AnnotationActivity.test.js +149 -0
- package/src/elements/content-sidebar/activity-feed/common/activity-message/ActivityMessage.tsx +13 -2
- package/src/elements/content-sidebar/activity-feed/common/activity-message/__tests__/ActivityMessage.test.js +30 -0
- package/src/elements/content-sidebar/activity-feed/utils/formatTaggedMessage.js +29 -11
- package/src/features/security-cloud-game/DragCloud.js +17 -2
package/src/elements/content-sidebar/activity-feed/annotations/__tests__/AnnotationActivity.test.js
CHANGED
|
@@ -367,4 +367,153 @@ describe('elements/content-sidebar/ActivityFeed/annotations/AnnotationActivity',
|
|
|
367
367
|
expect(event.stopPropagation).not.toHaveBeenCalled();
|
|
368
368
|
});
|
|
369
369
|
});
|
|
370
|
+
|
|
371
|
+
describe('video annotations', () => {
|
|
372
|
+
const mockVideoAnnotation = {
|
|
373
|
+
...mockAnnotation,
|
|
374
|
+
target: {
|
|
375
|
+
location: {
|
|
376
|
+
type: 'frame',
|
|
377
|
+
value: 60000, // 1 minute in milliseconds
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
test('should detect video annotation when target location type is frame', () => {
|
|
383
|
+
const wrapper = getWrapper({ item: mockVideoAnnotation });
|
|
384
|
+
const activityMessage = wrapper.find('ForwardRef(withFeatureConsumer(ActivityMessage))');
|
|
385
|
+
|
|
386
|
+
expect(activityMessage.prop('annotationsMillisecondTimestamp')).toBe('0:01:00');
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test('should not show version link for video annotations even when hasVersions is true', () => {
|
|
390
|
+
const wrapper = getWrapper({
|
|
391
|
+
item: mockVideoAnnotation,
|
|
392
|
+
hasVersions: true,
|
|
393
|
+
isCurrentVersion: true,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
expect(wrapper.exists('AnnotationActivityLink')).toBe(false);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test('should pass correct timestamp format to ActivityMessage for video annotations', () => {
|
|
400
|
+
const videoAnnotationWithTimestamp = {
|
|
401
|
+
...mockVideoAnnotation,
|
|
402
|
+
target: {
|
|
403
|
+
location: {
|
|
404
|
+
type: 'frame',
|
|
405
|
+
value: 3661000, // 1 hour, 1 minute, 1 second
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const wrapper = getWrapper({ item: videoAnnotationWithTimestamp });
|
|
411
|
+
const activityMessage = wrapper.find('ForwardRef(withFeatureConsumer(ActivityMessage))');
|
|
412
|
+
|
|
413
|
+
expect(activityMessage.prop('annotationsMillisecondTimestamp')).toBe('1:01:01');
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('should handle zero timestamp for video annotations', () => {
|
|
417
|
+
const videoAnnotationWithZeroTimestamp = {
|
|
418
|
+
...mockVideoAnnotation,
|
|
419
|
+
target: {
|
|
420
|
+
location: {
|
|
421
|
+
type: 'frame',
|
|
422
|
+
value: 0,
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const wrapper = getWrapper({ item: videoAnnotationWithZeroTimestamp });
|
|
428
|
+
const activityMessage = wrapper.find('ForwardRef(withFeatureConsumer(ActivityMessage))');
|
|
429
|
+
|
|
430
|
+
expect(activityMessage.prop('annotationsMillisecondTimestamp')).toBe('0:00:00');
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test('should handle undefined timestamp for video annotations', () => {
|
|
434
|
+
const videoAnnotationWithUndefinedTimestamp = {
|
|
435
|
+
...mockVideoAnnotation,
|
|
436
|
+
target: {
|
|
437
|
+
location: {
|
|
438
|
+
type: 'frame',
|
|
439
|
+
value: undefined,
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
const wrapper = getWrapper({ item: videoAnnotationWithUndefinedTimestamp });
|
|
445
|
+
const activityMessage = wrapper.find('ForwardRef(withFeatureConsumer(ActivityMessage))');
|
|
446
|
+
|
|
447
|
+
expect(activityMessage.prop('annotationsMillisecondTimestamp')).toBe('0:00:00');
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test('should not pass timestamp to ActivityMessage for non-video annotations', () => {
|
|
451
|
+
const regularAnnotation = {
|
|
452
|
+
...mockAnnotation,
|
|
453
|
+
target: {
|
|
454
|
+
location: {
|
|
455
|
+
type: 'page',
|
|
456
|
+
value: 1,
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
const wrapper = getWrapper({ item: regularAnnotation });
|
|
462
|
+
const activityMessage = wrapper.find('ForwardRef(withFeatureConsumer(ActivityMessage))');
|
|
463
|
+
|
|
464
|
+
expect(activityMessage.prop('annotationsMillisecondTimestamp')).toBeFalsy();
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test('should not pass timestamp to ActivityMessage when target location type is missing', () => {
|
|
468
|
+
const annotationWithoutType = {
|
|
469
|
+
...mockAnnotation,
|
|
470
|
+
target: {
|
|
471
|
+
location: {
|
|
472
|
+
value: 60000,
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
const wrapper = getWrapper({ item: annotationWithoutType });
|
|
478
|
+
const activityMessage = wrapper.find('ForwardRef(withFeatureConsumer(ActivityMessage))');
|
|
479
|
+
|
|
480
|
+
expect(activityMessage.prop('annotationsMillisecondTimestamp')).toBeFalsy();
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test('should not pass timestamp to ActivityMessage when target is missing', () => {
|
|
484
|
+
const annotationWithoutTarget = {
|
|
485
|
+
...mockAnnotation,
|
|
486
|
+
target: {
|
|
487
|
+
location: {
|
|
488
|
+
value: 1,
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const wrapper = getWrapper({ item: annotationWithoutTarget });
|
|
494
|
+
const activityMessage = wrapper.find('ForwardRef(withFeatureConsumer(ActivityMessage))');
|
|
495
|
+
|
|
496
|
+
expect(activityMessage.prop('annotationsMillisecondTimestamp')).toBeFalsy();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test('should show version link for non-video annotations when hasVersions is true', () => {
|
|
500
|
+
const regularAnnotation = {
|
|
501
|
+
...mockAnnotation,
|
|
502
|
+
target: {
|
|
503
|
+
location: {
|
|
504
|
+
type: 'page',
|
|
505
|
+
value: 1,
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
const wrapper = getWrapper({
|
|
511
|
+
item: regularAnnotation,
|
|
512
|
+
hasVersions: true,
|
|
513
|
+
isCurrentVersion: true,
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
expect(wrapper.exists('AnnotationActivityLink')).toBe(true);
|
|
517
|
+
});
|
|
518
|
+
});
|
|
370
519
|
});
|
package/src/elements/content-sidebar/activity-feed/common/activity-message/ActivityMessage.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import LoadingIndicator, { LoadingIndicatorSize } from '../../../../../component
|
|
|
7
7
|
import ShowOriginalButton from './ShowOriginalButton';
|
|
8
8
|
import TranslateButton from './TranslateButton';
|
|
9
9
|
|
|
10
|
-
import formatTaggedMessage from '../../utils/formatTaggedMessage';
|
|
10
|
+
import formatTaggedMessage, { renderTimestampWithText } from '../../utils/formatTaggedMessage';
|
|
11
11
|
import { withFeatureConsumer, isFeatureEnabled } from '../../../../common/feature-checking';
|
|
12
12
|
|
|
13
13
|
import messages from './messages';
|
|
@@ -22,11 +22,13 @@ export interface ActivityMessageProps extends WrappedComponentProps {
|
|
|
22
22
|
getUserProfileUrl?: GetProfileUrlCallback;
|
|
23
23
|
id: string;
|
|
24
24
|
isEdited?: boolean;
|
|
25
|
+
onClick?: () => void;
|
|
25
26
|
onTranslate?: ({ id, tagged_message }: { id: string; tagged_message: string }) => void;
|
|
26
27
|
tagged_message: string;
|
|
27
28
|
translatedTaggedMessage?: string;
|
|
28
29
|
translationEnabled?: boolean;
|
|
29
30
|
translationFailed?: boolean | null;
|
|
31
|
+
annotationsMillisecondTimestamp?: string | null;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
type State = {
|
|
@@ -92,6 +94,8 @@ class ActivityMessage extends React.Component<ActivityMessageProps, State> {
|
|
|
92
94
|
id,
|
|
93
95
|
intl,
|
|
94
96
|
isEdited,
|
|
97
|
+
onClick = noop,
|
|
98
|
+
annotationsMillisecondTimestamp,
|
|
95
99
|
tagged_message,
|
|
96
100
|
translatedTaggedMessage,
|
|
97
101
|
translationEnabled,
|
|
@@ -110,7 +114,14 @@ class ActivityMessage extends React.Component<ActivityMessageProps, State> {
|
|
|
110
114
|
) : (
|
|
111
115
|
<div className="bcs-ActivityMessage">
|
|
112
116
|
<MessageWrapper>
|
|
113
|
-
{
|
|
117
|
+
{annotationsMillisecondTimestamp
|
|
118
|
+
? renderTimestampWithText(
|
|
119
|
+
annotationsMillisecondTimestamp,
|
|
120
|
+
onClick,
|
|
121
|
+
intl,
|
|
122
|
+
` ${commentToDisplay}`,
|
|
123
|
+
)
|
|
124
|
+
: formatTaggedMessage(commentToDisplay, id, false, getUserProfileUrl, intl)}
|
|
114
125
|
{isEdited && (
|
|
115
126
|
<span className="bcs-ActivityMessage-edited">
|
|
116
127
|
<FormattedMessage {...messages.activityMessageEdited} />
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { act } from 'react';
|
|
2
2
|
import { mount, shallow } from 'enzyme';
|
|
3
|
+
import { createIntl } from 'react-intl';
|
|
3
4
|
|
|
4
5
|
import { ActivityMessage } from '../ActivityMessage';
|
|
5
6
|
|
|
@@ -184,4 +185,33 @@ describe('elements/content-sidebar/ActivityFeed/common/activity-message', () =>
|
|
|
184
185
|
|
|
185
186
|
expect(wrapper.exists({ id: 'be.contentSidebar.activityFeed.common.editedMessage' })).toBe(expected);
|
|
186
187
|
});
|
|
188
|
+
|
|
189
|
+
describe('video annotation', () => {
|
|
190
|
+
test('should render timestamp with text when annotationsMillisecondTimestamp is provided', () => {
|
|
191
|
+
const onClick = jest.fn();
|
|
192
|
+
const videoAnnotation = {
|
|
193
|
+
annotationsMillisecondTimestamp: '0:01:00',
|
|
194
|
+
tagged_message: 'test',
|
|
195
|
+
onClick,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const intl = createIntl({ locale: 'en' });
|
|
199
|
+
const wrapper = mount(<ActivityMessage id="123" {...videoAnnotation} intl={intl} />);
|
|
200
|
+
expect(wrapper.find('button[aria-label="Seek to video timestamp"]').length).toBe(1);
|
|
201
|
+
expect(wrapper.find('button[aria-label="Seek to video timestamp"]').text()).toBe('0:01:00');
|
|
202
|
+
wrapper.find('button[aria-label="Seek to video timestamp"]').simulate('click');
|
|
203
|
+
expect(onClick).toHaveBeenCalled();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('should render original message when annotationsMillisecondTimestamp is not provided', () => {
|
|
207
|
+
const comment = {
|
|
208
|
+
annotationsMillisecondTimestamp: undefined,
|
|
209
|
+
tagged_message: 'test',
|
|
210
|
+
};
|
|
211
|
+
const intl = createIntl({ locale: 'en' });
|
|
212
|
+
const wrapper = mount(<ActivityMessage id="123" {...comment} intl={intl} />);
|
|
213
|
+
expect(wrapper.find('button[aria-label="Seek to video timestamp"]').length).toBe(0);
|
|
214
|
+
expect(wrapper.find('.bcs-ActivityMessage').text()).toBe('test');
|
|
215
|
+
});
|
|
216
|
+
});
|
|
187
217
|
});
|
|
@@ -11,6 +11,34 @@ import UserLink from '../common/user-link';
|
|
|
11
11
|
import messages from '../common/activity-message/messages';
|
|
12
12
|
import { convertTimestampToSeconds, convertMillisecondsToHMMSS } from '../../../../utils/timestamp';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Renders the timestamp button and remaining text
|
|
16
|
+
* @param timestampInHHMMSS The formatted timestamp string (HH:MM:SS)
|
|
17
|
+
* @param timestampLabel The aria label for the timestamp button
|
|
18
|
+
* @param handleClick The click handler for the timestamp button
|
|
19
|
+
* @param textAfterTimestamp The text that comes after the timestamp
|
|
20
|
+
* @returns A React Fragment with timestamp button and text
|
|
21
|
+
*/
|
|
22
|
+
export const renderTimestampWithText = (
|
|
23
|
+
timestampInHHMMSS: string,
|
|
24
|
+
handleClick: (e: SyntheticMouseEvent<HTMLButtonElement>) => void,
|
|
25
|
+
intl: IntlShape,
|
|
26
|
+
textAfterTimestamp: string,
|
|
27
|
+
): React$Element<any> => (
|
|
28
|
+
<>
|
|
29
|
+
<div className="bcs-ActivityMessage-timestamp">
|
|
30
|
+
<button
|
|
31
|
+
aria-label={intl.formatMessage(messages.activityMessageTimestampLabel)}
|
|
32
|
+
type="button"
|
|
33
|
+
onClick={handleClick}
|
|
34
|
+
>
|
|
35
|
+
{timestampInHHMMSS}
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
{textAfterTimestamp}
|
|
39
|
+
</>
|
|
40
|
+
);
|
|
41
|
+
|
|
14
42
|
/**
|
|
15
43
|
* Formats text containing a timestamp by wrapping the timestamp in a Link component
|
|
16
44
|
* @param text The text containing the timestamp
|
|
@@ -51,17 +79,7 @@ const formatTimestamp = (text: string, timestamp: string, intl: IntlShape): Reac
|
|
|
51
79
|
}
|
|
52
80
|
};
|
|
53
81
|
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<>
|
|
57
|
-
<div className="bcs-ActivityMessage-timestamp">
|
|
58
|
-
<button aria-label={timestampLabel} type="button" onClick={handleClick}>
|
|
59
|
-
{timestampInHHMMSS}
|
|
60
|
-
</button>
|
|
61
|
-
</div>
|
|
62
|
-
{textAfterTimestamp}
|
|
63
|
-
</>
|
|
64
|
-
);
|
|
82
|
+
return renderTimestampWithText(timestampInHHMMSS, handleClick, intl, textAfterTimestamp);
|
|
65
83
|
};
|
|
66
84
|
|
|
67
85
|
// this regex matches one of the following regular expressions:
|
|
@@ -33,6 +33,7 @@ const DragCloud = ({
|
|
|
33
33
|
updateLiveText,
|
|
34
34
|
updatePosition,
|
|
35
35
|
}) => {
|
|
36
|
+
const nodeRef = React.useRef({});
|
|
36
37
|
const [isMoving, setIsMoving] = useState(false);
|
|
37
38
|
|
|
38
39
|
const dragCloudClasses = classNames('bdl-DragCloud', {
|
|
@@ -137,8 +138,22 @@ const DragCloud = ({
|
|
|
137
138
|
const onDrag = throttle((e, { x, y }) => updatePosition({ x, y }), 100, { leading: true, trailing: true });
|
|
138
139
|
|
|
139
140
|
return (
|
|
140
|
-
<Draggable
|
|
141
|
-
|
|
141
|
+
<Draggable
|
|
142
|
+
nodeRef={nodeRef}
|
|
143
|
+
bounds="parent"
|
|
144
|
+
disabled={disabled}
|
|
145
|
+
onDrag={onDrag}
|
|
146
|
+
onStop={onDrop}
|
|
147
|
+
position={position}
|
|
148
|
+
>
|
|
149
|
+
<div
|
|
150
|
+
ref={nodeRef}
|
|
151
|
+
className={dragCloudClasses}
|
|
152
|
+
onBlur={onBlur}
|
|
153
|
+
onKeyDown={onKeyDown}
|
|
154
|
+
role="button"
|
|
155
|
+
tabIndex={0}
|
|
156
|
+
>
|
|
142
157
|
<IconCloud
|
|
143
158
|
filter={{ id: 'drop-shadow', definition: <DropShadowFilter /> }}
|
|
144
159
|
height={cloudSize}
|