cozy-sharing 31.1.0 → 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.
- package/CHANGELOG.md +16 -0
- package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +15 -16
- package/dist/components/FederatedFolder/FederatedFolderModal.js +78 -103
- package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +102 -292
- package/dist/components/Recipient/RecipientConfirm.js +0 -5
- package/dist/components/ShareAutosuggest.js +9 -4
- package/dist/components/ShareByEmail.js +29 -274
- package/dist/components/ShareByEmail.spec.js +52 -230
- package/dist/components/ShareByLink.js +1 -3
- package/dist/components/ShareDialogCozyToCozy.js +169 -35
- package/dist/components/ShareDialogCozyToCozy.spec.js +122 -35
- package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +13 -3
- package/dist/components/ShareModal/SharingDetailsModal.js +0 -5
- package/dist/components/ShareRecipientsInput.js +6 -3
- package/dist/components/SharedDrive/SharedDriveForm.js +11 -23
- package/dist/components/SharedDrive/SharedDriveModal.js +1 -1
- package/dist/components/SharedDrive/useSharedDrive.js +33 -57
- package/dist/components/SharedDrive/useSharedDrive.spec.js +29 -93
- package/dist/components/SharedFolder/DumbBatchSharedFolderModal.js +15 -26
- package/dist/components/Sharetypeselect.js +85 -90
- package/dist/hooks/usePendingRecipients.js +20 -0
- package/dist/hooks/usePendingRecipients.spec.js +30 -0
- package/dist/styles/autosuggest.styl +8 -2
- package/dist/styles/share.styl +0 -39
- package/dist/stylesheet.css +8 -36
- package/locales/en.json +5 -9
- package/locales/fr.json +5 -9
- package/locales/ru.json +5 -9
- package/locales/vi.json +5 -9
- package/package.json +2 -2
- package/src/components/FederatedFolder/DumbFederatedFolderModal.jsx +14 -15
- package/src/components/FederatedFolder/FederatedFolderModal.jsx +50 -67
- package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +63 -186
- package/src/components/ShareAutosuggest.jsx +7 -2
- package/src/components/ShareByEmail.jsx +35 -186
- package/src/components/ShareByEmail.spec.jsx +22 -173
- package/src/components/ShareByLink.jsx +1 -7
- package/src/components/ShareDialogCozyToCozy.jsx +171 -38
- package/src/components/ShareDialogCozyToCozy.spec.jsx +66 -0
- package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +11 -1
- package/src/components/ShareRecipientsInput.jsx +5 -2
- package/src/components/SharedDrive/SharedDriveForm.jsx +10 -23
- package/src/components/SharedDrive/SharedDriveModal.jsx +1 -1
- package/src/components/SharedDrive/useSharedDrive.js +24 -50
- package/src/components/SharedDrive/useSharedDrive.spec.js +19 -67
- package/src/components/SharedFolder/DumbBatchSharedFolderModal.jsx +15 -29
- package/src/components/Sharetypeselect.jsx +75 -58
- package/src/hooks/usePendingRecipients.js +13 -0
- package/src/hooks/usePendingRecipients.spec.js +21 -0
- package/src/styles/autosuggest.styl +8 -2
- package/src/styles/share.styl +0 -39
- package/dist/components/SharedDrive/helpers.js +0 -102
- package/dist/components/SharedDrive/helpers.spec.js +0 -227
- package/dist/components/Sharesubmit.js +0 -28
- package/src/components/SharedDrive/helpers.js +0 -108
- package/src/components/SharedDrive/helpers.spec.js +0 -177
- package/src/components/Sharesubmit.jsx +0 -31
|
@@ -8,20 +8,18 @@ import { useI18n } from 'twake-i18n'
|
|
|
8
8
|
|
|
9
9
|
import withLocales from '../../hoc/withLocales'
|
|
10
10
|
import { default as DumbShareByEmail } from '../ShareByEmail'
|
|
11
|
-
import WhoHasAccess from '../WhoHasAccess'
|
|
12
11
|
import { useSharedDrive } from './useSharedDrive'
|
|
13
12
|
|
|
14
13
|
export const SharedDriveForm = withLocales(({ onSuccess, onCancel }) => {
|
|
15
14
|
const { t } = useI18n()
|
|
16
15
|
const {
|
|
17
16
|
sharedDriveName,
|
|
18
|
-
recipients,
|
|
19
17
|
handleSharedDriveNameChange,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
pendingRecipients,
|
|
19
|
+
setPendingRecipients,
|
|
20
|
+
selectedOption,
|
|
21
|
+
setSelectedOption,
|
|
22
|
+
onCreate
|
|
25
23
|
} = useSharedDrive({ onSuccess })
|
|
26
24
|
|
|
27
25
|
return (
|
|
@@ -40,25 +38,14 @@ export const SharedDriveForm = withLocales(({ onSuccess, onCancel }) => {
|
|
|
40
38
|
{t('SharedDrive.sharedDriveModal.addPeople')}
|
|
41
39
|
</Typography>
|
|
42
40
|
<DumbShareByEmail
|
|
43
|
-
|
|
44
|
-
currentRecipients={recipients}
|
|
41
|
+
currentRecipients={[]}
|
|
45
42
|
documentType="Files"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
sharingDesc=""
|
|
43
|
+
pendingRecipients={pendingRecipients}
|
|
44
|
+
onPendingRecipientsChange={setPendingRecipients}
|
|
45
|
+
selectedOption={selectedOption}
|
|
46
|
+
onSelectedOptionChange={setSelectedOption}
|
|
51
47
|
/>
|
|
52
48
|
</div>
|
|
53
|
-
<WhoHasAccess
|
|
54
|
-
isOwner
|
|
55
|
-
isSharedDrive
|
|
56
|
-
recipients={recipients}
|
|
57
|
-
documentType="Files"
|
|
58
|
-
className="u-w-100"
|
|
59
|
-
onRevoke={onRevoke}
|
|
60
|
-
onSetType={onSetType}
|
|
61
|
-
/>
|
|
62
49
|
<div className="u-flex u-ph-2 u-pv-1">
|
|
63
50
|
<Button
|
|
64
51
|
variant="text"
|
|
@@ -4,8 +4,8 @@ import React from 'react'
|
|
|
4
4
|
import { FixedDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
|
|
5
5
|
import { useI18n } from 'twake-i18n'
|
|
6
6
|
|
|
7
|
-
import withLocales from '../../hoc/withLocales'
|
|
8
7
|
import { SharedDriveForm } from './SharedDriveForm'
|
|
8
|
+
import withLocales from '../../hoc/withLocales'
|
|
9
9
|
|
|
10
10
|
export const SharedDriveModal = withLocales(({ onClose }) => {
|
|
11
11
|
const { t } = useI18n()
|
|
@@ -4,13 +4,8 @@ import { useClient } from 'cozy-client'
|
|
|
4
4
|
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
5
5
|
import { useI18n } from 'twake-i18n'
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
formatRecipients,
|
|
10
|
-
mergeAndDeduplicateRecipients,
|
|
11
|
-
moveRecipientToReadOnly,
|
|
12
|
-
moveRecipientToReadWrite
|
|
13
|
-
} from './helpers'
|
|
7
|
+
import { getOrCreateFromArray } from '../../helpers/contacts'
|
|
8
|
+
import { usePendingRecipients } from '../../hooks/usePendingRecipients'
|
|
14
9
|
import { Contact } from '../../models'
|
|
15
10
|
|
|
16
11
|
export const useSharedDrive = ({ onSuccess }) => {
|
|
@@ -18,36 +13,35 @@ export const useSharedDrive = ({ onSuccess }) => {
|
|
|
18
13
|
const { t } = useI18n()
|
|
19
14
|
const { showAlert } = useAlert()
|
|
20
15
|
|
|
21
|
-
const [sharedDriveRecipients, setSharedDriveRecipients] = useState({
|
|
22
|
-
recipients: [],
|
|
23
|
-
readOnlyRecipients: []
|
|
24
|
-
})
|
|
25
16
|
const [sharedDriveName, setSharedDriveName] = useState('')
|
|
17
|
+
const {
|
|
18
|
+
pendingRecipients,
|
|
19
|
+
setPendingRecipients,
|
|
20
|
+
selectedOption,
|
|
21
|
+
setSelectedOption
|
|
22
|
+
} = usePendingRecipients()
|
|
26
23
|
|
|
27
24
|
const handleSharedDriveNameChange = event => {
|
|
28
25
|
setSharedDriveName(event.target.value)
|
|
29
26
|
}
|
|
30
27
|
|
|
31
|
-
const
|
|
32
|
-
setSharedDriveRecipients(prev => ({
|
|
33
|
-
recipients: mergeAndDeduplicateRecipients([
|
|
34
|
-
prev.recipients,
|
|
35
|
-
params.recipients
|
|
36
|
-
]),
|
|
37
|
-
readOnlyRecipients: mergeAndDeduplicateRecipients([
|
|
38
|
-
prev.readOnlyRecipients,
|
|
39
|
-
params.readOnlyRecipients
|
|
40
|
-
])
|
|
41
|
-
}))
|
|
42
|
-
}
|
|
28
|
+
const createContact = contact => client.create(Contact.doctype, contact)
|
|
43
29
|
|
|
44
30
|
const onCreate = async () => {
|
|
45
31
|
try {
|
|
32
|
+
const contacts = await getOrCreateFromArray(
|
|
33
|
+
client,
|
|
34
|
+
pendingRecipients,
|
|
35
|
+
createContact
|
|
36
|
+
)
|
|
37
|
+
const readWriteRecipients = selectedOption === 'readOnly' ? [] : contacts
|
|
38
|
+
const readOnlyRecipients = selectedOption === 'readOnly' ? contacts : []
|
|
39
|
+
|
|
46
40
|
await client.collection('io.cozy.sharings').createSharedDrive({
|
|
47
41
|
name: sharedDriveName,
|
|
48
42
|
description: sharedDriveName,
|
|
49
|
-
recipients:
|
|
50
|
-
readOnlyRecipients
|
|
43
|
+
recipients: readWriteRecipients,
|
|
44
|
+
readOnlyRecipients
|
|
51
45
|
})
|
|
52
46
|
|
|
53
47
|
showAlert({
|
|
@@ -66,33 +60,13 @@ export const useSharedDrive = ({ onSuccess }) => {
|
|
|
66
60
|
}
|
|
67
61
|
}
|
|
68
62
|
|
|
69
|
-
const onSetType = (index, newType) => {
|
|
70
|
-
const _id = index.split(RECIPIENT_INDEX_PREFIX)[1]
|
|
71
|
-
setSharedDriveRecipients(prev =>
|
|
72
|
-
newType === 'two-way'
|
|
73
|
-
? moveRecipientToReadWrite(prev, _id)
|
|
74
|
-
: moveRecipientToReadOnly(prev, _id)
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const onRevoke = index => {
|
|
79
|
-
const _id = index.split(RECIPIENT_INDEX_PREFIX)[1]
|
|
80
|
-
setSharedDriveRecipients(prev => ({
|
|
81
|
-
recipients: prev.recipients.filter(r => r._id !== _id),
|
|
82
|
-
readOnlyRecipients: prev.readOnlyRecipients.filter(r => r._id !== _id)
|
|
83
|
-
}))
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const createContact = contact => client.create(Contact.doctype, contact)
|
|
87
|
-
|
|
88
63
|
return {
|
|
89
64
|
sharedDriveName,
|
|
90
|
-
recipients: formatRecipients(sharedDriveRecipients),
|
|
91
65
|
handleSharedDriveNameChange,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
66
|
+
pendingRecipients,
|
|
67
|
+
setPendingRecipients,
|
|
68
|
+
selectedOption,
|
|
69
|
+
setSelectedOption,
|
|
70
|
+
onCreate
|
|
97
71
|
}
|
|
98
72
|
}
|
|
@@ -11,16 +11,9 @@ const mockT = jest.fn(key => key)
|
|
|
11
11
|
|
|
12
12
|
jest.mock('cozy-client', () => ({
|
|
13
13
|
useClient: jest.fn(() => ({
|
|
14
|
-
collection: mockCollection
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
contact: {
|
|
18
|
-
getPrimaryEmail: jest.fn(r =>
|
|
19
|
-
Array.isArray(r.email) && r.email[0] ? r.email[0].address : ''
|
|
20
|
-
),
|
|
21
|
-
getDisplayName: jest.fn(r => r.displayName || r.name || '')
|
|
22
|
-
}
|
|
23
|
-
}
|
|
14
|
+
collection: mockCollection,
|
|
15
|
+
create: jest.fn()
|
|
16
|
+
}))
|
|
24
17
|
}))
|
|
25
18
|
|
|
26
19
|
jest.mock('cozy-ui/transpiled/react/providers/Alert', () => ({
|
|
@@ -35,6 +28,12 @@ jest.mock('../../models', () => ({
|
|
|
35
28
|
Contact: { doctype: 'io.cozy.contacts' }
|
|
36
29
|
}))
|
|
37
30
|
|
|
31
|
+
jest.mock('../../helpers/contacts', () => ({
|
|
32
|
+
getOrCreateFromArray: jest.fn((client, recipients) =>
|
|
33
|
+
Promise.resolve(recipients)
|
|
34
|
+
)
|
|
35
|
+
}))
|
|
36
|
+
|
|
38
37
|
describe('useSharedDrive', () => {
|
|
39
38
|
beforeEach(() => {
|
|
40
39
|
jest.clearAllMocks()
|
|
@@ -45,7 +44,8 @@ describe('useSharedDrive', () => {
|
|
|
45
44
|
useSharedDrive({ onSuccess: jest.fn() })
|
|
46
45
|
)
|
|
47
46
|
expect(result.current.sharedDriveName).toBe('')
|
|
48
|
-
expect(result.current.
|
|
47
|
+
expect(result.current.pendingRecipients).toEqual([])
|
|
48
|
+
expect(result.current.selectedOption).toBe('readWrite')
|
|
49
49
|
})
|
|
50
50
|
|
|
51
51
|
it('handleSharedDriveNameChange updates the name', () => {
|
|
@@ -60,56 +60,7 @@ describe('useSharedDrive', () => {
|
|
|
60
60
|
expect(result.current.sharedDriveName).toBe('My Drive')
|
|
61
61
|
})
|
|
62
62
|
|
|
63
|
-
it('
|
|
64
|
-
const { result } = renderHook(() =>
|
|
65
|
-
useSharedDrive({ onSuccess: jest.fn() })
|
|
66
|
-
)
|
|
67
|
-
act(() => {
|
|
68
|
-
result.current.onShare({
|
|
69
|
-
recipients: [{ _id: 'abc', displayName: 'Alice', email: [] }],
|
|
70
|
-
readOnlyRecipients: []
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
act(() => {
|
|
74
|
-
result.current.onRevoke(`virtual-shared-drive-sharing-abc`)
|
|
75
|
-
})
|
|
76
|
-
expect(result.current.recipients).toHaveLength(0)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('onSetType moves a recipient to read-only', () => {
|
|
80
|
-
const { result } = renderHook(() =>
|
|
81
|
-
useSharedDrive({ onSuccess: jest.fn() })
|
|
82
|
-
)
|
|
83
|
-
act(() => {
|
|
84
|
-
result.current.onShare({
|
|
85
|
-
recipients: [{ _id: 'abc', displayName: 'Alice', email: [] }],
|
|
86
|
-
readOnlyRecipients: []
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
act(() => {
|
|
90
|
-
result.current.onSetType(`virtual-shared-drive-sharing-abc`, 'one-way')
|
|
91
|
-
})
|
|
92
|
-
expect(result.current.recipients[0].type).toBe('one-way')
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('onSetType moves a recipient to read-write', () => {
|
|
96
|
-
const { result } = renderHook(() =>
|
|
97
|
-
useSharedDrive({ onSuccess: jest.fn() })
|
|
98
|
-
)
|
|
99
|
-
act(() => {
|
|
100
|
-
result.current.onShare({
|
|
101
|
-
recipients: [],
|
|
102
|
-
readOnlyRecipients: [{ _id: 'xyz', displayName: 'Bob', email: [] }]
|
|
103
|
-
})
|
|
104
|
-
})
|
|
105
|
-
act(() => {
|
|
106
|
-
result.current.onSetType(`virtual-shared-drive-sharing-xyz`, 'two-way')
|
|
107
|
-
})
|
|
108
|
-
expect(result.current.recipients).toHaveLength(1)
|
|
109
|
-
expect(result.current.recipients[0].type).toBe('two-way')
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
it('onCreate calls createSharedDrive, showAlert with success, and onSuccess', async () => {
|
|
63
|
+
it('onCreate calls createSharedDrive and onSuccess', async () => {
|
|
113
64
|
const onSuccess = jest.fn()
|
|
114
65
|
mockCreateSharedDrive.mockResolvedValue({})
|
|
115
66
|
|
|
@@ -119,18 +70,19 @@ describe('useSharedDrive', () => {
|
|
|
119
70
|
result.current.handleSharedDriveNameChange({
|
|
120
71
|
target: { value: 'My Drive' }
|
|
121
72
|
})
|
|
73
|
+
result.current.setPendingRecipients([{ email: 'a@b.c' }])
|
|
122
74
|
})
|
|
123
75
|
|
|
124
76
|
await act(async () => {
|
|
125
77
|
await result.current.onCreate()
|
|
126
78
|
})
|
|
127
79
|
|
|
128
|
-
expect(mockCreateSharedDrive).toHaveBeenCalledWith(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
80
|
+
expect(mockCreateSharedDrive).toHaveBeenCalledWith({
|
|
81
|
+
name: 'My Drive',
|
|
82
|
+
description: 'My Drive',
|
|
83
|
+
recipients: [{ email: 'a@b.c' }],
|
|
84
|
+
readOnlyRecipients: []
|
|
85
|
+
})
|
|
134
86
|
expect(mockShowAlert).toHaveBeenCalledWith(
|
|
135
87
|
expect.objectContaining({ severity: 'success' })
|
|
136
88
|
)
|
|
@@ -18,33 +18,29 @@ export const DumbBatchSharedFolderModal = withLocales(
|
|
|
18
18
|
document,
|
|
19
19
|
folderName,
|
|
20
20
|
handleFolderNameChange,
|
|
21
|
-
createContact,
|
|
22
21
|
recipients,
|
|
23
22
|
currentRecipients,
|
|
24
23
|
onRevoke,
|
|
25
|
-
onSetType,
|
|
26
24
|
onCreate,
|
|
27
25
|
onSend,
|
|
28
26
|
onClose,
|
|
29
|
-
onShare,
|
|
30
27
|
onRename,
|
|
31
28
|
showNameField = false,
|
|
32
29
|
sharingLink,
|
|
33
30
|
nameLabel,
|
|
34
31
|
addPeopleLabel,
|
|
35
|
-
addButtonLabel,
|
|
36
32
|
cancelLabel,
|
|
37
33
|
createLabel,
|
|
38
34
|
shareLabel,
|
|
39
35
|
saveLabel,
|
|
40
|
-
sharingDesc,
|
|
41
36
|
showShareByEmail = true,
|
|
42
37
|
autoOpenShareRestriction,
|
|
43
|
-
showGenerateLinkButton
|
|
38
|
+
showGenerateLinkButton,
|
|
39
|
+
pendingRecipients,
|
|
40
|
+
onPendingRecipientsChange,
|
|
41
|
+
selectedOption,
|
|
42
|
+
onSelectedOptionChange
|
|
44
43
|
}) => {
|
|
45
|
-
const hasRecipients = Boolean(recipients?.length)
|
|
46
|
-
|
|
47
|
-
// Determine action buttons based on modal mode
|
|
48
44
|
const actionButtons = (() => {
|
|
49
45
|
if (showNameField && onCreate) {
|
|
50
46
|
return (
|
|
@@ -80,7 +76,7 @@ export const DumbBatchSharedFolderModal = withLocales(
|
|
|
80
76
|
<Button
|
|
81
77
|
variant="primary"
|
|
82
78
|
label={shareLabel}
|
|
83
|
-
disabled={!
|
|
79
|
+
disabled={!onSend}
|
|
84
80
|
onClick={onSend}
|
|
85
81
|
/>
|
|
86
82
|
</>
|
|
@@ -130,17 +126,13 @@ export const DumbBatchSharedFolderModal = withLocales(
|
|
|
130
126
|
</Typography>
|
|
131
127
|
{showShareByEmail && (
|
|
132
128
|
<DumbShareByEmail
|
|
133
|
-
createContact={createContact}
|
|
134
129
|
currentRecipients={currentRecipients}
|
|
135
130
|
document={document}
|
|
136
131
|
documentType="Files"
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}}
|
|
142
|
-
submitLabel={addButtonLabel}
|
|
143
|
-
showNotifications={false}
|
|
132
|
+
pendingRecipients={pendingRecipients}
|
|
133
|
+
onPendingRecipientsChange={onPendingRecipientsChange}
|
|
134
|
+
selectedOption={selectedOption}
|
|
135
|
+
onSelectedOptionChange={onSelectedOptionChange}
|
|
144
136
|
/>
|
|
145
137
|
)}
|
|
146
138
|
</div>
|
|
@@ -152,7 +144,6 @@ export const DumbBatchSharedFolderModal = withLocales(
|
|
|
152
144
|
documentType="Files"
|
|
153
145
|
className="u-w-100"
|
|
154
146
|
onRevoke={onRevoke}
|
|
155
|
-
onSetType={onSetType}
|
|
156
147
|
link={sharingLink}
|
|
157
148
|
/>
|
|
158
149
|
</div>
|
|
@@ -168,31 +159,26 @@ DumbBatchSharedFolderModal.propTypes = {
|
|
|
168
159
|
document: PropTypes.object,
|
|
169
160
|
folderName: PropTypes.string,
|
|
170
161
|
handleFolderNameChange: PropTypes.func,
|
|
171
|
-
createContact: PropTypes.func.isRequired,
|
|
172
162
|
recipients: PropTypes.array,
|
|
173
163
|
currentRecipients: PropTypes.array,
|
|
174
164
|
onRevoke: PropTypes.func.isRequired,
|
|
175
|
-
onSetType: PropTypes.func.isRequired,
|
|
176
165
|
onCreate: PropTypes.func,
|
|
177
166
|
onSend: PropTypes.func,
|
|
178
167
|
onClose: PropTypes.func.isRequired,
|
|
179
|
-
onShare: PropTypes.func.isRequired,
|
|
180
168
|
onRename: PropTypes.func,
|
|
181
169
|
showNameField: PropTypes.bool,
|
|
182
170
|
sharingLink: PropTypes.string,
|
|
183
171
|
nameLabel: PropTypes.string,
|
|
184
172
|
addPeopleLabel: PropTypes.string,
|
|
185
|
-
addButtonLabel: PropTypes.string,
|
|
186
173
|
cancelLabel: PropTypes.string,
|
|
187
174
|
createLabel: PropTypes.string,
|
|
188
175
|
shareLabel: PropTypes.string,
|
|
189
176
|
saveLabel: PropTypes.string,
|
|
190
|
-
sharingDesc: PropTypes.string,
|
|
191
177
|
showShareByEmail: PropTypes.bool,
|
|
192
178
|
autoOpenShareRestriction: PropTypes.bool,
|
|
193
|
-
showGenerateLinkButton: PropTypes.bool
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
179
|
+
showGenerateLinkButton: PropTypes.bool,
|
|
180
|
+
pendingRecipients: PropTypes.array,
|
|
181
|
+
onPendingRecipientsChange: PropTypes.func,
|
|
182
|
+
selectedOption: PropTypes.oneOf(['readWrite', 'readOnly']),
|
|
183
|
+
onSelectedOptionChange: PropTypes.func
|
|
198
184
|
}
|
|
@@ -1,72 +1,89 @@
|
|
|
1
|
-
import cx from 'classnames'
|
|
2
1
|
import PropTypes from 'prop-types'
|
|
3
|
-
import React from 'react'
|
|
2
|
+
import React, { forwardRef, useRef, useState } from 'react'
|
|
4
3
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
4
|
+
import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
|
|
5
|
+
import { makeActions } from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
|
|
6
|
+
import ActionsMenuItem from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuItem'
|
|
7
|
+
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
|
|
8
|
+
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
9
|
+
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
10
|
+
import Radios from 'cozy-ui/transpiled/react/Radios'
|
|
9
11
|
|
|
10
12
|
import logger from '../logger'
|
|
11
|
-
import styles from '../styles/share.styl'
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const makeShareTypeAction = ({ option, isSelected, onSelect }) => {
|
|
15
|
+
const Component = forwardRef((props, ref) => (
|
|
16
|
+
<ActionsMenuItem
|
|
17
|
+
{...props}
|
|
18
|
+
ref={ref}
|
|
19
|
+
onClick={() => onSelect(option.value)}
|
|
20
|
+
disabled={option.disabled}
|
|
21
|
+
>
|
|
22
|
+
<ListItemIcon>
|
|
23
|
+
<Radios checked={isSelected} />
|
|
24
|
+
</ListItemIcon>
|
|
25
|
+
<ListItemText primary={option.label} />
|
|
26
|
+
</ActionsMenuItem>
|
|
27
|
+
))
|
|
28
|
+
Component.displayName = `ShareTypeAction-${option.value}`
|
|
18
29
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<div>
|
|
26
|
-
<div className={styles['select-option-label']}>{props.label}</div>
|
|
27
|
-
<div className={styles['select-option-desc']}>{props.data.desc}</div>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
</components.Option>
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
const customStyles = {
|
|
34
|
-
option: (base, state) => ({
|
|
35
|
-
...base,
|
|
36
|
-
color: 'var(--primaryTextColor)',
|
|
37
|
-
backgroundColor: state.isFocused ? 'var(--actionColorHover)' : null,
|
|
38
|
-
padding: 0,
|
|
39
|
-
borderBottom:
|
|
40
|
-
state.options.findIndex(o => o.value === state.value) === 0
|
|
41
|
-
? '1px solid var(--borderMainColor)'
|
|
42
|
-
: null
|
|
43
|
-
}),
|
|
44
|
-
menu: base => ({
|
|
45
|
-
...base,
|
|
46
|
-
backgroundColor: 'var(--paperBackgroundColor)',
|
|
47
|
-
width: '204%'
|
|
30
|
+
return () => ({
|
|
31
|
+
name: `shareType-${option.value}`,
|
|
32
|
+
label: option.label,
|
|
33
|
+
icon: null,
|
|
34
|
+
action: () => onSelect(option.value),
|
|
35
|
+
Component
|
|
48
36
|
})
|
|
49
37
|
}
|
|
50
38
|
|
|
51
|
-
const ShareTypeSelect = ({ options, onChange }) =>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
39
|
+
const ShareTypeSelect = ({ value, options, onChange }) => {
|
|
40
|
+
const buttonRef = useRef()
|
|
41
|
+
const [isOpen, setOpen] = useState(false)
|
|
42
|
+
|
|
43
|
+
const open = () => setOpen(true)
|
|
44
|
+
const close = () => setOpen(false)
|
|
45
|
+
|
|
46
|
+
const onSelect = nextValue => {
|
|
47
|
+
onChange(nextValue)
|
|
48
|
+
setOpen(false)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const selectedOption =
|
|
52
|
+
options.find(option => option.value === value) ?? options[0]
|
|
53
|
+
|
|
54
|
+
const actions = makeActions(
|
|
55
|
+
options.map(option =>
|
|
56
|
+
makeShareTypeAction({
|
|
57
|
+
option,
|
|
58
|
+
isSelected: option.value === selectedOption.value,
|
|
59
|
+
onSelect
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<>
|
|
66
|
+
<DropdownButton
|
|
67
|
+
ref={buttonRef}
|
|
68
|
+
onClick={open}
|
|
69
|
+
textVariant="body2"
|
|
70
|
+
size="small"
|
|
71
|
+
>
|
|
72
|
+
{selectedOption.label}
|
|
73
|
+
</DropdownButton>
|
|
74
|
+
<ActionsMenu
|
|
75
|
+
ref={buttonRef}
|
|
76
|
+
open={isOpen}
|
|
77
|
+
actions={actions}
|
|
78
|
+
autoClose
|
|
79
|
+
onClose={close}
|
|
80
|
+
/>
|
|
81
|
+
</>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
68
84
|
|
|
69
85
|
ShareTypeSelect.propTypes = {
|
|
86
|
+
value: PropTypes.string,
|
|
70
87
|
onChange: PropTypes.func,
|
|
71
88
|
options: PropTypes.array.isRequired
|
|
72
89
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export const usePendingRecipients = () => {
|
|
4
|
+
const [pendingRecipients, setPendingRecipients] = useState([])
|
|
5
|
+
const [selectedOption, setSelectedOption] = useState('readWrite')
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
pendingRecipients,
|
|
9
|
+
setPendingRecipients,
|
|
10
|
+
selectedOption,
|
|
11
|
+
setSelectedOption
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { renderHook, act } from '@testing-library/react-hooks'
|
|
2
|
+
|
|
3
|
+
import { usePendingRecipients } from './usePendingRecipients'
|
|
4
|
+
|
|
5
|
+
describe('usePendingRecipients', () => {
|
|
6
|
+
it('starts with empty recipients and readWrite option', () => {
|
|
7
|
+
const { result } = renderHook(() => usePendingRecipients())
|
|
8
|
+
expect(result.current.pendingRecipients).toEqual([])
|
|
9
|
+
expect(result.current.selectedOption).toBe('readWrite')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('exposes setters that update state', () => {
|
|
13
|
+
const { result } = renderHook(() => usePendingRecipients())
|
|
14
|
+
act(() => {
|
|
15
|
+
result.current.setPendingRecipients([{ email: 'a@b.c' }])
|
|
16
|
+
result.current.setSelectedOption('readOnly')
|
|
17
|
+
})
|
|
18
|
+
expect(result.current.pendingRecipients).toEqual([{ email: 'a@b.c' }])
|
|
19
|
+
expect(result.current.selectedOption).toBe('readOnly')
|
|
20
|
+
})
|
|
21
|
+
})
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
box-sizing border-box
|
|
6
6
|
position absolute
|
|
7
7
|
top 100%
|
|
8
|
-
margin-top .
|
|
8
|
+
margin-top .5rem
|
|
9
9
|
z-index var(--zIndex-alertMobile)
|
|
10
10
|
width 100%
|
|
11
11
|
overflow hidden
|
|
@@ -37,12 +37,13 @@
|
|
|
37
37
|
display flex
|
|
38
38
|
flex-wrap wrap
|
|
39
39
|
align-items center
|
|
40
|
-
padding .25rem .5rem
|
|
40
|
+
padding .25rem 3.5rem .25rem .5rem
|
|
41
41
|
border-radius .25rem
|
|
42
42
|
border 1px solid var(--borderMainColor)
|
|
43
43
|
min-height 2.5rem
|
|
44
44
|
max-height 6.75rem
|
|
45
45
|
overflow auto
|
|
46
|
+
position relative
|
|
46
47
|
|
|
47
48
|
&:hover
|
|
48
49
|
border 1px solid var(--borderMainColor)
|
|
@@ -69,3 +70,8 @@
|
|
|
69
70
|
font-size 1rem
|
|
70
71
|
.recipientChip
|
|
71
72
|
margin .25rem .5rem .25rem 0
|
|
73
|
+
|
|
74
|
+
.endAdornment
|
|
75
|
+
position absolute
|
|
76
|
+
top .75rem
|
|
77
|
+
right 1rem
|
package/src/styles/share.styl
CHANGED
|
@@ -38,45 +38,6 @@
|
|
|
38
38
|
.coz-form-desc
|
|
39
39
|
color var(--secondaryTextColor)
|
|
40
40
|
|
|
41
|
-
.share-type-control
|
|
42
|
-
display flex
|
|
43
|
-
flex-direction row
|
|
44
|
-
justify-content space-between
|
|
45
|
-
padding .5rem 0 0
|
|
46
|
-
|
|
47
|
-
.select-wrapper
|
|
48
|
-
flex 0 0 49%
|
|
49
|
-
position relative
|
|
50
|
-
|
|
51
|
-
.select-option
|
|
52
|
-
padding .5rem 1.5rem .5rem 3rem
|
|
53
|
-
position relative
|
|
54
|
-
|
|
55
|
-
svg
|
|
56
|
-
position absolute
|
|
57
|
-
left 1rem
|
|
58
|
-
top 1rem
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.select-option-label
|
|
62
|
-
line-height 2
|
|
63
|
-
|
|
64
|
-
.select-option-desc
|
|
65
|
-
line-height 1.5
|
|
66
|
-
font-size .8125rem
|
|
67
|
-
color var(--secondaryTextColor)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
button
|
|
72
|
-
flex 0 0 49%
|
|
73
|
-
margin 0
|
|
74
|
-
|
|
75
|
-
:global
|
|
76
|
-
// dirty hack for react-select: its API doesn't allow to make change to active styles
|
|
77
|
-
.react-select__option:active
|
|
78
|
-
background-color var(--defaultBackgroundColor) !important
|
|
79
|
-
|
|
80
41
|
input[type=text]
|
|
81
42
|
width 100%
|
|
82
43
|
|