cozy-sharing 32.1.1 → 32.3.1

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 (65) hide show
  1. package/.claude/settings.local.json +27 -0
  2. package/.storybook/main.js +8 -1
  3. package/.storybook/preview.jsx +11 -6
  4. package/AGENTS.md +7 -0
  5. package/CHANGELOG.md +22 -0
  6. package/dist/RefreshableSharings.spec.js +40 -0
  7. package/dist/assets/illustrations/illustration-shared-drives.svg +1 -0
  8. package/dist/components/EditLinkPermissionDialog.js +72 -0
  9. package/dist/components/EditLinkPermissionDialog.spec.js +48 -0
  10. package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +65 -0
  11. package/dist/components/FederatedFolder/DumbFederatedFolderModal.spec.js +107 -0
  12. package/dist/components/FederatedFolder/FederatedFolderModal.js +41 -4
  13. package/dist/components/FederatedFolder/FederatedFolderModal.jsx.tmp.179197.1773939270471 +172 -0
  14. package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +79 -8
  15. package/dist/components/Recipient/LinkRecipient.stories.js +3 -0
  16. package/dist/components/Recipient/LinkRecipientPermissions.stories.js +5 -1
  17. package/dist/components/Recipient/actions/revokeGroup.js +42 -0
  18. package/dist/components/Recipient/actions/revokeMember.js +42 -0
  19. package/dist/components/Recipient/actions/revokeSharedDriveMember.js +41 -0
  20. package/dist/components/ShareAutosuggest.js +1 -0
  21. package/dist/components/ShareAutosuggest.spec.js +12 -4
  22. package/dist/components/ShareDialogCozyToCozy.spec.js +55 -8
  23. package/dist/components/ShareDiscardChangesModal.js +35 -0
  24. package/dist/components/ShareModal/ShareModal copy.js +43 -0
  25. package/dist/components/ShareModal/ShareModal.spec.js +197 -0
  26. package/dist/components/ShareModal/ShareModalBatchContainer.js +276 -0
  27. package/dist/components/ShareModal/ShareModalBatchContainer.spec.js +131 -0
  28. package/dist/components/ShareModal/SharedDriveEditModal.js +59 -0
  29. package/dist/components/ShareRecipientsInput.js +13 -3
  30. package/dist/components/ShareRecipientsInput.spec.js +63 -4
  31. package/dist/components/SharedDrive/DumbSharedDriveModal.js +78 -0
  32. package/dist/components/SharedDrive/DumbSharedDriveModal.spec.js +126 -0
  33. package/dist/components/SharedDrive/SharedDriveEditModal.js +110 -0
  34. package/dist/components/SharedDrive/SharedDriveModal copy.js +356 -0
  35. package/dist/components/SharedDrive/helpers.js +102 -0
  36. package/dist/components/SharedDrive/helpers.spec.js +208 -0
  37. package/dist/components/SharedDriveEditModal.js +43 -0
  38. package/dist/components/SharedFolder/DumbBatchSharedFolderModal.spec.js +321 -0
  39. package/dist/components/SharedStatus.js +61 -0
  40. package/dist/components/SharedStatus.spec.js +44 -0
  41. package/dist/components/Sharesubmit.js +28 -0
  42. package/dist/components/__snapshots__/SharedStatus.spec.js.snap +94 -0
  43. package/dist/helpers/owner.js +14 -0
  44. package/dist/helpers/owner.spec.js +34 -0
  45. package/dist/hooks/useConfirmDiscardChanges.js +33 -0
  46. package/dist/hooks/useContactsAndGroups.js +65 -0
  47. package/dist/hooks/useSharedDrive.js +130 -0
  48. package/dist/hooks/useSharedDriveContactsAndGroups.js +65 -0
  49. package/dist/propTypes.js +26 -0
  50. package/dist/styles/shareddrive.styl +8 -0
  51. package/locales/en.json +10 -5
  52. package/locales/fr.json +10 -5
  53. package/locales/ru.json +10 -5
  54. package/locales/vi.json +10 -5
  55. package/package.json +2 -2
  56. package/src/components/FederatedFolder/FederatedFolderModal.jsx +184 -138
  57. package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +47 -0
  58. package/src/components/Recipient/LinkRecipient.stories.jsx +1 -0
  59. package/src/components/Recipient/LinkRecipientPermissions.stories.jsx +3 -1
  60. package/src/components/ShareAutosuggest.jsx +1 -0
  61. package/src/components/ShareAutosuggest.spec.jsx +10 -5
  62. package/src/components/ShareDialogCozyToCozy.spec.jsx +50 -4
  63. package/src/components/ShareDiscardChangesModal.jsx +40 -0
  64. package/src/components/ShareRecipientsInput.jsx +11 -6
  65. package/src/components/ShareRecipientsInput.spec.jsx +59 -0
