cozy-sharing 28.5.0 → 28.6.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 (32) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/SharingProvider.js +182 -86
  3. package/dist/SharingProvider.spec.js +152 -0
  4. package/dist/components/Recipient/LinkRecipient.js +6 -3
  5. package/dist/components/Recipient/LinkRecipient.stories.js +0 -1
  6. package/dist/components/Recipient/MemberRecipient.spec.js +13 -0
  7. package/dist/components/Recipient/MemberRecipientPermissions.js +80 -20
  8. package/dist/components/Recipient/MemberRecipientPermissions.spec.js +214 -0
  9. package/dist/components/Recipient/actions/setReadOnlySharedPermission.js +0 -1
  10. package/dist/components/Recipient/actions/setReadOnlySharedPermission.spec.js +72 -0
  11. package/dist/components/Recipient/actions/setReadWriteSharedPermission.js +0 -1
  12. package/dist/components/Recipient/actions/setReadWriteSharedPermission.spec.js +72 -0
  13. package/dist/components/ShareDialogCozyToCozy.spec.js +9 -0
  14. package/dist/components/WhoHasAccess.js +0 -1
  15. package/locales/en.json +4 -1
  16. package/locales/fr.json +4 -1
  17. package/locales/ru.json +4 -1
  18. package/locales/vi.json +4 -1
  19. package/package.json +2 -2
  20. package/src/SharingProvider.jsx +51 -1
  21. package/src/SharingProvider.spec.jsx +94 -0
  22. package/src/components/Recipient/LinkRecipient.jsx +10 -4
  23. package/src/components/Recipient/LinkRecipient.stories.jsx +0 -1
  24. package/src/components/Recipient/MemberRecipient.spec.jsx +11 -0
  25. package/src/components/Recipient/MemberRecipientPermissions.jsx +69 -16
  26. package/src/components/Recipient/MemberRecipientPermissions.spec.jsx +165 -0
  27. package/src/components/Recipient/actions/setReadOnlySharedPermission.js +1 -1
  28. package/src/components/Recipient/actions/setReadOnlySharedPermission.spec.jsx +95 -0
  29. package/src/components/Recipient/actions/setReadWriteSharedPermission.js +1 -1
  30. package/src/components/Recipient/actions/setReadWriteSharedPermission.spec.jsx +95 -0
  31. package/src/components/ShareDialogCozyToCozy.spec.jsx +6 -0
  32. package/src/components/WhoHasAccess.jsx +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "28.5.0",
3
+ "version": "28.6.0",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -83,5 +83,5 @@
83
83
  "sideEffects": [
84
84
  "*.css"
85
85
  ],
86
- "gitHead": "598f2e27b115ca69eda056332c6ab91fa92096d3"
86
+ "gitHead": "64311a9d08372224b5214cbf49f7d658d93f9af9"
87
87
  }
@@ -94,7 +94,8 @@ export class SharingProvider extends Component {
94
94
  revokeAllRecipients: this.revokeAllRecipients,
95
95
  refresh: this.fetchAllSharings,
96
96
  hasWriteAccess: this.hasWriteAccess,
97
- renameSharedDrive: this.renameSharedDrive
97
+ renameSharedDrive: this.renameSharedDrive,
98
+ updateSharingMemberType: this.updateSharingMemberType
98
99
  }
99
100
  this.isPublic = props.isPublic ?? false
100
101
  this.realtime = null
@@ -339,6 +340,55 @@ export class SharingProvider extends Component {
339
340
  this.dispatch(revokeSelf(sharing))
340
341
  }
341
342
 
