cozy-sharing 31.0.2 → 31.2.0

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 (81) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +15 -16
  3. package/dist/components/FederatedFolder/FederatedFolderModal.js +78 -103
  4. package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +102 -292
  5. package/dist/components/Recipient/GroupRecipientPermissions.js +15 -11
  6. package/dist/components/Recipient/LinkRecipient.js +15 -11
  7. package/dist/components/Recipient/LinkRecipientLite.js +1 -1
  8. package/dist/components/Recipient/LinkRecipientPermissions.js +2 -1
  9. package/dist/components/Recipient/MemberRecipient.spec.js +8 -10
  10. package/dist/components/Recipient/MemberRecipientLite.js +1 -1
  11. package/dist/components/Recipient/MemberRecipientPermissions.js +16 -12
  12. package/dist/components/Recipient/MemberRecipientPermissions.spec.js +62 -50
  13. package/dist/components/Recipient/RecipientConfirm.js +0 -5
  14. package/dist/components/ShareAutosuggest.js +9 -4
  15. package/dist/components/ShareByEmail.js +29 -274
  16. package/dist/components/ShareByEmail.spec.js +52 -230
  17. package/dist/components/ShareByLink.js +1 -3
  18. package/dist/components/ShareDialogCozyToCozy.js +169 -35
  19. package/dist/components/ShareDialogCozyToCozy.spec.js +123 -36
  20. package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +13 -3
  21. package/dist/components/ShareModal/SharingDetailsModal.js +0 -5
  22. package/dist/components/ShareRecipientsInput.js +6 -3
  23. package/dist/components/SharedDrive/SharedDriveForm.js +17 -25
  24. package/dist/components/SharedDrive/SharedDriveModal.js +1 -1
  25. package/dist/components/SharedDrive/SharedDriveRecipientPermissions.js +15 -10
  26. package/dist/components/SharedDrive/useSharedDrive.js +33 -57
  27. package/dist/components/SharedDrive/useSharedDrive.spec.js +29 -93
  28. package/dist/components/SharedFolder/DumbBatchSharedFolderModal.js +15 -26
  29. package/dist/components/Sharetypeselect.js +85 -90
  30. package/dist/hooks/usePendingRecipients.js +20 -0
  31. package/dist/hooks/usePendingRecipients.spec.js +30 -0
  32. package/dist/styles/autosuggest.styl +8 -2
  33. package/dist/styles/share.styl +0 -39
  34. package/dist/stylesheet.css +8 -36
  35. package/locales/en.json +14 -44
  36. package/locales/fr.json +14 -44
  37. package/locales/ru.json +14 -44
  38. package/locales/vi.json +14 -42
  39. package/package.json +2 -2
  40. package/src/components/FederatedFolder/DumbFederatedFolderModal.jsx +14 -15
  41. package/src/components/FederatedFolder/FederatedFolderModal.jsx +50 -67
  42. package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +63 -186
  43. package/src/components/Recipient/GroupRecipientPermissions.jsx +15 -13
  44. package/src/components/Recipient/LinkRecipient.jsx +9 -9
  45. package/src/components/Recipient/LinkRecipientLite.jsx +1 -3
  46. package/src/components/Recipient/LinkRecipientPermissions.jsx +2 -1
  47. package/src/components/Recipient/MemberRecipient.spec.jsx +4 -6
  48. package/src/components/Recipient/MemberRecipientLite.jsx +2 -2
  49. package/src/components/Recipient/MemberRecipientPermissions.jsx +16 -21
  50. package/src/components/Recipient/MemberRecipientPermissions.spec.jsx +21 -23
  51. package/src/components/ShareAutosuggest.jsx +7 -2
  52. package/src/components/ShareByEmail.jsx +35 -186
  53. package/src/components/ShareByEmail.spec.jsx +22 -173
  54. package/src/components/ShareByLink.jsx +1 -7
  55. package/src/components/ShareDialogCozyToCozy.jsx +171 -38
  56. package/src/components/ShareDialogCozyToCozy.spec.jsx +67 -1
  57. package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +11 -1
  58. package/src/components/ShareRecipientsInput.jsx +5 -2
  59. package/src/components/SharedDrive/SharedDriveForm.jsx +16 -25
  60. package/src/components/SharedDrive/SharedDriveModal.jsx +1 -1
  61. package/src/components/SharedDrive/SharedDriveRecipientPermissions.jsx +15 -17
  62. package/src/components/SharedDrive/useSharedDrive.js +24 -50
  63. package/src/components/SharedDrive/useSharedDrive.spec.js +19 -67
  64. package/src/components/SharedFolder/DumbBatchSharedFolderModal.jsx +15 -29
  65. package/src/components/Sharetypeselect.jsx +75 -58
  66. package/src/hooks/usePendingRecipients.js +13 -0
  67. package/src/hooks/usePendingRecipients.spec.js +21 -0
  68. package/src/styles/autosuggest.styl +8 -2
  69. package/src/styles/share.styl +0 -39
  70. package/dist/components/Recipient/actions/revokeGroup.js +0 -42
  71. package/dist/components/Recipient/actions/revokeMember.js +0 -42
  72. package/dist/components/Recipient/actions/revokeSharedDriveMember.js +0 -41
  73. package/dist/components/SharedDrive/helpers.js +0 -102
  74. package/dist/components/SharedDrive/helpers.spec.js +0 -227
  75. package/dist/components/Sharesubmit.js +0 -28
  76. package/src/components/Recipient/actions/revokeGroup.js +0 -42
  77. package/src/components/Recipient/actions/revokeMember.js +0 -42
  78. package/src/components/Recipient/actions/revokeSharedDriveMember.js +0 -41
  79. package/src/components/SharedDrive/helpers.js +0 -108
  80. package/src/components/SharedDrive/helpers.spec.js +0 -177
  81. package/src/components/Sharesubmit.jsx +0 -31