@@ -0,0 +1,172 @@
1
+ import PropTypes from 'prop-types'
2
+ import React, { useState, useEffect } from 'react'
3
+
4
+ import { useClient } from 'cozy-client'
5
+ import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
6
+ import { useI18n } from 'twake-i18n'
7
+
8
+ import { DumbFederatedFolderModal } from './DumbFederatedFolderModal'
9
+ import withLocales from '../../hoc/withLocales'
10
+ import { useSharingContext } from '../../hooks/useSharingContext'
11
+ import {
12
+ formatRecipients,
13
+ mergeAndDeduplicateRecipients,
14
+ moveRecipientToReadWrite,
15
+ moveRecipientToReadOnly,
16
+ RECIPIENT_INDEX_PREFIX
17
+ } from '../SharedDrive/helpers'
18
+
19
+ export const FederatedFolderModal = withLocales(
20
+ ({ onClose, document: existingDocument }) => {
21
+ const client = useClient()
22
+ const { t } = useI18n()
23
+ const {
24
+ share,
25
+ getSharingLink,
26
+ getFederatedShareLink,
27
+ getDocumentPermissions,
28
+ getOwner,
29
+ getSharingById,
30
+ getRecipients,
31
+ revoke
32
+ } = useSharingContext()
33
+ const { showAlert } = useAlert()
34
+
35
+ const [sharingLink, setSharingLink] = useState(null)
36
+
37
+ const documentPermissions = existingDocument
38
+ ? getDocumentPermissions(existingDocument._id)
39
+ : []
40
+
41
+ useEffect(() => {
42
+ const fetchSharingLink = async () => {
43
+ if (!existingDocument) return
44
+
45
+ if (existingDocument.driveId) {
46
+ const link = await getFederatedShareLink(existingDocument)
47
+ setSharingLink(link)
48
+ } else {
49
+ setSharingLink(getSharingLink(existingDocument._id))
50
+ }
51
+ }
52
+
53
+ fetchSharingLink()
54
+ }, [
55
+ existingDocument,
56
+ documentPermissions,
57
+ getFederatedShareLink,
58
+ getSharingLink,
59
+ getOwner,
60
+ getSharingById
61
+ ])
62
+
63
+ const [federatedRecipients, setFederatedRecipients] = useState({
64
+ recipients: [],
65
+ readOnlyRecipients: []
66
+ })
67
+ const [folderName] = useState(existingDocument?.name || '')
68
+
69
+ const onShare = params => {
70
+ setFederatedRecipients(prev => ({
71
+ recipients: mergeAndDeduplicateRecipients([
72
+ prev.recipients,
73
+ params.recipients || []
74
+ ]),
75
+ readOnlyRecipients: mergeAndDeduplicateRecipients([
76
+ prev.readOnlyRecipients,
77
+ params.readOnlyRecipients || []
78
+ ])
79
+ }))
80
+ }
81
+
82
+ const onSend = async () => {
83
+ try {
84
+ await share({
85
+ description: folderName,
86
+ document: existingDocument,
87
+ recipients: federatedRecipients.recipients,
88
+ readOnlyRecipients: federatedRecipients.readOnlyRecipients,
89
+ sharedDrive: true,
90
+ openSharing: false
91
+ })
92
+
93
+ showAlert({
94
+ message: t('FederatedFolder.successNotification'),
95
+ severity: 'success',
96
+ variant: 'filled'
97
+ })
98
+
99
+ onClose()
100
+ } catch (_err) {
101
+ showAlert({
102
+ message: t('FederatedFolder.errorNotification'),
103
+ severity: 'error',
104
+ variant: 'filled'
105
+ })
106
+ }
107
+ }
108
+
109
+ const onSetType = (index, newType) => {
110
+ const _id = index.split(RECIPIENT_INDEX_PREFIX)[1]
111
+
112
+ if (newType === 'two-way') {
113
+ setFederatedRecipients(prev => moveRecipientToReadWrite(prev, _id))
114
+ } else {
115
+ setFederatedRecipients(prev => moveRecipientToReadOnly(prev, _id))
116
+ }
117
+ }
118
+
119
+ const onRevoke = async (documentOrIndex, sharingId, memberIndex) => {
120
+ if (
121
+ typeof documentOrIndex === 'string' &&
122
+ documentOrIndex.startsWith(RECIPIENT_INDEX_PREFIX)
123
+ ) {
124
+ const _id = documentOrIndex.split(RECIPIENT_INDEX_PREFIX)[1]
125
+ setFederatedRecipients(prev => ({
126
+ recipients: prev.recipients.filter(r => r._id !== _id),
127
+ readOnlyRecipients: prev.readOnlyRecipients.filter(r => r._id !== _id)
128
+ }))
129
+ } else {
130
+ await revoke(documentOrIndex, sharingId, memberIndex)
131
+ }
132
+ }
133
+
134
+ const existingRecipients = existingDocument
135
+ ? getRecipients(existingDocument._id)
136
+ : []
137
+
138
+ const recipients = [
139
+ ...existingRecipients,
140
+ ...formatRecipients(federatedRecipients)
141
+ ]
142
+
143
+ const modalTitle = t('FederatedFolder.shareTitle', { name: folderName })
144
+
145
+ const isSharedDrive = Boolean(existingDocument?.driveId)
146
+
147
+ return (
148
+ <DumbFederatedFolderModal
149
+ title={modalTitle}
150
+ document={existingDocument}
151
+ createContact={contact => client.create('io.cozy.contacts', contact)}
152
+ recipients={recipients}
153
+ readOnlyRecipients={federatedRecipients.readOnlyRecipients}
154
+ currentRecipients={existingRecipients}
155
+ onRevoke={onRevoke}
156
+ onSetType={onSetType}
157
+ onSend={onSend}
158
+ onClose={onClose}
159
+ onShare={onShare}
160
+ sharingLink={sharingLink}
161
+ showShareByEmail={!isSharedDrive}
162
+ />
163
+ )
164
+ }
165
+ )
166
+
167
+ FederatedFolderModal.propTypes = {
168
+ onClose: PropTypes.func.isRequired,
169
+ document: PropTypes.object.isRequired
170
+ }
171
+
172
+ export default FederatedFolderModal
@@ -462,6 +462,77 @@ describe('FederatedFolderModal', function () {
462
462
  }
463
463
  }, _callee11);
