Skip to content

Commit 064bd2f

Browse files
authored
feat(unified-share-modal): add external collab warning message (#1220)
1 parent ab7a2cc commit 064bd2f

14 files changed

+304
-8
lines changed

i18n/en-US.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,8 @@ boxui.unifiedShare.collaboration.userCollabText = User
11461146
boxui.unifiedShare.collaboratorListTitle = People in '{itemName}'
11471147
# This string is displayed as tooltip on hovering over expire icon for collab
11481148
boxui.unifiedShare.collaborators.expirationTooltipClickableText = Access expires on {date}. Click for details.
1149+
# Text shown in share modal when there is at least one external collaborators
1150+
boxui.unifiedShare.contentSharedWithExternalCollaborators = This content will be shared with external collaborators.
11491151
# Text used in button label to describe permission level - co-owner
11501152
boxui.unifiedShare.coownerLevelButtonLabel = Invite as Co-owner
11511153
# Description for Co-owner permission level in permissions table

src/features/unified-share-modal/ContactsField.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,11 @@ class ContactsField extends React.Component<Props, State> {
9393
// filter contacts who have already been selected
9494
({ email, id }) => !selectedContacts.find(({ value }) => value === email || value === id),
9595
)
96-
.map<Object>(({ email, id, name, type }) => ({
96+
.map<Object>(({ email, id, isExternalUser, name, type }) => ({
9797
// map to standardized DatalistItem format
9898
email,
9999
id,
100+
isExternalUser,
100101
text: name,
101102
type,
102103
value: email || id, // if email doesn't exist, contact is a group, use id

src/features/unified-share-modal/EmailForm.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import PillSelectorDropdown from '../../components/pill-selector-dropdown';
1818
import commonMessages from '../../common/messages';
1919
import { emailValidator } from '../../utils/validators';
2020
import type { inlineNoticeType } from '../../common/box-types';
21+
import IconGlobe from '../../icons/general/IconGlobe';
2122

2223
import ContactsField from './ContactsField';
2324
import messages from './messages';
@@ -37,6 +38,7 @@ type Props = {
3738
intl: IntlShape,
3839
isContactsFieldEnabled: boolean,
3940
isExpanded: boolean,
41+
isExternalUserSelected: boolean,
4042
messageProps?: Object,
4143
onContactAdd?: Function,
4244
onContactInput?: Function,
@@ -210,6 +212,7 @@ class EmailForm extends React.Component<Props, State> {
210212
contactsFieldLabel,
211213
inlineNotice,
212214
isContactsFieldEnabled,
215+
isExternalUserSelected,
213216
getContacts,
214217
intl,
215218
isExpanded,
@@ -283,6 +286,14 @@ class EmailForm extends React.Component<Props, State> {
283286
{...messageProps}
284287
/>
285288
)}
289+
{isExpanded && isExternalUserSelected && (
290+
<div className="security-indicator-note">
291+
<span className="security-indicator-icon-globe">
292+
<IconGlobe height={12} width={12} />
293+
</span>
294+
<FormattedMessage {...messages.contentSharedWithExternalCollaborators} />
295+
</div>
296+
)}
286297
{isExpanded && (
287298
<ModalActions>
288299
<Button isDisabled={submitting} onClick={this.handleClose} type="button" {...cancelButtonProps}>

src/features/unified-share-modal/SharedLinkSection.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ class SharedLinkSection extends React.Component<Props> {
196196
)}
197197
</div>
198198
{accessLevel === ANYONE_WITH_LINK && (
199-
<div className="shared-link-access-note">
200-
<span className="shared-link-icon-globe">
199+
<div className="security-indicator-note">
200+
<span className="security-indicator-icon-globe">
201201
<IconGlobe height={12} width={12} />
202202
</span>
203203
<FormattedMessage {...messages.sharedLinkPubliclyAvailable} />

src/features/unified-share-modal/UnifiedShareModal.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ type State = {
127127
inviteePermissionLevel: string,
128128
isConfirmModalOpen: boolean,
129129
isEmailLinkSectionExpanded: boolean,
130+
isExternalUserInEmailSharedLinkContacts: boolean,
131+
isExternalUserIninviteCollabsContacts: boolean,
130132
isFetching: boolean,
131133
isInviteSectionExpanded: boolean,
132134
sharedLinkLoaded: boolean,
@@ -157,6 +159,8 @@ class UnifiedShareModal extends React.Component<Props, State> {
157159
inviteePermissionLevel: '',
158160
isConfirmModalOpen: false,
159161
isEmailLinkSectionExpanded: false,
162+
isExternalUserInEmailSharedLinkContacts: false,
163+
isExternalUserIninviteCollabsContacts: false,
160164
isFetching: true,
161165
isInviteSectionExpanded: false,
162166
showCollaboratorList: false,
@@ -367,16 +371,28 @@ class UnifiedShareModal extends React.Component<Props, State> {
367371
this.setState({ isEmailLinkSectionExpanded: false });
368372
};
369373

374+
hasExternalContact = (contacts: Array<Contact>) => {
375+
return contacts.some(contact => contact.isExternalUser);
376+
};
377+
370378
updateInviteCollabsContacts = (inviteCollabsContacts: Array<Contact>) => {
371379
const { setUpdatedContacts } = this.props;
372-
this.setState({ inviteCollabsContacts });
380+
const hasExternalContact = this.hasExternalContact(inviteCollabsContacts);
381+
this.setState({
382+
inviteCollabsContacts,
383+
isExternalUserIninviteCollabsContacts: hasExternalContact,
384+
});
373385
if (setUpdatedContacts) {
374386
setUpdatedContacts(inviteCollabsContacts);
375387
}
376388
};
377389

378390
updateEmailSharedLinkContacts = (emailSharedLinkContacts: Array<Contact>) => {
379-
this.setState({ emailSharedLinkContacts });
391+
const hasExternalContact = this.hasExternalContact(emailSharedLinkContacts);
392+
this.setState({
393+
emailSharedLinkContacts,
394+
isExternalUserInEmailSharedLinkContacts: hasExternalContact,
395+
});
380396
};
381397

382398
shouldAutoFocusSharedLink = () => {
@@ -409,7 +425,7 @@ class UnifiedShareModal extends React.Component<Props, State> {
409425
trackingProps,
410426
} = this.props;
411427
const { type } = item;
412-
const { isInviteSectionExpanded, shouldRenderFTUXTooltip } = this.state;
428+
const { isExternalUserIninviteCollabsContacts, isInviteSectionExpanded, shouldRenderFTUXTooltip } = this.state;
413429
const { inviteCollabsEmailTracking, modalTracking } = trackingProps;
414430
const contactsFieldDisabledTooltip =
415431
type === ITEM_TYPE_WEBLINK ? (
@@ -470,6 +486,7 @@ class UnifiedShareModal extends React.Component<Props, State> {
470486
inlineNotice={inlineNotice}
471487
isContactsFieldEnabled={canInvite}
472488
isExpanded={isInviteSectionExpanded}
489+
isExternalUserSelected={isExternalUserIninviteCollabsContacts}
473490
onContactInput={this.openInviteCollaborators}
474491
onRequestClose={this.closeInviteCollaborators}
475492
onSubmit={this.handleSendInvites}
@@ -652,6 +669,7 @@ class UnifiedShareModal extends React.Component<Props, State> {
652669
const { modalProps } = modalTracking;
653670
const {
654671
isEmailLinkSectionExpanded,
672+
isExternalUserInEmailSharedLinkContacts,
655673
isFetching,
656674
isInviteSectionExpanded,
657675
isConfirmModalOpen,
@@ -710,6 +728,7 @@ class UnifiedShareModal extends React.Component<Props, State> {
710728
}}
711729
isContactsFieldEnabled
712730
isExpanded
731+
isExternalUserSelected={isExternalUserInEmailSharedLinkContacts}
713732
onRequestClose={this.closeEmailSharedLinkForm}
714733
onSubmit={this.handleSendSharedLink}
715734
showEnterEmailsCallout={showEnterEmailsCallout}

src/features/unified-share-modal/UnifiedShareModal.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,11 @@
212212
}
213213
}
214214

215-
.shared-link-access-note {
215+
.security-indicator-note {
216216
color: $sevens;
217217
margin-top: 10px;
218218

219-
.shared-link-icon-globe {
219+
.security-indicator-icon-globe {
220220
padding-right: 5px;
221221

222222
.icon-globe {

src/features/unified-share-modal/__tests__/ContactsField-test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,21 @@ describe('features/unified-share-modal/ContactsField', () => {
99
{
1010
email: 'x@example.com',
1111
id: '12345',
12+
isExternalUser: false,
1213
name: 'X User',
1314
type: 'group',
1415
},
1516
{
1617
email: 'y@example.com',
1718
id: '23456',
19+
isExternalUser: true,
1820
name: 'Y User',
1921
type: 'user',
2022
},
2123
{
2224
email: 'z@example.com',
2325
id: '34567',
26+
isExternalUser: false,
2427
name: 'Z User',
2528
type: 'user',
2629
},
@@ -30,20 +33,23 @@ describe('features/unified-share-modal/ContactsField', () => {
3033
{
3134
email: 'x@example.com',
3235
id: '12345',
36+
isExternalUser: false,
3337
text: 'X User',
3438
type: 'group',
3539
value: 'x@example.com',
3640
},
3741
{
3842
email: 'y@example.com',
3943
id: '23456',
44+
isExternalUser: true,
4045
text: 'Y User',
4146
type: 'user',
4247
value: 'y@example.com',
4348
},
4449
{
4550
email: 'z@example.com',
4651
id: '34567',
52+
isExternalUser: false,
4753
text: 'Z User',
4854
type: 'user',
4955
value: 'z@example.com',
@@ -261,30 +267,35 @@ describe('features/unified-share-modal/ContactsField', () => {
261267
{
262268
email: 'a@example.com',
263269
id: '12',
270+
isExternalUser: true,
264271
name: 'a b',
265272
type: 'user',
266273
},
267274
{
268275
email: 'b@example.com',
269276
id: '13',
277+
isExternalUser: false,
270278
name: 'a b',
271279
type: 'user',
272280
},
273281
{
274282
email: 'c@example.com',
275283
id: '14',
284+
isExternalUser: true,
276285
name: 'a c',
277286
type: 'user',
278287
},
279288
{
280289
email: 'd@example.com',
281290
id: '14',
291+
isExternalUser: false,
282292
name: 'a d',
283293
type: 'user',
284294
},
285295
{
286296
email: 'e@example.com',
287297
id: '14',
298+
isExternalUser: true,
288299
name: 'a e',
289300
type: 'user',
290301
},

src/features/unified-share-modal/__tests__/EmailForm-test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,11 @@ describe('features/unified-share-modal/EmailForm', () => {
316316
expect(wrapper).toMatchSnapshot();
317317
});
318318

319+
test('should render default component with secruity indicator notes when expanded and has external users selected', () => {
320+
const wrapper = getWrapper({ isExpanded: true, isExternalUserSelected: true });
321+
expect(wrapper).toMatchSnapshot();
322+
});
323+
319324
test('should render default component when not expanded', () => {
320325
const wrapper = getWrapper({ isExpanded: false });
321326
expect(wrapper).toMatchSnapshot();

src/features/unified-share-modal/__tests__/UnifiedShareModal-test.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,124 @@ describe('features/unified-share-modal/UnifiedShareModal', () => {
267267
});
268268
});
269269

270+
describe('updateInviteCollabsContacts', () => {
271+
test('should set isExternalUserIninviteCollabsContacts to true if the invited collabs include at least one external user', () => {
272+
const contacts = [
273+
{
274+
email: 'x@example.com',
275+
id: '12345',
276+
isExternalUser: false,
277+
name: 'X User',
278+
type: 'group',
279+
},
280+
{
281+
email: 'y@example.com',
282+
id: '23456',
283+
isExternalUser: true,
284+
name: 'Y User',
285+
type: 'user',
286+
},
287+
{
288+
email: 'z@example.com',
289+
id: '34567',
290+
isExternalUser: false,
291+
name: 'Z User',
292+
type: 'user',
293+
},
294+
];
295+
296+
const wrapper = getWrapper();
297+
wrapper.setState({ isExternalUserIninviteCollabsContacts: false });
298+
wrapper.instance().updateInviteCollabsContacts(contacts);
299+
300+
expect(wrapper.state('isExternalUserIninviteCollabsContacts')).toBe(true);
301+
});
302+
303+
test('should not set isExternalUserIninviteCollabsContacts to true if the invited collabs does not include any external user', () => {
304+
const contacts = [
305+
{
306+
email: 'x@example.com',
307+
id: '12345',
308+
isExternalUser: false,
309+
name: 'X User',
310+
type: 'group',
311+
},
312+
{
313+
email: 'z@example.com',
314+
id: '34567',
315+
isExternalUser: false,
316+
name: 'Z User',
317+
type: 'user',
318+
},
319+
];
320+
321+
const wrapper = getWrapper();
322+
wrapper.setState({ isExternalUserIninviteCollabsContacts: false });
323+
wrapper.instance().updateInviteCollabsContacts(contacts);
324+
325+
expect(wrapper.state('isExternalUserIninviteCollabsContacts')).toBe(false);
326+
});
327+
});
328+
329+
describe('updateEmailSharedLinkContacts', () => {
330+
test('should set isExternalUserInEmailSharedLinkContacts to true if the Email Shared Link contacts include at least one external user', () => {
331+
const contacts = [
332+
{
333+
email: 'x@example.com',
334+
id: '12345',
335+
isExternalUser: false,
336+
name: 'X User',
337+
type: 'group',
338+
},
339+
{
340+
email: 'y@example.com',
341+
id: '23456',
342+
isExternalUser: true,
343+
name: 'Y User',
344+
type: 'user',
345+
},
346+
{
347+
email: 'z@example.com',
348+
id: '34567',
349+
isExternalUser: false,
350+
name: 'Z User',
351+
type: 'user',
352+
},
353+
];
354+
355+
const wrapper = getWrapper();
356+
wrapper.setState({ isExternalUserInEmailSharedLinkContacts: false });
357+
wrapper.instance().updateEmailSharedLinkContacts(contacts);
358+
359+
expect(wrapper.state('isExternalUserInEmailSharedLinkContacts')).toBe(true);
360+
});
361+
362+
test('should not set isExternalUserInEmailSharedLinkContacts to true if the Email Shared Link contacts does not include any external user', () => {
363+
const contacts = [
364+
{
365+
email: 'x@example.com',
366+
id: '12345',
367+
isExternalUser: false,
368+
name: 'X User',
369+
type: 'group',
370+
},
371+
{
372+
email: 'z@example.com',
373+
id: '34567',
374+
isExternalUser: false,
375+
name: 'Z User',
376+
type: 'user',
377+
},
378+
];
379+
380+
const wrapper = getWrapper();
381+
wrapper.setState({ isExternalUserInEmailSharedLinkContacts: false });
382+
wrapper.instance().updateEmailSharedLinkContacts(contacts);
383+
384+
expect(wrapper.state('isExternalUserInEmailSharedLinkContacts')).toBe(false);
385+
});
386+
});
387+
270388
describe('getInitialData()', () => {
271389
test('getInitialData is not called when item.type is null', () => {
272390
const getInitialDataStub = jest.fn();

0 commit comments

Comments
 (0)