@@ -1,138 +1,44 @@
1
- import { render, fireEvent, waitFor, screen } from '@testing-library/react'
1
+ import { fireEvent, render, screen } from '@testing-library/react'
2
2
  import React from 'react'
3
3
  import { act } from 'react-dom/test-utils'
4
4
 
5
- import flag from 'cozy-flags'
6
-
7
5
  import { ShareByEmail } from './ShareByEmail'
8
6
  import AppLike from '../../test/AppLike'
9
- import { getOrCreateFromArray } from '../helpers/contacts'
10
-
11
- jest.mock('../helpers/contacts', () => ({
12
- // eslint-disable-next-line no-unused-vars
13
- getOrCreateFromArray: jest.fn((client, recipients, _createContact) => {
14
- // Return the recipients as-is by default (simulating contact creation/lookup)
15
- return Promise.resolve(recipients)
16
- })
17
- }))
18
-
19
- jest.mock('cozy-flags', () =>
20
- jest.fn(flagName => {
21
- if (flagName === 'drive.federated-shared-folder.enabled') {
22
- return false
23
- }
24
- return null
25
- })
26
- )
27
-
28
- describe('ShareByEmailComponent', () => {
29
- const defaultDocument = {
30
- id: 'doc_id',
31
- name: 'documentName'
32
- }
33
-
34
- const onShare = jest.fn()
35
7
 
36
- const setup = ({
37
- sharingDesc = 'test',
38
- document = defaultDocument,
39
- currentRecipients = [],
40
- sharedDrive = false
41
- } = {}) => {
8
+ describe('ShareByEmail (controlled)', () => {
9
+ const setup = (overrides = {}) => {
10
+ const onPendingRecipientsChange = jest.fn()
11
+ const onSelectedOptionChange = jest.fn()
42
12
  const props = {
13
+ document: { id: 'doc_id', name: 'documentName' },
43
14
  documentType: 'Files',
44
- onShare,
45
- document,
46
- sharingDesc,
47
- currentRecipients,
48
- createContact: jest.fn(),
49
- sharedDrive
15
+ currentRecipients: [],
16
+ pendingRecipients: [],
17
+ onPendingRecipientsChange,
18
+ selectedOption: 'readWrite',
19
+ onSelectedOptionChange,
20
+ ...overrides
50
21
  }
51
- return render(
22
+ const utils = render(
52
23
  <AppLike>
53
24
  <ShareByEmail {...props} />
54
25
  </AppLike>
55
26
  )
27
+ return { ...utils, onPendingRecipientsChange, onSelectedOptionChange }
56
28
  }
57
29
 
58
30
  beforeEach(() => {
59
31
  jest.clearAllMocks()
60
- // Reset flag mock to default (non-federated mode)
61
- flag.mockImplementation(flagName => {
62
- if (flagName === 'drive.federated-shared-folder.enabled') {
63
- return false
64
- }
65
- return null
66
- })
67
32
  })
68
33
 
69
- it('shoud call share if submited', async () => {
70
- const sharingDesc = 'test'
71
-
72
- setup({ sharingDesc })
34
+ it('calls onPendingRecipientsChange when an email is picked', () => {
35
+ const { onPendingRecipientsChange } = setup()
73
36
 
74
37
  act(() => {
75
38
  fireEvent.change(screen.getByPlaceholderText('Add contacts'), {
76
39
  target: { value: 'quentin@cozycloud.cc' }
77
40
  })
78
41
  })
79
-
80
- act(() => {
81
- fireEvent.keyPress(screen.getByPlaceholderText('Add contacts'), {
82
- key: 'Enter',
83
- code: 'Enter',
84
- charCode: 13
85
- })
86
- })
87
-
88
- act(() => {
89
- fireEvent.click(screen.getByRole('button', { name: 'Send' }))
90
- })
91
-
92
- await waitFor(() => {
93
- expect(onShare).toHaveBeenCalledWith({
94
- description: sharingDesc,
95
- document: defaultDocument,
96
- openSharing: true,
97
- readOnlyRecipients: [],
98
- recipients: [{ email: 'quentin@cozycloud.cc' }],
99
- sharedDrive: false
100
- })
101
- })
102
- })
103
-
104
- it('should alert user when it has reached the recipients limit for a document', async () => {
105
- flag.mockImplementation(flagName => {
106
- if (flagName === 'sharing.recipients-limit') {
107
- return 2
108
- }
109
- if (flagName === 'drive.federated-shared-folder.enabled') {
110
- return false
111
- }
112
- return null
113
- })
114
-
115
- setup({
116
- currentRecipients: [
117
- {
118
- email: 'alice@gmail.com',
119
- status: 'pending',
120
- type: 'two-way'
121
- },
122
- {
123
- email: 'bob@gmail.com',
124
- status: 'pending',
125
- type: 'two-way'
126
- }
127
- ]
128
- })
129
-
130
- act(() => {
131
- fireEvent.change(screen.getByPlaceholderText('Add contacts'), {
132
- target: { value: 'john@gmail.com' }
133
- })
134
- })
135
-
136
42
  act(() => {
137
43
  fireEvent.keyPress(screen.getByPlaceholderText('Add contacts'), {
138
44
  key: 'Enter',
@@ -141,70 +47,13 @@ describe('ShareByEmailComponent', () => {
141
47
  })
142
48
  })
143
49
 
144
- act(() => {
145
- fireEvent.click(screen.getByRole('button', { name: 'Send' }))
146
- })
147
-
148
- act(() => {
149
- fireEvent.click(screen.getByRole('button', { name: 'I understand' }))
150
- })
151
-
152
- await waitFor(() => {
153
- expect(onShare).not.toHaveBeenCalled()
154
- })
50
+ expect(onPendingRecipientsChange).toHaveBeenCalledWith([
51
+ { email: 'quentin@cozycloud.cc' }
52
+ ])
155
53
  })
156
54
 
157
- describe('federated mode', () => {
158
- beforeEach(() => {
159
- flag.mockImplementation(flagName => {
160
- if (flagName === 'drive.federated-shared-folder.enabled') {
161
- return true
162
- }
163
- return null
164
- })
165
- getOrCreateFromArray.mockResolvedValue([
166
- { _id: 'contact1', email: 'quentin@cozycloud.cc' }
167
- ])
168
- })
169
-
170
- it('should directly share when selecting a recipient in federated mode', async () => {
171
- const sharingDesc = 'test'
172
- const createContact = jest.fn()
173
-
174
- setup({ sharingDesc, createContact })
175
-
176
- act(() => {
177
- fireEvent.change(screen.getByPlaceholderText('Add contacts'), {
178
- target: { value: 'quentin@cozycloud.cc' }
179
- })
180
- })
181
-
182
- act(() => {
183
- fireEvent.keyPress(screen.getByPlaceholderText('Add contacts'), {
184
- key: 'Enter',
185
- code: 'Enter',
186
- charCode: 13
187
- })
188
- })
189
-
190
- await waitFor(() => {
191
- expect(getOrCreateFromArray).toHaveBeenCalledWith(
192
- expect.any(Object),
193
- [{ email: 'quentin@cozycloud.cc' }],
194
- expect.any(Function)
195
- )
196
- })
197
-
198
- await waitFor(() => {
199
- expect(onShare).toHaveBeenCalledWith({
200
- document: defaultDocument,
201
- recipients: [{ _id: 'contact1', email: 'quentin@cozycloud.cc' }],
202
- readOnlyRecipients: [],
203
- description: sharingDesc,
204
- openSharing: false,
205
- sharedDrive: true
206
- })
207
- })
208
- })
55
+ it('always shows the read/write dropdown trigger inside the input', () => {
56
+ setup()
57
+ expect(screen.getByRole('button', { name: 'Editor' })).not.toBeNull()
209
58
  })
210
59
  })
@@ -2,7 +2,6 @@ import PropTypes from 'prop-types'
2
2
  import React, { useReducer, useState } from 'react'
3
3
 
4
4
  import { useClient } from 'cozy-client'
5
- import flag from 'cozy-flags'
6
5
  import Button from 'cozy-ui/transpiled/react/Buttons'
7
6
  import Icon from 'cozy-ui/transpiled/react/Icon'
8
7
  import CopyIcon from 'cozy-ui/transpiled/react/Icons/Copy'
@@ -36,7 +35,6 @@ const ShareByLink = ({
36
35
  const showGenerateButton = showGenerateLinkButton && !link
37
36
  const showCopyAndSendButtons = !showGenerateButton && isMobile && canShare
38
37
  const showOnlyCopyButton = !showGenerateButton && (!isMobile || !canShare)
39
- const isFederated = flag('drive.federated-shared-folder.enabled')
40
38
 
41
39
  const [isEditDialogOpen, toggleEditDialogOpen] = useReducer(
42
40
  state => !state,
@@ -139,11 +137,7 @@ const ShareByLink = ({
139
137
  }
140
138
 
141
139
  return (
142
- <div
143
- className={
144
- isFederated ? 'u-w-100' : 'u-w-100 u-flex u-flex-justify-center'
145
- }
146
- >
140
+ <div className="u-w-100">
147
141
  {showCopyAndSendButtons && (
148
142
  <>
149
143
  <Button
@@ -1,7 +1,11 @@
1
1
  import cx from 'classnames'
2
- import React, { useMemo } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import React, { useCallback, useMemo, useState } from 'react'
3
4
 
5
+ import { useClient } from 'cozy-client'
6
+ import Button from 'cozy-ui/transpiled/react/Buttons'
4
7
  import Typography from 'cozy-ui/transpiled/react/Typography'
8
+ import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
5
9
  import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
6
10
  import { useI18n } from 'twake-i18n'
7
11
 
@@ -9,31 +13,34 @@ import AntivirusAlert from './AntivirusAlert'
9
13
  import { default as DumbShareByEmail } from './ShareByEmail'
10
14
  import { default as DumbShareByLink } from './ShareByLink'
11
15
  import ShareDialogTwoStepsConfirmationContainer from './ShareDialogTwoStepsConfirmationContainer'
16
+ import { ShareRecipientsLimitModal } from './ShareRecipientsLimitModal'
12
17
  import WhoHasAccess from './WhoHasAccess'
18
+ import { getOrCreateFromArray } from '../helpers/contacts'
19
+ import { hasReachRecipientsLimit } from '../helpers/recipients'
20
+ import { getErrorMessage, getSuccessMessage } from '../helpers/share'
21
+ import { usePendingRecipients } from '../hooks/usePendingRecipients'
13
22
  import styles from '../styles/share.styl'
14
23
 
15
- /**
16
- * Displays the sharing interface that can be used when ShareDialogCozyToCozy is in sharing state
17
- */
18
24
  const SharingContent = ({
19
- createContact,
20
25
  document,
21
26
  documentType,
22
27
  hasSharedParent,
23
28
  isOwner,
24
29
  onRevoke,
25
30
  onRevokeSelf,
26
- onShare,
27
31
  recipients,
28
32
  sharing,
29
- sharingDesc,
30
33
  showShareByEmail,
31
34
  showShareOnlyByLink,
32
35
  showWhoHasAccess,
33
36
  recipientsToBeConfirmed,
34
37
  verifyRecipient,
35
38
  link,
36
- permissions
39
+ permissions,
40
+ pendingRecipients,
41
+ onPendingRecipientsChange,
42
+ selectedOption,
43
+ onSelectedOptionChange
37
44
  }) => {
38
45
  const { t } = useI18n()
39
46
  const { isMobile } = useBreakpoints()
@@ -64,13 +71,14 @@ const SharingContent = ({
64
71
  </Typography>
65
72
  {showShareByEmail && (
66
73
  <DumbShareByEmail
67
- createContact={createContact}
68
74
  currentRecipients={recipients}
69
75
  document={document}
70
76
  documentType={documentType}
71
- onShare={onShare}
72
77
  sharing={sharing}
73
- sharingDesc={sharingDesc}
78
+ pendingRecipients={pendingRecipients}
79
+ onPendingRecipientsChange={onPendingRecipientsChange}
80
+ selectedOption={selectedOption}
81
+ onSelectedOptionChange={onSelectedOptionChange}
74
82
  />
75
83
  )}
76
84
  </div>
@@ -92,55 +100,180 @@ const SharingContent = ({
92
100
  )
93
101
  }
94
102
 
95
- /**
96
- * Displays the dialog's title that can be used when ShareDialogCozyToCozy is in sharing state
97
- */
98
103
  const SharingTitleFunction = ({ documentType, document }) =>
99
104
  function SharingTitle() {
100
105
  const { t } = useI18n()
101
-
102
106
  const title = t(`${documentType}.share.title`, {
103
107
  name: document.name || document.attributes?.name
104
108
  })
105
-
106
109
  return title
107
110
  }
108
111
 
109
- /**
110
- * Displays a sharing dialog that allows to share a document between multiple Cozy users
111
- */
112
112
  const ShareDialogCozyToCozy = ({
113
113
  showShareByLink,
114
114
  showGenerateLinkButton,
115
115
  autoOpenShareRestriction,
116
116
  documentType,
117
117
  document,
118
+ onShare,
119
+ recipients,
120
+ sharingDesc,
121
+ createContact,
122
+ onClose,
118
123
  ...props
119
124
  }) => {
125
+ const { t } = useI18n()
126
+ const client = useClient()
127
+ const { showAlert } = useAlert()
128
+
129
+ const {
130
+ pendingRecipients,
131
+ setPendingRecipients,
132
+ selectedOption,
133
+ setSelectedOption
134
+ } = usePendingRecipients()
135
+
136
+ const [submitting, setSubmitting] = useState(false)
137
+ const [showRecipientsLimit, setShowRecipientsLimit] = useState(false)
138
+
139
+ const handleShare = useCallback(async () => {
140
+ if (pendingRecipients.length === 0) {
141
+ onClose()
142
+ return
143
+ }
144
+
145
+ if (hasReachRecipientsLimit(recipients, pendingRecipients)) {
146
+ setShowRecipientsLimit(true)
147
+ return
148
+ }
149
+
150
+ setSubmitting(true)
151
+ try {
152
+ const contacts = await getOrCreateFromArray(
153
+ client,
154
+ pendingRecipients,
155
+ createContact
156
+ )
157
+ const readWriteRecipients = selectedOption === 'readOnly' ? [] : contacts
158
+ const readOnlyRecipients = selectedOption === 'readOnly' ? contacts : []
159
+
160
+ await onShare({
161
+ document,
162
+ recipients: readWriteRecipients,
163
+ readOnlyRecipients,
164
+ description: sharingDesc,
165
+ openSharing: readWriteRecipients.length > 0
166
+ })
167
+
168
+ showAlert({
169
+ message: t(...getSuccessMessage(recipients, contacts, documentType)),
170
+ severity: 'success',
171
+ variant: 'filled'
172
+ })
173
+ onClose()
174
+ } catch (err) {
175
+ showAlert({
176
+ message: t(
177
+ ...getErrorMessage({
178
+ t,
179
+ err,
180
+ documentType,
181
+ recipients: pendingRecipients,
182
+ selectedOption
183
+ })
184
+ ),
185
+ severity: 'error',
186
+ variant: 'filled'
187
+ })
188
+ } finally {
189
+ setSubmitting(false)
190
+ }
191
+ }, [
192
+ pendingRecipients,
193
+ selectedOption,
194
+ client,
195
+ showAlert,
196
+ t,
197
+ recipients,
198
+ onShare,
199
+ onClose,
200
+ createContact,
201
+ document,
202
+ documentType,
203
+ sharingDesc
204
+ ])
205
+
120
206
  const dialogActionsOnShare = useMemo(() => {
121
- if (!showShareByLink) return null
122
- const BoundShareByLink = dialogActionProps => (
123
- <DumbShareByLink
124
- {...dialogActionProps}
125
- showGenerateLinkButton={showGenerateLinkButton}
126
- autoOpenShareRestriction={autoOpenShareRestriction}
127
- />
207
+ const Actions = dialogActionProps => (
208
+ <>
209
+ {showShareByLink && (
210
+ <DumbShareByLink
211
+ {...dialogActionProps}
212
+ showGenerateLinkButton={showGenerateLinkButton}
213
+ autoOpenShareRestriction={autoOpenShareRestriction}
214
+ />
215
+ )}
216
+ <Button
217
+ variant="primary"
218
+ label={t(`${documentType}.share.shareByEmail.send`)}
219
+ busy={submitting}
220
+ onClick={handleShare}
221
+ />
222
+ </>
128
223
  )
129
- BoundShareByLink.displayName = 'BoundShareByLink'
130
- return BoundShareByLink
131
- }, [showShareByLink, showGenerateLinkButton, autoOpenShareRestriction])
224
+ Actions.displayName = 'ShareDialogCozyToCozyActions'
225
+ return Actions
226
+ }, [
227
+ handleShare,
228
+ submitting,
229
+ t,
230
+ documentType,
231
+ showShareByLink,
232
+ showGenerateLinkButton,
233
+ autoOpenShareRestriction
234
+ ])
132
235
 
133
236
  return (
134
- <ShareDialogTwoStepsConfirmationContainer
135
- {...props}
136
- documentType={documentType}
137
- document={document}
138
- dialogContentOnShare={SharingContent}
139
- dialogActionsOnShare={dialogActionsOnShare}
140
- dialogTitleOnShare={SharingTitleFunction({ documentType, document })}
141
- disableGutters
142
- />
237
+ <>
238
+ <ShareDialogTwoStepsConfirmationContainer
239
+ {...props}
240
+ documentType={documentType}
241
+ document={document}
242
+ recipients={recipients}
243
+ sharingDesc={sharingDesc}
244
+ createContact={createContact}
245
+ onShare={onShare}
246
+ onClose={onClose}
247
+ dialogContentOnShare={SharingContent}
248
+ dialogActionsOnShare={dialogActionsOnShare}
249
+ dialogTitleOnShare={SharingTitleFunction({ documentType, document })}
250
+ disableGutters
251
+ pendingRecipients={pendingRecipients}
252
+ onPendingRecipientsChange={setPendingRecipients}
253
+ selectedOption={selectedOption}
254
+ onSelectedOptionChange={setSelectedOption}
255
+ />
256
+ {showRecipientsLimit && (
257
+ <ShareRecipientsLimitModal
258
+ documentName={document?.name}
259
+ onConfirm={() => setShowRecipientsLimit(false)}
260
+ />
261
+ )}
262
+ </>
143
263
  )
144
264
  }
145
265
 
266
+ ShareDialogCozyToCozy.propTypes = {
267
+ showShareByLink: PropTypes.bool,
268
+ showGenerateLinkButton: PropTypes.bool,
269
+ autoOpenShareRestriction: PropTypes.bool,
270
+ documentType: PropTypes.string.isRequired,
271
+ document: PropTypes.object.isRequired,
272
+ onShare: PropTypes.func.isRequired,
273
+ recipients: PropTypes.array.isRequired,
274
+ sharingDesc: PropTypes.string,
275
+ createContact: PropTypes.func.isRequired,
276
+ onClose: PropTypes.func.isRequired
277
+ }
278
+
146
279
  export default ShareDialogCozyToCozy
@@ -20,6 +20,12 @@ jest.mock('../hooks/useSharingContext', () => ({
20
20
  })
21
21
  }))
22
22
 
23
+ jest.mock('../helpers/contacts', () => ({
24
+ getOrCreateFromArray: jest.fn((client, recipients) =>
25
+ Promise.resolve(recipients)
26
+ )
27
+ }))
28
+
23
29
  describe('ShareDialogCozyToCozy', () => {
24
30
  const client = createMockClient({})
25
31
  client.options = {
@@ -42,7 +48,7 @@ describe('ShareDialogCozyToCozy', () => {
42
48
  const { getByText } = setup(props)
43
49
 
44
50
  expect(getByText('You')).toBeTruthy()
45
- expect(getByText('owner')).toBeTruthy()
51
+ expect(getByText('Owner')).toBeTruthy()
46
52
  })
47
53
 
48
54
  it('should show loading while calling getRecipientsToBeConfirmed', async () => {
@@ -163,6 +169,65 @@ describe('ShareDialogCozyToCozy', () => {
163
169
  ).not.toBeInTheDocument()
164
170
  })
165
171
 
172
+ it('main Share button calls onClose when there are no pending chips', () => {
173
+ const onClose = jest.fn()
174
+ const onShare = jest.fn()
175
+ const props = { ...getMockProps(), onClose, onShare }
176
+
177
+ const { getByRole } = setup(props)
178
+
179
+ fireEvent.click(getByRole('button', { name: 'Share' }))
180
+
181
+ expect(onShare).not.toHaveBeenCalled()
182
+ expect(onClose).toHaveBeenCalledTimes(1)
183
+ })
184
+
185
+ it('main Share button calls onShare with chips then closes', async () => {
186
+ const onClose = jest.fn()
187
+ const onShare = jest.fn().mockResolvedValue(undefined)
188
+ const props = { ...getMockProps(), onClose, onShare }
189
+
190
+ const { getByPlaceholderText, getByRole } = setup(props)
191
+
192
+ act(() => {
193
+ fireEvent.change(
194
+ getByPlaceholderText(
195
+ 'Enter the email address or name of the recipient'
196
+ ),
197
+ {
198
+ target: { value: 'new@cozycloud.cc' }
199
+ }
200
+ )
201
+ })
202
+ act(() => {
203
+ fireEvent.keyPress(
204
+ getByPlaceholderText(
205
+ 'Enter the email address or name of the recipient'
206
+ ),
207
+ {
208
+ key: 'Enter',
209
+ code: 'Enter',
210
+ charCode: 13
211
+ }
212
+ )
213
+ })
214
+
215
+ await act(async () => {
216
+ fireEvent.click(getByRole('button', { name: 'Share' }))
217
+ })
218
+
219
+ expect(onShare).toHaveBeenCalledWith(
220
+ expect.objectContaining({
221
+ document: props.document,
222
+ recipients: [{ email: 'new@cozycloud.cc' }],
223
+ readOnlyRecipients: [],
224
+ description: props.sharingDesc,
225
+ openSharing: true
226
+ })
227
+ )
228
+ expect(onClose).toHaveBeenCalledTimes(1)
229
+ })
230
+
166
231
  it(`should display confirmation of 'contact rejection' when user click on the 'reject' button`, async () => {
167
232
  const { promise, resolve } = deferablePromise()
168
233
 
@@ -372,6 +437,7 @@ const getMockProps = () => {
372
437
  showWhoHasAccess: true,
373
438
  onRevoke: jest.fn(),
374
439
  onShare: jest.fn(),
440
+ onClose: jest.fn(),
375
441
  createContact: jest.fn()
376
442
  }
377
443
  }
@@ -108,7 +108,11 @@ const ShareDialogTwoStepsConfirmationContainer = ({
108
108
  twoStepsConfirmationMethods = {},
109
109
  dialogContentOnShare: DialogContentOnShare,
110
110
  dialogActionsOnShare: DialogActionsOnShare,
111
- dialogTitleOnShare: DialogTitleOnShare
111
+ dialogTitleOnShare: DialogTitleOnShare,
112
+ pendingRecipients,
113
+ onPendingRecipientsChange,
114
+ selectedOption,
115
+ onSelectedOptionChange
112
116
  }) => {
113
117
  const { t } = useI18n()
114
118
 
@@ -222,6 +226,10 @@ const ShareDialogTwoStepsConfirmationContainer = ({
222
226
  permissions={permissions}
223
227
  onUpdateShareLinkPermissions={onUpdateShareLinkPermissions}
224
228
  onRevokeLink={onRevokeLink}
229
+ pendingRecipients={pendingRecipients}
230
+ onPendingRecipientsChange={onPendingRecipientsChange}
231
+ selectedOption={selectedOption}
232
+ onSelectedOptionChange={onSelectedOptionChange}
225
233
  />
226
234
  )
227
235
 
@@ -230,6 +238,8 @@ const ShareDialogTwoStepsConfirmationContainer = ({
230
238
  link={link}
231
239
  document={document}
232
240
  documentType={documentType}
241
+ pendingRecipients={pendingRecipients}
242
+ selectedOption={selectedOption}
233
243
  />
234
244
  ) : null
235
245
  } else if (status === 'confirmingRecipient') {
@@ -23,7 +23,8 @@ const ShareRecipientsInput = ({
23
23
  placeholder,
24
24
  onPick,
25
25
  onRemove,
26
- disabled
26
+ disabled,
27
+ endAdornment
27
28
  }) => {
28
29
  const reachableContactsQuery = buildReachableContactsQuery()
29
30
  const reachableContactsResult = useQueryAll(
@@ -109,6 +110,7 @@ const ShareRecipientsInput = ({
109
110
  onPick={onPick}
110
111
  onRemove={onRemove}
111
112
  placeholder={placeholder}
113
+ endAdornment={endAdornment}
112
114
  />
113
115
  )
114
116
  }
@@ -119,7 +121,8 @@ ShareRecipientsInput.propTypes = {
119
121
  placeholder: PropTypes.string,
120
122
  onPick: PropTypes.func.isRequired,
121
123
  onRemove: PropTypes.func.isRequired,
122
- disabled: PropTypes.bool
124
+ disabled: PropTypes.bool,
125
+ endAdornment: PropTypes.node
123
126
  }
124
127
 
125
128
  ShareRecipientsInput.defaultProps = {