464
464
  })));
465
+ it('should ask confirmation when close button is clicked with pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
466
+ var _setup12, getByRole, getByTestId, getByText;
467
+
468
+ return _regeneratorRuntime.wrap(function _callee12$(_context12) {
469
+ while (1) switch (_context12.prev = _context12.next) {
470
+ case 0:
471
+ usePendingRecipients.mockReturnValue({
472
+ pendingRecipients: [{
473
+ name: 'Alice',
474
+ email: 'alice@example.com'
475
+ }],
476
+ setPendingRecipients: jest.fn(),
477
+ selectedOption: 'readWrite',
478
+ setSelectedOption: jest.fn()
479
+ });
480
+ _setup12 = setup(), getByRole = _setup12.getByRole, getByTestId = _setup12.getByTestId, getByText = _setup12.getByText;
481
+ _context12.next = 4;
482
+ return waitFor(function () {
483
+ expect(getByTestId('btn-close')).toBeTruthy();
484
+ });
485
+
486
+ case 4:
487
+ fireEvent.click(getByTestId('btn-close'));
488
+ expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
489
+ expect(mockOnClose).not.toHaveBeenCalled();
490
+ fireEvent.click(getByRole('button', {
491
+ name: 'Cancel'
492
+ }));
493
+ expect(mockOnClose).not.toHaveBeenCalled();
494
+
495
+ case 9:
496
+ case "end":
497
+ return _context12.stop();
498
+ }
499
+ }, _callee12);
500
+ })));
501
+ it('should close after discard confirmation', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
502
+ var _setup13, getByRole, getByTestId, getByText;
503
+
504
+ return _regeneratorRuntime.wrap(function _callee13$(_context13) {
505
+ while (1) switch (_context13.prev = _context13.next) {
506
+ case 0:
507
+ usePendingRecipients.mockReturnValue({
508
+ pendingRecipients: [{
509
+ name: 'Alice',
510
+ email: 'alice@example.com'
511
+ }],
512
+ setPendingRecipients: jest.fn(),
513
+ selectedOption: 'readWrite',
514
+ setSelectedOption: jest.fn()
515
+ });
516
+ _setup13 = setup(), getByRole = _setup13.getByRole, getByTestId = _setup13.getByTestId, getByText = _setup13.getByText;
517
+ _context13.next = 4;
518
+ return waitFor(function () {
519
+ expect(getByTestId('btn-close')).toBeTruthy();
520
+ });
521
+
522
+ case 4:
523
+ fireEvent.click(getByTestId('btn-close'));
524
+ expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
525
+ fireEvent.click(getByRole('button', {
526
+ name: 'Discard'
527
+ }));
528
+ expect(mockOnClose).toHaveBeenCalledTimes(1);
529
+
530
+ case 8:
531
+ case "end":
532
+ return _context13.stop();
533
+ }
534
+ }, _callee13);
535
+ })));
465
536
  });