343
+ /**
344
+ * Update a sharing member's read_only field to change permission type
345
+ * @param {string} sharingId - The ID of the sharing
346
+ * @param {number} memberIndex - The index of the member in the sharing members array
347
+ * @param {string} newType - 'one-way' for read-only, 'two-way' for read-write
348
+ */
349
+ updateSharingMemberType = async (sharingId, memberIndex, newType) => {
350
+ const sharing = getSharingById(this.state, sharingId)
351
+ if (!sharing) throw new Error('Sharing not found')
352
+
353
+ const member = sharing.attributes.members[memberIndex]
354
+ if (!member) throw new Error('Member not found')
355
+
356
+ // Find the contact by email (same pattern as helpers/contacts.js)
357
+ const matchedContacts = await this.props.client
358
+ .collection('io.cozy.contacts')
359
+ .find(
360
+ {
361
+ email: { $elemMatch: { address: member.email } },
362
+ _id: { $gt: null }
363
+ },
364
+ { indexedFields: ['_id'] }
365
+ )
366
+
367
+ if (!matchedContacts.data.length) {
368
+ throw new Error(`Contact not found for member ${member.email}`)
369
+ }
370
+ const contact = matchedContacts.data[0]
371
+
372
+ // Revoke (API call only, no state dispatch to avoid UI flickering)
373
+ await this.sharingCol.revokeRecipient(sharing, memberIndex)
374
+
375
+ // Re-add with the new permission type
376
+ const readOnly = newType === 'one-way'
377
+ try {
378
+ await this.addRecipients({
379
+ document: sharing,
380
+ recipients: readOnly ? [] : [contact],
381
+ readOnlyRecipients: readOnly ? [contact] : []
382
+ })
383
+ } catch (error) {
384
+ log.error(
385
+ `Failed to re-add member ${member.email} after revocation. The member has been revoked but could not be re-added with the new permission type.`,
386
+ error
387
+ )
388
+ throw error
389
+ }
390
+ }
391
+
342
392
  shareByLink = async (document, options) => {
343
393
  const resp = document.driveId
344
394
  ? await this.createSharedDriveSharingLink(document, options)
@@ -234,6 +234,100 @@ describe('updateDocumentPermissions', () => {
234
234
  })
235
235
  })
236
236
 
237
+ describe('updateSharingMemberType', () => {
238
+ const mockContact = { _id: 'contact-1', email: [{ address: 'bob@bob.cozy' }] }
239
+ const mockSharing = {
240
+ id: 'sharing-123',
241
+ type: 'io.cozy.sharings',
242
+ attributes: {
243
+ members: [
244
+ { status: 'owner', email: 'owner@cozy.local' },
245
+ { status: 'ready', email: 'bob@bob.cozy' }
246
+ ]
247
+ }
248
+ }
249
+
250
+ let instance
251
+ let mockContactsCollection
252
+
253
+ beforeEach(() => {
254
+ mockContactsCollection = {
255
+ find: jest.fn().mockResolvedValue({ data: [mockContact] })
256
+ }
257
+
258
+ const mockClient = {
259
+ getStackClient: () => ({ uri: 'http://cozy.local' }),
260
+ collection: jest.fn().mockReturnValue(mockContactsCollection)
261
+ }
262
+
263
+ instance = new SharingProvider({ client: mockClient })
264
+ instance.state = {
265
+ ...instance.state,
266
+ sharings: [mockSharing]
267
+ }
268
+ instance.sharingCol = {
269
+ revokeRecipient: jest.fn().mockResolvedValue({})
270
+ }
271
+ jest.spyOn(instance, 'addRecipients').mockResolvedValue({})
272
+ })
273
+
274
+ it('should throw when sharing is not found', async () => {
275
+ await expect(
276
+ instance.updateSharingMemberType('unknown-id', 1, 'one-way')
277
+ ).rejects.toThrow('Sharing not found')
278
+ })
279
+
280
+ it('should throw when member is not found', async () => {
281
+ await expect(
282
+ instance.updateSharingMemberType('sharing-123', 99, 'one-way')
283
+ ).rejects.toThrow('Member not found')
284
+ })
285
+
286
+ it('should throw when contact is not found', async () => {
287
+ mockContactsCollection.find.mockResolvedValue({ data: [] })
288
+
289
+ await expect(
290
+ instance.updateSharingMemberType('sharing-123', 1, 'one-way')
291
+ ).rejects.toThrow('Contact not found')
292
+ })
293
+
294
+ it('should revoke and re-add with readOnly when switching to one-way', async () => {
295
+ await instance.updateSharingMemberType('sharing-123', 1, 'one-way')
296
+
297
+ expect(instance.sharingCol.revokeRecipient).toHaveBeenCalledWith(
298
+ mockSharing,
299
+ 1
300
+ )
301
+ expect(instance.addRecipients).toHaveBeenCalledWith({
302
+ document: mockSharing,
303
+ recipients: [],
304
+ readOnlyRecipients: [mockContact]
305
+ })
306
+ })
307
+
308
+ it('should revoke and re-add as read-write when switching to two-way', async () => {
309
+ await instance.updateSharingMemberType('sharing-123', 1, 'two-way')
310
+
311
+ expect(instance.sharingCol.revokeRecipient).toHaveBeenCalledWith(
312
+ mockSharing,
313
+ 1
314
+ )
315
+ expect(instance.addRecipients).toHaveBeenCalledWith({
316
+ document: mockSharing,
317
+ recipients: [mockContact],
318
+ readOnlyRecipients: []
319
+ })
320
+ })
321
+
322
+ it('should rethrow error if addRecipients fails after revocation', async () => {
323
+ instance.addRecipients.mockRejectedValue(new Error('Network error'))
324
+
325
+ await expect(
326
+ instance.updateSharingMemberType('sharing-123', 1, 'one-way')
327
+ ).rejects.toThrow('Network error')
328
+ })
329
+ })
330
+
237
331
  // TODO Convert with react-testing-library
