cozy-sharing 28.5.0 → 28.7.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 +15 -0
- package/dist/SharingProvider.js +186 -87
- package/dist/SharingProvider.spec.js +152 -0
- package/dist/components/Recipient/LinkRecipient.js +27 -8
- package/dist/components/Recipient/LinkRecipient.stories.js +0 -1
- package/dist/components/Recipient/LinkRecipientPermissions.js +62 -15
- package/dist/components/Recipient/MemberRecipient.spec.js +13 -0
- package/dist/components/Recipient/MemberRecipientPermissions.js +80 -20
- package/dist/components/Recipient/MemberRecipientPermissions.spec.js +214 -0
- package/dist/components/Recipient/actions/revokeLink.js +34 -0
- package/dist/components/Recipient/actions/setReadOnlySharedPermission.js +0 -1
- package/dist/components/Recipient/actions/setReadOnlySharedPermission.spec.js +72 -0
- package/dist/components/Recipient/actions/setReadWriteSharedPermission.js +0 -1
- package/dist/components/Recipient/actions/setReadWriteSharedPermission.spec.js +72 -0
- package/dist/components/ShareDialogCozyToCozy.spec.js +9 -0
- package/dist/components/WhoHasAccess.js +0 -1
- package/locales/en.json +4 -1
- package/locales/fr.json +4 -1
- package/locales/ru.json +4 -1
- package/locales/vi.json +4 -1
- package/package.json +2 -2
- package/src/SharingProvider.jsx +52 -1
- package/src/SharingProvider.spec.jsx +94 -0
- package/src/components/Recipient/LinkRecipient.jsx +30 -7
- package/src/components/Recipient/LinkRecipient.stories.jsx +0 -1
- package/src/components/Recipient/LinkRecipientPermissions.jsx +54 -16
- package/src/components/Recipient/MemberRecipient.spec.jsx +11 -0
- package/src/components/Recipient/MemberRecipientPermissions.jsx +69 -16
- package/src/components/Recipient/MemberRecipientPermissions.spec.jsx +165 -0
- package/src/components/Recipient/actions/revokeLink.js +33 -0
- package/src/components/Recipient/actions/setReadOnlySharedPermission.js +1 -1
- package/src/components/Recipient/actions/setReadOnlySharedPermission.spec.jsx +95 -0
- package/src/components/Recipient/actions/setReadWriteSharedPermission.js +1 -1
- package/src/components/Recipient/actions/setReadWriteSharedPermission.spec.jsx +95 -0
- package/src/components/ShareDialogCozyToCozy.spec.jsx +6 -0
- package/src/components/WhoHasAccess.jsx +0 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
|
-
import React from 'react'
|
|
2
|
+
import React, { useState } from 'react'
|
|
3
3
|
import { useI18n } from 'twake-i18n'
|
|
4
4
|
|
|
5
5
|
import Avatar from 'cozy-ui/transpiled/react/Avatar'
|
|
6
|
+
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
|
|
6
7
|
import Fade from 'cozy-ui/transpiled/react/Fade'
|
|
7
8
|
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
8
9
|
import LinkIcon from 'cozy-ui/transpiled/react/Icons/Link'
|
|
9
10
|
import ListItem from 'cozy-ui/transpiled/react/ListItem'
|
|
10
11
|
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
11
12
|
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
12
|
-
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
13
13
|
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
|
|
14
14
|
|
|
15
15
|
import LinkRecipientPermissions from './LinkRecipientPermissions'
|
|
@@ -20,14 +20,17 @@ import {
|
|
|
20
20
|
} from '../../helpers/permissions'
|
|
21
21
|
import { FADE_IN_DURATION } from '../../helpers/recipients'
|
|
22
22
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
23
|
+
import { ShareRestrictionModal } from '../ShareRestrictionModal/ShareRestrictionModal'
|
|
23
24
|
|
|
24
25
|
const LinkRecipient = props => {
|
|
25
26
|
const { t, f, lang } = useI18n()
|
|
26
27
|
const { isMobile } = useBreakpoints()
|
|
27
28
|
const { getDocumentPermissions } = useSharingContext()
|
|
28
29
|
|
|
29
|
-
const { recipientConfirmationData, verifyRecipient,
|
|
30
|
-
|
|
30
|
+
const { recipientConfirmationData, verifyRecipient, fadeIn, document } = props
|
|
31
|
+
|
|
32
|
+
const [openShareRestrictionModal, setOpenShareRestrictionModal] =
|
|
33
|
+
useState(false)
|
|
31
34
|
|
|
32
35
|
const permissions = getDocumentPermissions(document?._id)
|
|
33
36
|
const hasPassword = checkIsPermissionHasPassword(permissions)
|
|
@@ -44,7 +47,7 @@ const LinkRecipient = props => {
|
|
|
44
47
|
? t('Share.recipients.expires', {
|
|
45
48
|
date: dateFormatted
|
|
46
49
|
})
|
|
47
|
-
:
|
|
50
|
+
: ''
|
|
48
51
|
|
|
49
52
|
const RightPart = recipientConfirmationData ? (
|
|
50
53
|
<RecipientConfirm
|
|
@@ -64,17 +67,37 @@ const LinkRecipient = props => {
|
|
|
64
67
|
</Avatar>
|
|
65
68
|
</ListItemIcon>
|
|
66
69
|
<ListItemText
|
|
67
|
-
primary={
|
|
70
|
+
primary={
|
|
71
|
+
<DropdownButton
|
|
72
|
+
textVariant="body1"
|
|
73
|
+
onClick={() => setOpenShareRestrictionModal(true)}
|
|
74
|
+
>
|
|
75
|
+
{textPrimary}
|
|
76
|
+
</DropdownButton>
|
|
77
|
+
}
|
|
68
78
|
secondary={textSecondary}
|
|
69
79
|
/>
|
|
70
80
|
{RightPart}
|
|
81
|
+
{openShareRestrictionModal && (
|
|
82
|
+
<ShareRestrictionModal
|
|
83
|
+
file={document}
|
|
84
|
+
onClose={() => setOpenShareRestrictionModal(false)}
|
|
85
|
+
/>
|
|
86
|
+
)}
|
|
71
87
|
</ListItem>
|
|
72
88
|
</Fade>
|
|
73
89
|
)
|
|
74
90
|
}
|
|
75
91
|
|
|
76
92
|
LinkRecipient.propTypes = {
|
|
77
|
-
|
|
93
|
+
recipientConfirmationData: PropTypes.oneOfType([
|
|
94
|
+
PropTypes.bool,
|
|
95
|
+
PropTypes.object
|
|
96
|
+
]),
|
|
97
|
+
verifyRecipient: PropTypes.func,
|
|
98
|
+
fadeIn: PropTypes.bool,
|
|
99
|
+
document: PropTypes.object,
|
|
100
|
+
permissions: PropTypes.arrayOf(PropTypes.object)
|
|
78
101
|
}
|
|
79
102
|
|
|
80
103
|
export default LinkRecipient
|
|
@@ -2,18 +2,58 @@ import PropTypes from 'prop-types'
|
|
|
2
2
|
import React, { useState, useRef } from 'react'
|
|
3
3
|
import { useI18n } from 'twake-i18n'
|
|
4
4
|
|
|
5
|
+
import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
|
|
6
|
+
import {
|
|
7
|
+
makeActions,
|
|
8
|
+
divider
|
|
9
|
+
} from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
|
|
5
10
|
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
|
|
6
11
|
|
|
12
|
+
import { revokeLink } from './actions/revokeLink'
|
|
13
|
+
import { setReadOnlySharedPermission } from './actions/setReadOnlySharedPermission'
|
|
14
|
+
import { setReadWriteSharedPermission } from './actions/setReadWriteSharedPermission'
|
|
15
|
+
import { isOnlyReadOnlyLinkAllowed } from '../../helpers/link'
|
|
7
16
|
import { checkIsReadOnlyPermissions } from '../../helpers/permissions'
|
|
8
|
-
import {
|
|
17
|
+
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
18
|
+
import { WRITE_PERMS, READ_ONLY_PERMS } from '../ShareRestrictionModal/helpers'
|
|
9
19
|
|
|
10
|
-
const LinkRecipientPermissions = ({ className, document
|
|
20
|
+
const LinkRecipientPermissions = ({ className, document }) => {
|
|
11
21
|
const { t } = useI18n()
|
|
12
22
|
const buttonRef = useRef()
|
|
13
|
-
const [
|
|
14
|
-
|
|
23
|
+
const [isMenuDisplayed, setMenuDisplayed] = useState(false)
|
|
24
|
+
const {
|
|
25
|
+
updateDocumentPermissions,
|
|
26
|
+
revokeSharingLink,
|
|
27
|
+
documentType,
|
|
28
|
+
getDocumentPermissions
|
|
29
|
+
} = useSharingContext()
|
|
15
30
|
|
|
31
|
+
const permissions = getDocumentPermissions(document._id)
|
|
16
32
|
const isReadOnlyPermissions = checkIsReadOnlyPermissions(permissions)
|
|
33
|
+
const type = isReadOnlyPermissions ? 'one-way' : 'two-way'
|
|
34
|
+
|
|
35
|
+
const toggleMenu = () => setMenuDisplayed(!isMenuDisplayed)
|
|
36
|
+
const hideMenu = () => setMenuDisplayed(false)
|
|
37
|
+
|
|
38
|
+
const handleSetType = newType => {
|
|
39
|
+
const verbs = newType === 'one-way' ? READ_ONLY_PERMS : WRITE_PERMS
|
|
40
|
+
updateDocumentPermissions(document, { verbs })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handleRevocation = () => revokeSharingLink(document)
|
|
44
|
+
|
|
45
|
+
const actionsToShow = [setReadOnlySharedPermission]
|
|
46
|
+
if (!isOnlyReadOnlyLinkAllowed({ documentType })) {
|
|
47
|
+
actionsToShow.push(setReadWriteSharedPermission)
|
|
48
|
+
}
|
|
49
|
+
actionsToShow.push(divider, revokeLink)
|
|
50
|
+
|
|
51
|
+
const actions = makeActions(actionsToShow, {
|
|
52
|
+
t,
|
|
53
|
+
type,
|
|
54
|
+
setType: handleSetType,
|
|
55
|
+
handleRevocation
|
|
56
|
+
})
|
|
17
57
|
|
|
18
58
|
return (
|
|
19
59
|
<div className={className}>
|
|
@@ -21,18 +61,17 @@ const LinkRecipientPermissions = ({ className, document, permissions }) => {
|
|
|
21
61
|
<DropdownButton
|
|
22
62
|
ref={buttonRef}
|
|
23
63
|
textVariant="body2"
|
|
24
|
-
onClick={
|
|
64
|
+
onClick={toggleMenu}
|
|
25
65
|
>
|
|
26
|
-
{t(
|
|
27
|
-
`Share.type.${isReadOnlyPermissions ? 'one-way' : 'two-way'}`
|
|
28
|
-
).toLowerCase()}
|
|
66
|
+
{t(`Share.type.${type}`).toLowerCase()}
|
|
29
67
|
</DropdownButton>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
68
|
+
<ActionsMenu
|
|
69
|
+
ref={buttonRef}
|
|
70
|
+
open={isMenuDisplayed}
|
|
71
|
+
actions={actions}
|
|
72
|
+
autoClose
|
|
73
|
+
onClose={hideMenu}
|
|
74
|
+
/>
|
|
36
75
|
</>
|
|
37
76
|
</div>
|
|
38
77
|
)
|
|
@@ -40,8 +79,7 @@ const LinkRecipientPermissions = ({ className, document, permissions }) => {
|
|
|
40
79
|
|
|
41
80
|
LinkRecipientPermissions.propTypes = {
|
|
42
81
|
className: PropTypes.string,
|
|
43
|
-
document: PropTypes.object.isRequired
|
|
44
|
-
permissions: PropTypes.arrayOf(PropTypes.object).isRequired
|
|
82
|
+
document: PropTypes.object.isRequired
|
|
45
83
|
}
|
|
46
84
|
|
|
47
85
|
export default LinkRecipientPermissions
|
|
@@ -6,6 +6,17 @@ import { createMockClient } from 'cozy-client'
|
|
|
6
6
|
import MemberRecipient from './MemberRecipient'
|
|
7
7
|
import AppLike from '../../../test/AppLike'
|
|
8
8
|
|
|
9
|
+
const mockUpdateDocumentPermissions = jest.fn()
|
|
10
|
+
const mockUpdateSharingMemberType = jest.fn()
|
|
11
|
+
|
|
12
|
+
jest.mock('../../hooks/useSharingContext', () => ({
|
|
13
|
+
// eslint-disable-next-line react/display-name
|
|
14
|
+
useSharingContext: () => ({
|
|
15
|
+
updateDocumentPermissions: mockUpdateDocumentPermissions,
|
|
16
|
+
updateSharingMemberType: mockUpdateSharingMemberType
|
|
17
|
+
})
|
|
18
|
+
}))
|
|
19
|
+
|
|
9
20
|
describe('MemberRecipient component', () => {
|
|
10
21
|
const client = createMockClient({})
|
|
11
22
|
client.options = {
|
|
@@ -2,6 +2,7 @@ import React, { useState, useRef, useCallback } from 'react'
|
|
|
2
2
|
import { useI18n } from 'twake-i18n'
|
|
3
3
|
|
|
4
4
|
import { useClient } from 'cozy-client'
|
|
5
|
+
import minilog from 'cozy-minilog'
|
|
5
6
|
import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
|
|
6
7
|
import {
|
|
7
8
|
makeActions,
|
|
@@ -10,9 +11,14 @@ import {
|
|
|
10
11
|
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
|
|
11
12
|
import Spinner from 'cozy-ui/transpiled/react/Spinner'
|
|
12
13
|
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
14
|
+
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
13
15
|
|
|
14
|
-
import { permission } from './actions/permission'
|
|
15
16
|
import { revokeMember } from './actions/revokeMember'
|
|
17
|
+
import { setReadOnlySharedPermission } from './actions/setReadOnlySharedPermission'
|
|
18
|
+
import { setReadWriteSharedPermission } from './actions/setReadWriteSharedPermission'
|
|
19
|
+
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
20
|
+
|
|
21
|
+
const log = minilog('MemberRecipientPermissions')
|
|
16
22
|
|
|
17
23
|
const MemberRecipientPermissions = ({
|
|
18
24
|
isOwner,
|
|
@@ -30,6 +36,8 @@ const MemberRecipientPermissions = ({
|
|
|
30
36
|
const { t } = useI18n()
|
|
31
37
|
const buttonRef = useRef()
|
|
32
38
|
const client = useClient()
|
|
39
|
+
const { updateSharingMemberType } = useSharingContext()
|
|
40
|
+
const { showAlert } = useAlert()
|
|
33
41
|
|
|
34
42
|
const [revoking, setRevoking] = useState(false)
|
|
35
43
|
const [isMenuDisplayed, setMenuDisplayed] = useState(false)
|
|
@@ -42,27 +50,72 @@ const MemberRecipientPermissions = ({
|
|
|
42
50
|
!contactIsOwner &&
|
|
43
51
|
((instanceMatchesClient && !isOwner) || isOwner)
|
|
44
52
|
|
|
45
|
-
const toggleMenu = () =>
|
|
46
|
-
|
|
53
|
+
const toggleMenu = useCallback(() => {
|
|
54
|
+
setMenuDisplayed(displayed => !displayed)
|
|
55
|
+
}, [])
|
|
56
|
+
const hideMenu = useCallback(() => {
|
|
57
|
+
setMenuDisplayed(false)
|
|
58
|
+
}, [])
|
|
47
59
|
|
|
48
60
|
const handleRevocation = useCallback(async () => {
|
|
49
61
|
setRevoking(true)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
try {
|
|
63
|
+
if (isOwner) {
|
|
64
|
+
await onRevoke(document, sharingId, memberIndex)
|
|
65
|
+
} else {
|
|
66
|
+
await onRevokeSelf(document)
|
|
67
|
+
}
|
|
68
|
+
} finally {
|
|
69
|
+
setRevoking(false)
|
|
54
70
|
}
|
|
55
|
-
setRevoking(false)
|
|
56
71
|
}, [isOwner, onRevoke, onRevokeSelf, document, sharingId, memberIndex])
|
|
57
72
|
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
const setType = useCallback(
|
|
74
|
+
async newType => {
|
|
75
|
+
if (newType === type) {
|
|
76
|
+
hideMenu()
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
await updateSharingMemberType(sharingId, memberIndex, newType)
|
|
81
|
+
} catch (error) {
|
|
82
|
+
log.error('Failed to change member permission type', error)
|
|
83
|
+
showAlert({
|
|
84
|
+
message: t('Share.members.error.changePermission'),
|
|
85
|
+
severity: 'error',
|
|
86
|
+
variant: 'filled'
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
hideMenu()
|
|
90
|
+
},
|
|
91
|
+
[
|
|
92
|
+
hideMenu,
|
|
93
|
+
memberIndex,
|
|
94
|
+
sharingId,
|
|
95
|
+
showAlert,
|
|
96
|
+
t,
|
|
97
|
+
type,
|
|
98
|
+
updateSharingMemberType
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
const actions = makeActions(
|
|
103
|
+
[
|
|
104
|
+
setReadOnlySharedPermission,
|
|
105
|
+
setReadWriteSharedPermission,
|
|
106
|
+
divider,
|
|
107
|
+
revokeMember
|
|
108
|
+
],
|
|
109
|
+
{
|
|
110
|
+
client,
|
|
111
|
+
t,
|
|
112
|
+
type: type ?? 'one-way',
|
|
113
|
+
isOwner,
|
|
114
|
+
isSharedDrive,
|
|
115
|
+
setType,
|
|
116
|
+
handleRevocation
|
|
117
|
+
}
|
|
118
|
+
)
|
|
66
119
|
|
|
67
120
|
return (
|
|
68
121
|
<div className={className}>
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { render, fireEvent, screen, act, waitFor } from '@testing-library/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import { createMockClient } from 'cozy-client'
|
|
5
|
+
|
|
6
|
+
import MemberRecipientPermissions from './MemberRecipientPermissions'
|
|
7
|
+
import AppLike from '../../../test/AppLike'
|
|
8
|
+
|
|
9
|
+
const mockUpdateSharingMemberType = jest.fn()
|
|
10
|
+
const mockShowAlert = jest.fn()
|
|
11
|
+
|
|
12
|
+
jest.mock('../../hooks/useSharingContext', () => ({
|
|
13
|
+
useSharingContext: () => ({
|
|
14
|
+
updateSharingMemberType: mockUpdateSharingMemberType
|
|
15
|
+
})
|
|
16
|
+
}))
|
|
17
|
+
|
|
18
|
+
jest.mock('cozy-ui/transpiled/react/providers/Alert', () => ({
|
|
19
|
+
__esModule: true,
|
|
20
|
+
default: ({ children }) => children,
|
|
21
|
+
useAlert: () => ({
|
|
22
|
+
showAlert: mockShowAlert
|
|
23
|
+
})
|
|
24
|
+
}))
|
|
25
|
+
|
|
26
|
+
describe('MemberRecipientPermissions component', () => {
|
|
27
|
+
const client = createMockClient({})
|
|
28
|
+
client.options = {
|
|
29
|
+
uri: 'http://cozy.local:8080'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const defaultProps = {
|
|
33
|
+
isOwner: true,
|
|
34
|
+
isSharedDrive: false,
|
|
35
|
+
status: 'ready',
|
|
36
|
+
instance: 'http://cozy.local:8080',
|
|
37
|
+
type: 'two-way',
|
|
38
|
+
document: { id: 'doc-123', type: 'io.cozy.files', path: '/test' },
|
|
39
|
+
sharingId: 'sharing-123',
|
|
40
|
+
memberIndex: 1,
|
|
41
|
+
onRevoke: jest.fn(),
|
|
42
|
+
onRevokeSelf: jest.fn()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
jest.clearAllMocks()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const setup = props => {
|
|
50
|
+
return render(
|
|
51
|
+
<AppLike client={client}>
|
|
52
|
+
<MemberRecipientPermissions {...defaultProps} {...props} />
|
|
53
|
+
</AppLike>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let createRangeBackup
|
|
58
|
+
|
|
59
|
+
beforeAll(() => {
|
|
60
|
+
createRangeBackup = global.document.createRange
|
|
61
|
+
global.document.createRange = jest.fn(() => ({
|
|
62
|
+
setStart: () => {},
|
|
63
|
+
setEnd: () => {},
|
|
64
|
+
commonAncestorContainer: {
|
|
65
|
+
nodeName: 'BODY',
|
|
66
|
+
ownerDocument: document
|
|
67
|
+
}
|
|
68
|
+
}))
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
afterAll(() => {
|
|
72
|
+
global.document.createRange = createRangeBackup
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should render the current permission type', () => {
|
|
76
|
+
setup()
|
|
77
|
+
expect(screen.getByText('can change')).toBeInTheDocument()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should call updateSharingMemberType when changing to one-way', async () => {
|
|
81
|
+
mockUpdateSharingMemberType.mockResolvedValue(undefined)
|
|
82
|
+
|
|
83
|
+
const { getByText } = setup({ type: 'two-way' })
|
|
84
|
+
|
|
85
|
+
fireEvent.click(getByText('can change'))
|
|
86
|
+
fireEvent.click(getByText('Can view'))
|
|
87
|
+
|
|
88
|
+
expect(mockUpdateSharingMemberType).toHaveBeenCalledWith(
|
|
89
|
+
'sharing-123',
|
|
90
|
+
1,
|
|
91
|
+
'one-way'
|
|
92
|
+
)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('should call updateSharingMemberType when changing to two-way', async () => {
|
|
96
|
+
mockUpdateSharingMemberType.mockResolvedValue(undefined)
|
|
97
|
+
|
|
98
|
+
const { getByText } = setup({ type: 'one-way' })
|
|
99
|
+
|
|
100
|
+
fireEvent.click(getByText('can view'))
|
|
101
|
+
fireEvent.click(getByText('Can change'))
|
|
102
|
+
|
|
103
|
+
expect(mockUpdateSharingMemberType).toHaveBeenCalledWith(
|
|
104
|
+
'sharing-123',
|
|
105
|
+
1,
|
|
106
|
+
'two-way'
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should show error alert when updateSharingMemberType fails', async () => {
|
|
111
|
+
mockUpdateSharingMemberType.mockRejectedValue(new Error('Network error'))
|
|
112
|
+
|
|
113
|
+
const { getByText } = setup({ type: 'two-way' })
|
|
114
|
+
|
|
115
|
+
await act(async () => {
|
|
116
|
+
fireEvent.click(getByText('can change'))
|
|
117
|
+
fireEvent.click(getByText('Can view'))
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
await waitFor(() => {
|
|
121
|
+
expect(mockShowAlert).toHaveBeenCalledWith({
|
|
122
|
+
message:
|
|
123
|
+
'An error occurred while changing the permissions. Please try again.',
|
|
124
|
+
severity: 'error',
|
|
125
|
+
variant: 'filled'
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('should not call updateSharingMemberType when selecting same type', async () => {
|
|
131
|
+
mockUpdateSharingMemberType.mockResolvedValue(undefined)
|
|
132
|
+
|
|
133
|
+
const { getByText } = setup({ type: 'two-way' })
|
|
134
|
+
|
|
135
|
+
fireEvent.click(getByText('can change'))
|
|
136
|
+
fireEvent.click(getByText('Can change'))
|
|
137
|
+
|
|
138
|
+
expect(mockUpdateSharingMemberType).not.toHaveBeenCalled()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should not render menu when user is not owner and not the shared drive contact', () => {
|
|
142
|
+
const { queryByText } = setup({
|
|
143
|
+
isOwner: false,
|
|
144
|
+
instance: 'http://other-cozy.local:8080'
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
expect(queryByText('can change')).not.toBeInTheDocument()
|
|
148
|
+
expect(queryByText('can view')).not.toBeInTheDocument()
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('should render menu for owner', () => {
|
|
152
|
+
const { getByText } = setup({ isOwner: true })
|
|
153
|
+
|
|
154
|
+
expect(getByText('can change')).toBeInTheDocument()
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should render menu for the user themselves', () => {
|
|
158
|
+
const { getByText } = setup({
|
|
159
|
+
isOwner: false,
|
|
160
|
+
instance: 'http://cozy.local:8080'
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
expect(getByText('can change')).toBeInTheDocument()
|
|
164
|
+
})
|
|
165
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
|
|
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
|
+
const revokeLink = ({ t, handleRevocation }) => {
|
|
11
|
+
const title = t('Share.permissionLink.deactivate')
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
name: 'revokeLink',
|
|
15
|
+
label: title,
|
|
16
|
+
icon: TrashIcon,
|
|
17
|
+
action: handleRevocation,
|
|
18
|
+
Component: forwardRef(function RevokeLinkItem(props, ref) {
|
|
19
|
+
return (
|
|
20
|
+
<ActionsMenuItem {...props} ref={ref}>
|
|
21
|
+
<ListItemIcon>
|
|
22
|
+
<Icon icon={TrashIcon} color="var(--errorColor)" />
|
|
23
|
+
</ListItemIcon>
|
|
24
|
+
<ListItemText
|
|
25
|
+
primary={<Typography color="error">{title}</Typography>}
|
|
26
|
+
/>
|
|
27
|
+
</ActionsMenuItem>
|
|
28
|
+
)
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { revokeLink }
|
|
@@ -8,7 +8,7 @@ import Radios from 'cozy-ui/transpiled/react/Radios'
|
|
|
8
8
|
const makeComponent = ({ label, type, onClick }) => {
|
|
9
9
|
const Component = forwardRef((props, ref) => {
|
|
10
10
|
return (
|
|
11
|
-
<ActionsMenuItem {...props} ref={ref}
|
|
11
|
+
<ActionsMenuItem {...props} ref={ref} onClick={onClick}>
|
|
12
12
|
<ListItemIcon>
|
|
13
13
|
<Radios checked={type === 'one-way'} />
|
|
14
14
|
</ListItemIcon>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { render, screen, fireEvent } from '@testing-library/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import { setReadOnlySharedPermission } from './setReadOnlySharedPermission'
|
|
5
|
+
import AppLike from '../../../../test/AppLike'
|
|
6
|
+
|
|
7
|
+
const mockT = key => {
|
|
8
|
+
const translations = {
|
|
9
|
+
'Share.type.one-way': 'Can view',
|
|
10
|
+
'Share.type.two-way': 'Can change'
|
|
11
|
+
}
|
|
12
|
+
return translations[key] || key
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('setReadOnlySharedPermission', () => {
|
|
16
|
+
let setType
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
setType = jest.fn()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should return correct action structure', () => {
|
|
23
|
+
const action = setReadOnlySharedPermission({
|
|
24
|
+
t: mockT,
|
|
25
|
+
type: 'one-way',
|
|
26
|
+
setType
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
expect(action.name).toBe('setReadOnlySharedPermission')
|
|
30
|
+
expect(action.label).toBe('Can view')
|
|
31
|
+
expect(action.icon).toBeNull()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should call setType with one-way when action is triggered', () => {
|
|
35
|
+
const action = setReadOnlySharedPermission({
|
|
36
|
+
t: mockT,
|
|
37
|
+
type: 'two-way',
|
|
38
|
+
setType
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
action.action()
|
|
42
|
+
|
|
43
|
+
expect(setType).toHaveBeenCalledWith('one-way')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should render radio as checked when type is one-way', () => {
|
|
47
|
+
const { Component } = setReadOnlySharedPermission({
|
|
48
|
+
t: mockT,
|
|
49
|
+
type: 'one-way',
|
|
50
|
+
setType
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
render(
|
|
54
|
+
<AppLike>
|
|
55
|
+
<Component />
|
|
56
|
+
</AppLike>
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
expect(screen.getByRole('radio')).toBeChecked()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should render radio as unchecked when type is two-way', () => {
|
|
63
|
+
const { Component } = setReadOnlySharedPermission({
|
|
64
|
+
t: mockT,
|
|
65
|
+
type: 'two-way',
|
|
66
|
+
setType
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
render(
|
|
70
|
+
<AppLike>
|
|
71
|
+
<Component />
|
|
72
|
+
</AppLike>
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
expect(screen.getByRole('radio')).not.toBeChecked()
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('should call setType with one-way when the component is clicked', () => {
|
|
79
|
+
const { Component } = setReadOnlySharedPermission({
|
|
80
|
+
t: mockT,
|
|
81
|
+
type: 'two-way',
|
|
82
|
+
setType
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
render(
|
|
86
|
+
<AppLike>
|
|
87
|
+
<Component />
|
|
88
|
+
</AppLike>
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
fireEvent.click(screen.getByText('Can view'))
|
|
92
|
+
|
|
93
|
+
expect(setType).toHaveBeenCalledWith('one-way')
|
|
94
|
+
})
|
|
95
|
+
})
|
|
@@ -8,7 +8,7 @@ import Radios from 'cozy-ui/transpiled/react/Radios'
|
|
|
8
8
|
const makeComponent = ({ label, type, onClick }) => {
|
|
9
9
|
const Component = forwardRef((props, ref) => {
|
|
10
10
|
return (
|
|
11
|
-
<ActionsMenuItem {...props} ref={ref}
|
|
11
|
+
<ActionsMenuItem {...props} ref={ref} onClick={onClick}>
|
|
12
12
|
<ListItemIcon>
|
|
13
13
|
<Radios checked={type === 'two-way'} />
|
|
14
14
|
</ListItemIcon>
|