466
537
  describe('existing recipients', function () {
467
538
  var existingRecipients = [{
@@ -481,24 +552,24 @@ describe('FederatedFolderModal', function () {
481
552
  sharingId: 'abc',
482
553
  memberIndex: 1
483
554
  }];
484
- it('should display existing recipients from getRecipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
485
- var _setup12, getByTestId;
555
+ it('should display existing recipients from getRecipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
556
+ var _setup14, getByTestId;
486
557
 
487
- return _regeneratorRuntime.wrap(function _callee12$(_context12) {
488
- while (1) switch (_context12.prev = _context12.next) {
558
+ return _regeneratorRuntime.wrap(function _callee14$(_context14) {
559
+ while (1) switch (_context14.prev = _context14.next) {
489
560
  case 0:
490
561
  mockGetRecipients.mockReturnValue(existingRecipients);
491
- _setup12 = setup(), getByTestId = _setup12.getByTestId;
492
- _context12.next = 4;
562
+ _setup14 = setup(), getByTestId = _setup14.getByTestId;
563
+ _context14.next = 4;
493
564
  return waitFor(function () {
494
565
  expect(getByTestId('recipients-count').textContent).toBe('2');
495
566
  });
496
567
 
497
568
  case 4:
498
569
  case "end":
499
- return _context12.stop();
570
+ return _context14.stop();
500
571
  }
501
- }, _callee12);
572
+ }, _callee14);
502
573
  })));
503
574
  });
504
575
  });
@@ -6,6 +6,9 @@ var meta = {
6
6
  verifyRecipient: function verifyRecipient() {
7
7
  return true;
8
8
  },
9
+ document: {
10
+ _id: '123'
11
+ },
9
12
  fadeIn: false
10
13
  }
11
14
  };
@@ -1,7 +1,11 @@
1
1
  import LinkRecipientPermissions from './LinkRecipientPermissions';
2
2
  var meta = {
3
3
  component: LinkRecipientPermissions,
4
- args: {}
4
+ args: {
5
+ document: {
6
+ _id: '123'
7
+ }
8
+ }
5
9
  };
6
10
  export default meta;