238
332
  // describe('hasWriteAccess', () => {
239
333
  // it('tells if a doc is writtable', () => {
@@ -26,8 +26,7 @@ const LinkRecipient = props => {
26
26
  const { isMobile } = useBreakpoints()
27
27
  const { getDocumentPermissions } = useSharingContext()
28
28
 
29
- const { recipientConfirmationData, verifyRecipient, link, fadeIn, document } =
30
- props
29
+ const { recipientConfirmationData, verifyRecipient, fadeIn, document } = props
31
30
 
32
31
  const permissions = getDocumentPermissions(document?._id)
33
32
  const hasPassword = checkIsPermissionHasPassword(permissions)
@@ -44,7 +43,7 @@ const LinkRecipient = props => {
44
43
  ? t('Share.recipients.expires', {
45
44
  date: dateFormatted
46
45
  })
47
- : link
46
+ : ''
48
47
 
49
48
  const RightPart = recipientConfirmationData ? (
50
49
  <RecipientConfirm
@@ -74,7 +73,14 @@ const LinkRecipient = props => {
74
73
  }
75
74
 
76
75
  LinkRecipient.propTypes = {
77
- link: PropTypes.string.isRequired
76
+ recipientConfirmationData: PropTypes.oneOfType([
77
+ PropTypes.bool,
78
+ PropTypes.object
79
+ ]),
80
+ verifyRecipient: PropTypes.func,
81
+ fadeIn: PropTypes.bool,
82
+ document: PropTypes.object,
83
+ permissions: PropTypes.arrayOf(PropTypes.object)
78
84
  }
79
85
 
80
86
  export default LinkRecipient
@@ -5,7 +5,6 @@ const meta = {
5
5
  args: {
6
6
  recipientConfirmationData: false,
7
7
  verifyRecipient: () => true,
8
- link: 'test',
9
8
  fadeIn: false
10
9
  }
11
10
  }
@@ -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 = () => setMenuDisplayed(!isMenuDisplayed)
46
- const hideMenu = () => setMenuDisplayed(false)
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
- if (isOwner) {
51
- await onRevoke(document, sharingId, memberIndex)
52
- } else {
53
- await onRevokeSelf(document)
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 actions = makeActions([permission, divider, revokeMember], {
59
- client,
60
- t,
61
- type: type ?? 'one-way',
62
- isOwner,
63
- isSharedDrive,
64
- handleRevocation
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
+ })
@@ -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} button={false} onClick={onClick}>
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} button={false} onClick={onClick}>
11
+ <ActionsMenuItem {...props} ref={ref} onClick={onClick}>
12
12
  <ListItemIcon>
13
13
  <Radios checked={type === 'two-way'} />
14
14
  </ListItemIcon>