Skip to content

Commit cb9e714

Browse files
Mingzemergify[bot]
authored andcommitted
feat(sidebar): Show retention policy info in sidebar (#1648)
* feat(sidebar): Show retention policy info in sidebar * feat(sidebar): change retention action message * feat(sidebar): address comments * feat(sidebar): add tooltip * feat(sidebar): improve test * feat(sidebar): change retention removal message
1 parent b716cd7 commit cb9e714

19 files changed

+375
-39
lines changed

flow-typed/box-ui-elements.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ type BoxItemVersionPermission = {
169169
can_upload?: boolean,
170170
};
171171

172+
type BoxItemVersionRetention = {
173+
applied_at: string,
174+
disposition_action: 'delete' | 'remove',
175+
disposition_at: string,
176+
id: string,
177+
type: 'file_version_retention',
178+
};
179+
172180
type User = {
173181
avatar_url?: string,
174182
email?: string,
@@ -364,6 +372,7 @@ type BoxItemVersion = {
364372
permissions?: BoxItemVersionPermission,
365373
restored_at?: string,
366374
restored_by?: ?User,
375+
retention?: BoxItemVersionRetention,
367376
sha1?: string,
368377
size?: number,
369378
trashed_at: ?string,

i18n/en-US.properties

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,10 @@ be.sidebarVersions.versionMaxEntries = Version history is limited to the last {m
488488
be.sidebarVersions.versionNumberBadge = V{versionNumber}
489489
# Label given to the version badge for screen readers.
490490
be.sidebarVersions.versionNumberLabel = Version number {versionNumber}
491+
# Retention message describing when the version will be deleted.
492+
be.sidebarVersions.versionRetentionDelete = Will be deleted {time} by retention policy.
493+
# Retention message describing when the retention policy will be removed.
494+
be.sidebarVersions.versionRetentionRemove = Retention policy expires on {time}.
491495
# Name displayed for unknown or deleted users.
492496
be.sidebarVersions.versionUserUnknown = Unknown
493497
# Header to display for group of versions created today
@@ -974,6 +978,8 @@ boxui.readableTime.eventTimeDate = {time, date, medium} at {time, time, short}
974978
boxui.readableTime.eventTimeDateShort = {date} at {time, time, short}
975979
# The time that an event occurred today
976980
boxui.readableTime.eventTimeToday = Today at {time, time, short}
981+
# The time that an event occurred at a given date with the weekday included
982+
boxui.readableTime.eventTimeWeekdayLong = {weekday}, {time, date, medium}
977983
# The time that an event occurred yesterday
978984
boxui.readableTime.eventTimeYesterday = Yesterday at {time, time, short}
979985
# Title for a clear button

src/components/time/ReadableTime.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ type Props = {
1414
alwaysShowTime?: boolean,
1515
/** The number of milliseconds before now that a relative (vs. absolute) time should be displayed (Default: 1 hour) */
1616
relativeThreshold?: number,
17+
/** A boolean that will include the weekday alongside the date, if the date is shown */
18+
showWeekday?: boolean,
1719
/** The timestamp which should be used to display the date */
1820
timestamp: number,
1921
};
@@ -23,6 +25,7 @@ const ReadableTime = ({
2325
relativeThreshold = ONE_HOUR_MS,
2426
allowFutureTimestamps = true,
2527
alwaysShowTime = false,
28+
showWeekday = false,
2629
}: Props) => {
2730
const relativeIfNewerThanTs = Date.now() - relativeThreshold;
2831
const shouldShowYear = !isCurrentYear(timestamp);
@@ -35,12 +38,17 @@ const ReadableTime = ({
3538
// e.g. Oct 5, 2018
3639
let dateMessage = messages.eventTime;
3740
let date = null;
41+
let weekday = null;
3842
if (isToday(timestamp)) {
3943
// e.g. Today at 12:30 PM
4044
dateMessage = messages.eventTimeToday;
4145
} else if (isYesterday(timestamp)) {
4246
// e.g. Yesterday at 11:30 AM
4347
dateMessage = messages.eventTimeYesterday;
48+
} else if (showWeekday) {
49+
// e.g. Monday, Oct 5, 2018
50+
dateMessage = messages.eventTimeWeekdayLong;
51+
weekday = <FormattedDate value={timestamp} weekday="long" />;
4452
} else if (shouldShowYear && alwaysShowTime) {
4553
// e.g. Oct 5, 2018 at 10:30 PM
4654
dateMessage = messages.eventTimeDate;
@@ -53,7 +61,7 @@ const ReadableTime = ({
5361
return <FormattedDate value={timestamp} month="short" day="numeric" />;
5462
}
5563

56-
let output = <FormattedMessage {...dateMessage} values={{ time: timestamp, date }} />;
64+
let output = <FormattedMessage {...dateMessage} values={{ time: timestamp, date, weekday }} />;
5765

5866
// if the time stamp is within +/- the relative threshold for the current time,
5967
// print the default time format

src/components/time/__tests__/ReadableTime-test.js

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ describe('components/time/ReadableTime', () => {
9494
timestamp: msTwoDaysAgo,
9595
alwaysShowTime: true,
9696
},
97+
{
98+
description: 'should render with weekday when we show weekday',
99+
timestamp: msTwoDaysAgo,
100+
showWeekday: true,
101+
},
97102
{
98103
description: 'should render with format "Today at hh:mm" when there is a future time stamp',
99104
timestamp: relativeThreshold * 2 + now,
@@ -113,20 +118,23 @@ describe('components/time/ReadableTime', () => {
113118
'should render with format "30 minutes ago" when timestamp is within relative threshold (behind)',
114119
timestamp: withinRelativeThresholdBehind,
115120
},
116-
].forEach(({ description, timestamp, allowFutureTimestamps = true, alwaysShowTime = false }) => {
117-
test(description, () => {
118-
const wrapper = shallow(
119-
<ReadableTime
120-
allowFutureTimestamps={allowFutureTimestamps}
121-
alwaysShowTime={alwaysShowTime}
122-
relativeThreshold={oneHourInMs}
123-
timestamp={timestamp}
124-
/>,
125-
);
121+
].forEach(
122+
({ description, timestamp, allowFutureTimestamps = true, alwaysShowTime = false, showWeekday = false }) => {
123+
test(description, () => {
124+
const wrapper = shallow(
125+
<ReadableTime
126+
allowFutureTimestamps={allowFutureTimestamps}
127+
alwaysShowTime={alwaysShowTime}
128+
relativeThreshold={oneHourInMs}
129+
showWeekday={showWeekday}
130+
timestamp={timestamp}
131+
/>,
132+
);
126133

127-
expect(wrapper).toMatchSnapshot();
128-
});
129-
});
134+
expect(wrapper).toMatchSnapshot();
135+
});
136+
},
137+
);
130138

131139
test('should use default relative threshold if not provided', () => {
132140
const wrapper = shallow(<ReadableTime timestamp={withinRelativeThresholdAhead} />);

src/components/time/__tests__/__snapshots__/ReadableTime-test.js.snap

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ exports[`components/time/ReadableTime should render two days ago with format "mm
1212
value={1506378656000}
1313
/>,
1414
"time": 1506378656000,
15+
"weekday": null,
1516
}
1617
}
1718
/>
@@ -33,6 +34,7 @@ exports[`components/time/ReadableTime should render with distant past format "mm
3334
Object {
3435
"date": null,
3536
"time": 946627200000,
37+
"weekday": null,
3638
}
3739
}
3840
/>
@@ -58,6 +60,7 @@ exports[`components/time/ReadableTime should render with format "Today at hh:mm"
5860
Object {
5961
"date": null,
6062
"time": 1506558656000,
63+
"weekday": null,
6164
}
6265
}
6366
/>
@@ -71,6 +74,7 @@ exports[`components/time/ReadableTime should render with format "Yesterday at hh
7174
Object {
7275
"date": null,
7376
"time": 1506465056000,
77+
"weekday": null,
7478
}
7579
}
7680
/>
@@ -81,3 +85,20 @@ exports[`components/time/ReadableTime should render with format "in 30 minutes"
8185
value={1506553256000}
8286
/>
8387
`;
88+
89+
exports[`components/time/ReadableTime should render with weekday when we show weekday 1`] = `
90+
<FormattedMessage
91+
defaultMessage="{weekday}, {time, date, medium}"
92+
id="boxui.readableTime.eventTimeWeekdayLong"
93+
values={
94+
Object {
95+
"date": null,
96+
"time": 1506378656000,
97+
"weekday": <FormattedDate
98+
value={1506378656000}
99+
weekday="long"
100+
/>,
101+
}
102+
}
103+
/>
104+
`;

src/components/time/messages.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ const messages = defineMessages({
2727
description: 'The time that an event occurred at a given date without the year included',
2828
id: 'boxui.readableTime.eventTimeDateShort',
2929
},
30+
eventTimeWeekdayLong: {
31+
defaultMessage: '{weekday}, {time, date, medium}',
32+
description: 'The time that an event occurred at a given date with the weekday included',
33+
id: 'boxui.readableTime.eventTimeWeekdayLong',
34+
},
3035
});
3136

3237
export default messages;

src/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ export const FIELD_ACTIVITY_TEMPLATE: 'activity_template' = 'activity_template';
139139
export const FIELD_APP: 'app' = 'app';
140140
export const FIELD_OCCURRED_AT: 'occurred_at' = 'occurred_at';
141141
export const FIELD_RENDERED_TEXT: 'rendered_text' = 'rendered_text';
142+
export const FIELD_RETENTION: 'retention' = 'retention';
142143

143144
/* ----------------------- Permissions --------------------------- */
144145
export const PERMISSION_CAN_PREVIEW = 'can_preview';
@@ -364,6 +365,8 @@ export const VERSION_DELETE_ACTION: 'delete' = 'delete';
364365
export const VERSION_PROMOTE_ACTION: 'promote' = 'promote';
365366
export const VERSION_RESTORE_ACTION: 'restore' = 'restore';
366367
export const VERSION_UPLOAD_ACTION: 'upload' = 'upload';
368+
export const VERSION_RETENTION_DELETE_ACTION: 'delete' = 'delete';
369+
export const VERSION_RETENTION_REMOVE_ACTION: 'remove' = 'remove';
367370

368371
/* ------------------ Placeholder Feed Items ------------------------- */
369372
export const PLACEHOLDER_USER: UserMini = { type: 'user', id: '2', name: '' };

src/elements/content-sidebar/versions/VersionsItem.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
VERSION_PROMOTE_ACTION,
1919
VERSION_RESTORE_ACTION,
2020
VERSION_UPLOAD_ACTION,
21+
VERSION_RETENTION_DELETE_ACTION,
22+
VERSION_RETENTION_REMOVE_ACTION,
2123
} from '../../../constants';
2224
import type { VersionActionCallback } from './flowTypes';
2325
import './VersionsItem.scss';
@@ -43,6 +45,10 @@ const ACTION_MAP = {
4345
[VERSION_PROMOTE_ACTION]: messages.versionPromotedBy,
4446
[VERSION_UPLOAD_ACTION]: messages.versionUploadedBy,
4547
};
48+
const RETENTION_MAP = {
49+
[VERSION_RETENTION_DELETE_ACTION]: messages.versionRetentionDelete,
50+
[VERSION_RETENTION_REMOVE_ACTION]: messages.versionRetentionRemove,
51+
};
4652
const FIVE_MINUTES_MS = 5 * 60 * 1000;
4753

4854
const VersionsItem = ({
@@ -65,12 +71,19 @@ const VersionsItem = ({
6571
is_download_available,
6672
permissions = {},
6773
restored_at: restoredAt,
74+
retention,
6875
size,
6976
trashed_at: trashedAt,
7077
version_number: versionNumber,
7178
version_promoted: versionPromoted,
7279
} = version;
7380
const { can_delete, can_download, can_preview, can_upload } = permissions;
81+
const {
82+
disposition_action: retentionDispositionAction,
83+
disposition_at: retentionDispositionTime,
84+
applied_at: retentionAppliedTime,
85+
} = retention || {};
86+
const retentionDispositionTimestamp = retentionDispositionTime && new Date(retentionDispositionTime).getTime();
7487

7588
// Version info helpers
7689
const versionAction = selectors.getVersionAction(version);
@@ -86,6 +99,9 @@ const VersionsItem = ({
8699
const isDownloadable = !!is_download_available;
87100
const isLimited = versionCount - versionInteger >= versionLimit;
88101
const isRestricted = isWatermarked && !isCurrent; // Watermarked files do not support prior version preview
102+
const isRetained =
103+
!!retentionAppliedTime &&
104+
(!retentionDispositionTimestamp || retentionDispositionTimestamp > new Date().getTime());
89105

90106
// Version action helpers
91107
const canPreview = can_preview && !isDeleted && !isLimited && !isRestricted;
@@ -129,9 +145,10 @@ const VersionsItem = ({
129145
values={{ name: versionUserName, versionPromoted }}
130146
/>
131147
</div>
148+
132149
<div className="bcs-VersionsItem-info">
133150
{versionTimestamp && (
134-
<time className="bcs-VersionsItem-date" dateTime={createdAt}>
151+
<time className="bcs-VersionsItem-date" dateTime={versionTime}>
135152
<ReadableTime
136153
alwaysShowTime
137154
relativeThreshold={FIVE_MINUTES_MS}
@@ -142,6 +159,17 @@ const VersionsItem = ({
142159
{!!size && <span className="bcs-VersionsItem-size">{sizeUtil(size)}</span>}
143160
</div>
144161

162+
{retentionDispositionTimestamp && (
163+
<div className="bcs-VersionsItem-retention">
164+
<FormattedMessage
165+
{...RETENTION_MAP[retentionDispositionAction]}
166+
values={{
167+
time: <ReadableTime timestamp={retentionDispositionTimestamp} showWeekday />,
168+
}}
169+
/>
170+
</div>
171+
)}
172+
145173
{isLimited && hasActions && (
146174
<div className="bcs-VersionsItem-footer">
147175
<FormattedMessage {...messages.versionLimitExceeded} values={{ versionLimit }} />
@@ -152,6 +180,7 @@ const VersionsItem = ({
152180

153181
{!isLimited && hasActions && (
154182
<VersionsItemActions
183+
enableDelete={!isRetained}
155184
fileId={fileId}
156185
isCurrent={isCurrent}
157186
onDelete={handleAction(onDelete)}

src/elements/content-sidebar/versions/VersionsItem.scss

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,16 @@
3737
.bcs-VersionsItem-details {
3838
flex: 1 1 100%;
3939
overflow: hidden;
40+
41+
& > :not(.bcs-VersionsItem-info) {
42+
margin-right: 35px; // Avoid flowing under action toggle button
43+
}
4044
}
4145

42-
.bcs-VersionsItem-footer {
43-
color: $bdl-gray-62;
44-
font-size: 12px;
46+
.bcs-VersionsItem-log {
47+
overflow: hidden;
48+
white-space: nowrap;
49+
text-overflow: ellipsis;
4550
}
4651

4752
.bcs-VersionsItem-info {
@@ -51,11 +56,16 @@
5156
white-space: nowrap;
5257
}
5358

54-
.bcs-VersionsItem-log {
55-
margin-right: 35px; // Avoid flowing under action toggle button
56-
overflow: hidden;
57-
white-space: nowrap;
58-
text-overflow: ellipsis;
59+
.bcs-VersionsItem-retention {
60+
display: flex;
61+
align-items: center;
62+
margin-top: 8px;
63+
color: $bdl-gray-62;
64+
}
65+
66+
.bcs-VersionsItem-footer {
67+
color: $bdl-gray-62;
68+
font-size: 12px;
5969
}
6070

6171
.bcs-VersionsItem-size {

src/elements/content-sidebar/versions/VersionsItemActions.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import IconTrash from '../../../icons/general/IconTrash';
1515
import IconUpload from '../../../icons/general/IconUpload';
1616
import messages from './messages';
1717
import PlainButton from '../../../components/plain-button';
18+
import Tooltip from '../../../components/tooltip/Tooltip';
1819
import VersionsItemAction from './VersionsItemAction';
1920
import { Menu } from '../../../components/menu';
2021
import './VersionsItemActions.scss';
2122

2223
type Props = {
24+
enableDelete?: boolean,
2325
fileId: string,
2426
isCurrent?: boolean,
2527
onDelete?: () => void,
@@ -45,6 +47,7 @@ const handleToggleClick = (event: SyntheticMouseEvent<HTMLButtonElement>) => {
4547
};
4648

4749
const VersionsItemActions = ({
50+
enableDelete = true,
4851
fileId,
4952
isCurrent = false,
5053
onDelete,
@@ -115,10 +118,23 @@ const VersionsItemActions = ({
115118
</VersionsItemAction>
116119
)}
117120
{showDelete && (
118-
<VersionsItemAction action="remove" fileId={fileId} isCurrent={isCurrent} onClick={onDelete}>
119-
<IconTrash {...ICON_SIZE} />
120-
<FormattedMessage {...messages.versionActionDelete} />
121-
</VersionsItemAction>
121+
<Tooltip
122+
position="middle-left"
123+
text="Disabled by retention policy"
124+
isTabbable={false}
125+
isDisabled={enableDelete}
126+
>
127+
<VersionsItemAction
128+
action="remove"
129+
fileId={fileId}
130+
isCurrent={isCurrent}
131+
isDisabled={!enableDelete}
132+
onClick={onDelete}
133+
>
134+
<IconTrash {...ICON_SIZE} />
135+
<FormattedMessage {...messages.versionActionDelete} />
136+
</VersionsItemAction>
137+
</Tooltip>
122138
)}
123139
</Menu>
124140
</DropdownMenu>

0 commit comments

Comments
 (0)