7
11
  export var Default = {
@@ -0,0 +1,42 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React, { forwardRef } from 'react';
3
+ import ActionsMenuItem from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuItem';
4
+ import Icon from 'cozy-ui/transpiled/react/Icon';
5
+ import ForbiddenIcon from 'cozy-ui/transpiled/react/Icons/Forbidden';
6
+ import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon';
7
+ import ListItemText from 'cozy-ui/transpiled/react/ListItemText';
8
+ import Typography from 'cozy-ui/transpiled/react/Typography';
9
+
10
+ var revokeGroup = function revokeGroup(_ref) {
11
+ var t = _ref.t,
12
+ isOwner = _ref.isOwner,
13
+ isSharedDrive = _ref.isSharedDrive,
14
+ handleRevocation = _ref.handleRevocation;
15
+ var revokeType = isOwner ? 'revoke' : 'revokeSelf';
16
+ var title = t("RevokeGroupItem.".concat(revokeType, ".title"));
17
+ var desc = isOwner && isSharedDrive ? t("RevokeGroupItem.revoke.sharedDriveDesc") : t("RevokeGroupItem.".concat(revokeType, ".desc"));
18
+ var icon = ForbiddenIcon;
19
+ return {
20
+ name: 'revokeMember',
21
+ label: title,
22
+ icon: icon,
23
+ action: function action() {
24
+ handleRevocation();
25
+ },
26
+ Component: /*#__PURE__*/forwardRef(function RevokeMemberItem(props, ref) {
27
+ return /*#__PURE__*/React.createElement(ActionsMenuItem, _extends({}, props, {
28
+ ref: ref
29
+ }), /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(Icon, {
30
+ icon: icon,
31
+ color: "var(--errorColor)"
32
+ })), /*#__PURE__*/React.createElement(ListItemText, {
33
+ primary: /*#__PURE__*/React.createElement(Typography, {
34
+ color: "error"
35
+ }, title),
36
+ secondary: desc
37
+ }));
38
+ })
39
+ };
40
+ };
41
+
42
+ export { revokeGroup };
@@ -0,0 +1,42 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React, { forwardRef } from 'react';
3
+ import ActionsMenuItem from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuItem';
4
+ import Icon from 'cozy-ui/transpiled/react/Icon';
5
+ import TrashIcon from 'cozy-ui/transpiled/react/Icons/Trash';
6
+ import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon';
7
+ import ListItemText from 'cozy-ui/transpiled/react/ListItemText';
8
+ import Typography from 'cozy-ui/transpiled/react/Typography';
9
+
10
+ var revokeMember = function revokeMember(_ref) {
11
+ var t = _ref.t,
12
+ isOwner = _ref.isOwner,
13
+ isSharedDrive = _ref.isSharedDrive,
14
+ handleRevocation = _ref.handleRevocation;
15
+ var revokeType = isOwner ? 'revoke' : 'revokeSelf';
16
+ var title = t("RevokeMemberItem.".concat(revokeType, ".title"));
17
+ var desc = isOwner && isSharedDrive ? t("RevokeMemberItem.revoke.sharedDriveDesc") : t("RevokeMemberItem.".concat(revokeType, ".desc"));
18
+ var icon = TrashIcon;
19
+ return {
20
+ name: 'revokeMember',
21
+ label: title,
22
+ icon: icon,
23
+ action: function action() {
24
+ handleRevocation();
25
+ },
26
+ Component: /*#__PURE__*/forwardRef(function RevokeMemberItem(props, ref) {
27
+ return /*#__PURE__*/React.createElement(ActionsMenuItem, _extends({}, props, {
28
+ ref: ref
29
+ }), /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(Icon, {
30
+ icon: icon,
31
+ color: "var(--errorColor)"
32
+ })), /*#__PURE__*/React.createElement(ListItemText, {
33
+ primary: /*#__PURE__*/React.createElement(Typography, {
34
+ color: "error"
35
+ }, title),
36
+ secondary: desc
37
+ }));
38
+ })
39
+ };
40
+ };
41
+
42
+ export { revokeMember };
@@ -0,0 +1,41 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React, { forwardRef } from 'react';
3
+ import ActionsMenuItem from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuItem';
4
+ import Icon from 'cozy-ui/transpiled/react/Icon';
5
+ import TrashIcon from 'cozy-ui/transpiled/react/Icons/Trash';
6
+ import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon';
7
+ import ListItemText from 'cozy-ui/transpiled/react/ListItemText';
8
+ import Typography from 'cozy-ui/transpiled/react/Typography';
9
+
10
+ var makeComponent = function makeComponent(title) {
11
+ var Component = /*#__PURE__*/forwardRef(function (props, ref) {
12
+ return /*#__PURE__*/React.createElement(ActionsMenuItem, _extends({}, props, {
13
+ ref: ref
14
+ }), /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(Icon, {
15
+ icon: TrashIcon,
16
+ color: "var(--errorColor)"
17
+ })), /*#__PURE__*/React.createElement(ListItemText, {
18
+ primary: /*#__PURE__*/React.createElement(Typography, {
19
+ color: "error"
20
+ }, title)
21
+ }));
22
+ });
23
+ Component.displayName = 'RevokeSharedDriveMember';
24
+ return Component;
25
+ };
26
+
27
+ var revokeSharedDriveMember = function revokeSharedDriveMember(_ref) {
28
+ var t = _ref.t,
29
+ handleRevocation = _ref.handleRevocation;
30
+ var title = t("RevokeSharedDriveMemberItem.revoke.title");
31
+ return {
32
+ name: 'revokeSharedDriveMember',
33
+ label: title,
34
+ action: function action() {
35
+ handleRevocation();
36
+ },
37
+ Component: makeComponent(title)
38
+ };
39
+ };
40
+
41
+ export { revokeSharedDriveMember };
@@ -220,6 +220,7 @@ var ShareAutocomplete = function ShareAutocomplete(_ref) {
220
220
  renderInputComponent: renderInput,
221
221
  highlightFirstSuggestion: true,
222
222
  inputProps: {
223
+ autoFocus: true,
223
224
  onFocus: onAutosuggestFocus,
224
225
  onChange: onAutosuggestChange,
225
226
  onPaste: onAutosuggestPaste,
@@ -78,17 +78,25 @@ describe('ShareAutosuggest', function () {
78
78
  email: 'quentin.valmori@cozycloud.cc'
79
79
  });
80
80
  });
81
- it('should show loading only after user focus input', function () {
81
+ it('should show loading indicator when loading prop is true (autoFocus triggers onFocus on mount)', function () {
82
82
  var onPick = jest.fn();
83
83
  var onRemove = jest.fn();
84
84
  setup({
85
85
  onPick: onPick,
86
86
  onRemove: onRemove,
87
87
  loading: true
88
+ }); // autoFocus triggers onFocus on mount, which sets isLoadingDisplayed
89
+
90
+ expect(screen.getByText('loading')).toBeInTheDocument();
91
+ });
92
+ it('should not show loading indicator when loading prop is false', function () {
93
+ var onPick = jest.fn();
94
+ var onRemove = jest.fn();
95
+ setup({
96
+ onPick: onPick,
97
+ onRemove: onRemove,
98
+ loading: false
88
99
  });
89
- var inputNode = screen.getByPlaceholderText('myPlaceHolder');
90
100
  expect(screen.queryByText('loading')).toBeNull();
91
- fireEvent.focus(inputNode);
92
- expect(screen.getByText('loading')).toBeInTheDocument();
93
101
  });
94
102
  });
@@ -250,7 +250,7 @@ describe('ShareDialogCozyToCozy', function () {
250
250
  }
251
251
  }, _callee8);
252
252
  })));
253
- it('main Share button calls onClose when there are no pending chips', function () {
253
+ it('main Done button calls onClose when there are no pending chips', function () {
254
254
  var onClose = jest.fn();
255
255
  var onShare = jest.fn();
256
256
 
@@ -263,13 +263,60 @@ describe('ShareDialogCozyToCozy', function () {
263
263
  getByRole = _setup6.getByRole;
264
264
 
265
265
  fireEvent.click(getByRole('button', {
266
- name: 'Share'
266
+ name: 'Done'
267
267
  }));
268
268
  expect(onShare).not.toHaveBeenCalled();
269
269
  expect(onClose).toHaveBeenCalledTimes(1);
270
270
  });
271
- it('main Share button calls onShare with chips then closes', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() {
272
- var onClose, onShare, props, _setup7, getByPlaceholderText, getByRole;
271
+ it('close action closes dialog when there are no pending chips', function () {
272
+ var onClose = jest.fn();
273
+
274
+ var props = _objectSpread(_objectSpread({}, getMockProps()), {}, {
275
+ onClose: onClose
276
+ });
277
+
278
+ var _setup7 = setup(props),
279
+ getByRole = _setup7.getByRole;
280
+
281
+ fireEvent.click(getByRole('button', {
282
+ name: 'Close'
283
+ }));
284
+ expect(onClose).toHaveBeenCalledTimes(1);
285
+ });
286
+ it('close action closes dialog when there are pending chips', function () {
287
+ var onClose = jest.fn();
288
+
289
+ var props = _objectSpread(_objectSpread({}, getMockProps()), {}, {
290
+ onClose: onClose
291
+ });
292
+
293
+ var _setup8 = setup(props),
294
+ getByPlaceholderText = _setup8.getByPlaceholderText,
295
+ getByRole = _setup8.getByRole,
296
+ queryByText = _setup8.queryByText;
297
+
298
+ act(function () {
299
+ fireEvent.change(getByPlaceholderText('Enter the email address or name of the recipient'), {
300
+ target: {
301
+ value: 'new@cozycloud.cc'
302
+ }
303
+ });
304
+ });
305
+ act(function () {
306
+ fireEvent.keyPress(getByPlaceholderText('Enter the email address or name of the recipient'), {
307
+ key: 'Enter',
308
+ code: 'Enter',
309
+ charCode: 13
310
+ });
311
+ });
312
+ fireEvent.click(getByRole('button', {
313
+ name: 'Close'
314
+ }));
315
+ expect(queryByText("Discard the changes you haven't saved?")).toBe(null);
316
+ expect(onClose).toHaveBeenCalledTimes(1);
317
+ });
318
+ it('main Done button calls onShare with chips then closes', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() {
319
+ var onClose, onShare, props, _setup9, getByPlaceholderText, getByRole;
273
320
 
274
321
  return _regeneratorRuntime.wrap(function _callee10$(_context10) {
275
322
  while (1) switch (_context10.prev = _context10.next) {
@@ -280,7 +327,7 @@ describe('ShareDialogCozyToCozy', function () {
280
327
  onClose: onClose,
281
328
  onShare: onShare
282
329
  });
283
- _setup7 = setup(props), getByPlaceholderText = _setup7.getByPlaceholderText, getByRole = _setup7.getByRole;
330
+ _setup9 = setup(props), getByPlaceholderText = _setup9.getByPlaceholderText, getByRole = _setup9.getByRole;
284
331
  act(function () {
285
332
  fireEvent.change(getByPlaceholderText('Enter the email address or name of the recipient'), {
286
333
  target: {
@@ -301,7 +348,7 @@ describe('ShareDialogCozyToCozy', function () {
301
348
  while (1) switch (_context9.prev = _context9.next) {
302
349
  case 0:
303
350
  fireEvent.click(getByRole('button', {
304
- name: 'Share'
351
+ name: 'Done'
305
352
  }));
306
353
 
307
354
  case 1:
@@ -330,7 +377,7 @@ describe('ShareDialogCozyToCozy', function () {
330
377
  }, _callee10);
331
378
  })));
332
379
  it("should display confirmation of 'contact rejection' when user click on the 'reject' button", /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
333
- var _deferablePromise5, promise, resolve, rejectRecipient, props, _setup8, getByText, verifyButton, rejectButton, confirmRejectButton;
380
+ var _deferablePromise5, promise, resolve, rejectRecipient, props, _setup10, getByText, verifyButton, rejectButton, confirmRejectButton;
334
381
 
335
382
  return _regeneratorRuntime.wrap(function _callee15$(_context15) {
336
383
  while (1) switch (_context15.prev = _context15.next) {
@@ -348,7 +395,7 @@ describe('ShareDialogCozyToCozy', function () {
348
395
  recipientConfirmationDialogContent: CozyPassFingerprintDialogContent
349
396
  }
350
397
  });
351
- _setup8 = setup(props), getByText = _setup8.getByText;
398
+ _setup10 = setup(props), getByText = _setup10.getByText;
352
399
  _context15.next = 6;
353
400
  return act( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
354
401
  return _regeneratorRuntime.wrap(function _callee11$(_context11) {
@@ -0,0 +1,35 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+ import Button from 'cozy-ui/transpiled/react/Buttons';
4
+ import { ConfirmDialog } from 'cozy-ui/transpiled/react/CozyDialogs';
5
+ import { useI18n } from 'twake-i18n';
6
+
7
+ var ShareDiscardChangesModal = function ShareDiscardChangesModal(_ref) {
8
+ var onCancel = _ref.onCancel,
9
+ onConfirm = _ref.onConfirm;
10
+
11
+ var _useI18n = useI18n(),
12
+ t = _useI18n.t;
13
+
14
+ return /*#__PURE__*/React.createElement(ConfirmDialog, {
15
+ open: true,
16
+ title: t('ShareDiscardChangesModal.title'),
17
+ actions: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, {
18
+ variant: "secondary",
19
+ label: t('ShareDiscardChangesModal.cancel'),
20
+ className: "u-fz-small",
21
+ onClick: onCancel
22
+ }), /*#__PURE__*/React.createElement(Button, {
23
+ variant: "primary",
24
+ label: t('ShareDiscardChangesModal.discard'),
25
+ className: "u-fz-small",
26
+ onClick: onConfirm
27
+ }))
28
+ });
29
+ };
30
+
31
+ ShareDiscardChangesModal.propTypes = {
32
+ onCancel: PropTypes.func.isRequired,
33
+ onConfirm: PropTypes.func.isRequired
34
+ };
35
+ export { ShareDiscardChangesModal };
@@ -0,0 +1,43 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import omit from 'lodash/omit';
3
+ import React from 'react';
4
+ import { useLocation } from 'react-router';
5
+ import EditableSharingModal from './EditableSharingModal';
6
+ import { SharingDetailsModal } from './SharingDetailsModal';
7
+ import withLocales from '../../hoc/withLocales';
8
+ import { useSharingContext } from '../../hooks/useSharingContext';
9
+ export var ShareModal = withLocales(function (props) {
10
+ var _location$state;
11
+
12
+ var location = useLocation === null || useLocation === void 0 ? void 0 : useLocation();
13
+ var locationProps = location === null || location === void 0 || (_location$state = location.state) === null || _location$state === void 0 ? void 0 : _location$state.modalProps;
14
+ var document = (locationProps === null || locationProps === void 0 ? void 0 : locationProps.document) || props.document;
15
+ var rest = omit(locationProps || props, 'document');
16
+
17
+ var _useSharingContext = useSharingContext(),
18
+ byDocId = _useSharingContext.byDocId,
19
+ isOwner = _useSharingContext.isOwner,
20
+ canReshare = _useSharingContext.canReshare,
21
+ documentType = _useSharingContext.documentType,
22
+ getOwner = _useSharingContext.getOwner,
23
+ getSharingType = _useSharingContext.getSharingType,
24
+ getRecipients = _useSharingContext.getRecipients,
25
+ revokeSelf = _useSharingContext.revokeSelf;
26
+
27
+ var isEditable = !byDocId[document.id] || isOwner(document.id) || canReshare(document.id);
28
+
29
+ if (isEditable) {
30
+ return /*#__PURE__*/React.createElement(EditableSharingModal, _extends({
31
+ document: document
32
+ }, rest));
33
+ }
34
+
35
+ return /*#__PURE__*/React.createElement(SharingDetailsModal, _extends({
36
+ document: document,
37
+ documentType: documentType,
38
+ owner: getOwner(document.id),
39
+ sharingType: getSharingType(document.id),
40
+ recipients: getRecipients(document.id),
41
+ onRevoke: revokeSelf
42
+ }, rest));
43
+ });