cozy-sharing 36.4.2 → 37.0.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 CHANGED
@@ -3,6 +3,23 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [37.0.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@36.5.0...cozy-sharing@37.0.0) (2026-07-16)
7
+
8
+ ### Features
9
+
10
+ - Display correct icon and colors for group avatar ([e630c7a](https://github.com/cozy/cozy-libs/commit/e630c7a9c9cc2d3b784c3bce1848bdcbbf257c25))
11
+ - Update cozy-ui ([8d1d2a1](https://github.com/cozy/cozy-libs/commit/8d1d2a1a32963e7b1fc8efdc07ca76f3e48a430b))
12
+
13
+ ### BREAKING CHANGES
14
+
15
+ - You need cozy-ui >= 140.5.0
16
+
17
+ # [36.5.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@36.4.2...cozy-sharing@36.5.0) (2026-07-09)
18
+
19
+ ### Features
20
+
21
+ - Extract PermissionTypeMenu and export it ([a7ee85a](https://github.com/cozy/cozy-libs/commit/a7ee85a4b87295b6c4276710f3e8090f95a6dbba))
22
+
6
23
  ## [36.4.2](https://github.com/cozy/cozy-libs/compare/cozy-sharing@36.4.1...cozy-sharing@36.4.2) (2026-07-09)
7
24
 
8
25
  **Note:** Version bump only for package cozy-sharing
@@ -88,7 +88,8 @@ var AvatarList = function AvatarList(_ref2) {
88
88
  "data-testid": "AvatarList-avatar".concat(recipient.status === 'owner' ? '-owner' : ''),
89
89
  key: recipient.index
90
90
  }, /*#__PURE__*/React.createElement(GroupAvatar, {
91
- size: size
91
+ size: size,
92
+ color: recipient.color
92
93
  }));
93
94
  }
94
95
 
@@ -1,16 +1,20 @@
1
- import { Icon, Team } from '@linagora/twake-icons';
2
1
  import React from 'react';
3
- import Avatar from 'cozy-ui/transpiled/react/Avatar';
2
+ import { Icon, Peoples } from '@linagora/twake-icons';
3
+ import Avatar, { colorToGradient } from 'cozy-ui/transpiled/react/Avatar';
4
+ import { COLORS } from 'cozy-ui/transpiled/react/ColorList/helpers';
5
+ var DEFAULT_COLOR = COLORS[4];
4
6
 
5
7
  var GroupAvatar = function GroupAvatar(_ref) {
6
8
  var size = _ref.size,
9
+ _ref$color = _ref.color,
10
+ color = _ref$color === void 0 ? DEFAULT_COLOR : _ref$color,
7
11
  className = _ref.className;
8
12
  return /*#__PURE__*/React.createElement(Avatar, {
9
13
  size: size,
10
- className: className,
11
- border: true
14
+ color: colorToGradient(color),
15
+ className: className
12
16
  }, /*#__PURE__*/React.createElement(Icon, {
13
- icon: Team
17
+ icon: Peoples
14
18
  }));
15
19
  };
16
20
 
@@ -18,7 +18,8 @@ export var ContactSuggestion = function ContactSuggestion(_ref) {
18
18
  return /*#__PURE__*/React.createElement(ListItem, {
19
19
  button: true
20
20
  }, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(GroupAvatar, {
21
- size: "m"
21
+ size: "m",
22
+ color: contactOrGroup.color
22
23
  })), /*#__PURE__*/React.createElement(ListItemText, {
23
24
  primary: contactOrGroup.name,
24
25
  secondary: t('Share.members.count', {
@@ -17,6 +17,7 @@ import { GroupAvatar } from '../Avatar/GroupAvatar';
17
17
 
18
18
  var GroupRecipient = function GroupRecipient(props) {
19
19
  var name = props.name,
20
+ color = props.color,
20
21
  members = props.members,
21
22
  fadeIn = props.fadeIn,
22
23
  owner = props.owner,
@@ -60,7 +61,8 @@ var GroupRecipient = function GroupRecipient(props) {
60
61
  gutters: isMobile ? 'default' : 'double',
61
62
  size: "small"
62
63
  }, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(GroupAvatar, {
63
- size: "m"
64
+ size: "m",
65
+ color: color
64
66
  })), /*#__PURE__*/React.createElement(ListItemText, {
65
67
  primary: /*#__PURE__*/React.createElement(Typography, {
66
68
  variant: "body1"
@@ -1,8 +1,8 @@
1
1
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
2
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  import _regeneratorRuntime from "@babel/runtime/regenerator";
4
- import { Icon, CrossCircleOutline } from '@linagora/twake-icons';
5
4
  import React, { useState, useRef } from 'react';
5
+ import { Icon, CrossCircleOutline } from '@linagora/twake-icons';
6
6
  import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu';
7
7
  import { makeActions } from 'cozy-ui/transpiled/react/ActionsMenu/Actions';
8
8
  import ActionsMenuMobileHeader from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuMobileHeader';
@@ -19,6 +19,7 @@ import { GroupAvatar } from '../Avatar/GroupAvatar';
19
19
 
20
20
  var GroupRecipientPermissions = function GroupRecipientPermissions(_ref) {
21
21
  var name = _ref.name,
22
+ color = _ref.color,
22
23
  isOwner = _ref.isOwner,
23
24
  isReadOnly = _ref.isReadOnly,
24
25
  sharingId = _ref.sharingId,
@@ -118,7 +119,8 @@ var GroupRecipientPermissions = function GroupRecipientPermissions(_ref) {
118
119
  autoClose: true,
119
120
  onClose: hideMenu
120
121
  }, /*#__PURE__*/React.createElement(ActionsMenuMobileHeader, null, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(GroupAvatar, {
121
- size: "m"
122
+ size: "m",
123
+ color: color
122
124
  })), /*#__PURE__*/React.createElement(ListItemText, {
123
125
  primary: name,
124
126
  primaryTypographyProps: {
@@ -1,22 +1,14 @@
1
1
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
2
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  import _regeneratorRuntime from "@babel/runtime/regenerator";
4
+ import React, { useState, useCallback } from 'react';
4
5
  import { Icon, CrossCircleOutline } from '@linagora/twake-icons';
5
- import React, { useState, useRef, useCallback } from 'react';
6
6
  import { useClient } from 'cozy-client';
7
- import minilog from 'cozy-minilog';
8
- import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu';
9
- import { makeActions } from 'cozy-ui/transpiled/react/ActionsMenu/Actions';
10
- import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton';
11
7
  import IconButton from 'cozy-ui/transpiled/react/IconButton';
12
8
  import Spinner from 'cozy-ui/transpiled/react/Spinner';
13
9
  import Typography from 'cozy-ui/transpiled/react/Typography';
14
- import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert';
15
10
  import { useI18n } from 'twake-i18n';
16
- import { setReadOnlySharedPermission } from './actions/setReadOnlySharedPermission';
17
- import { setReadWriteSharedPermission } from './actions/setReadWriteSharedPermission';
18
- import { useSharingContext } from '../../hooks/useSharingContext';
19
- var log = minilog('MemberRecipientPermissions');
11
+ import { PermissionTypeMenu } from './PermissionTypeMenu';
20
12
 
21
13
  var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
22
14
  var isOwner = _ref.isOwner,
@@ -35,36 +27,16 @@ var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
35
27
  var _useI18n = useI18n(),
36
28
  t = _useI18n.t;
37
29
 
38
- var buttonRef = useRef();
39
30
  var client = useClient();
40
31
 
41
- var _useSharingContext = useSharingContext(),
42
- updateSharingMemberType = _useSharingContext.updateSharingMemberType;
43
-
44
- var _useAlert = useAlert(),
45
- showAlert = _useAlert.showAlert;
46
-
47
32
  var _useState = useState(false),
48
33
  _useState2 = _slicedToArray(_useState, 2),
49
34
  revoking = _useState2[0],
50
35
  setRevoking = _useState2[1];
51
36
 
52
- var _useState3 = useState(false),
53
- _useState4 = _slicedToArray(_useState3, 2),
54
- isMenuDisplayed = _useState4[0],
55
- setMenuDisplayed = _useState4[1];
56
-
57
37
  var instanceMatchesClient = instance !== undefined && instance === client.options.uri;
58
38
  var contactIsOwner = status === 'owner';
59
39
  var shouldShowMenu = !isReadOnly && !revoking && !contactIsOwner && (instanceMatchesClient && !isOwner || canManageSharing);
60
- var toggleMenu = useCallback(function () {
61
- setMenuDisplayed(function (displayed) {
62
- return !displayed;
63
- });
64
- }, []);
65
- var hideMenu = useCallback(function () {
66
- setMenuDisplayed(false);
67
- }, []);
68
40
  var handleRevocation = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
69
41
  return _regeneratorRuntime.wrap(function _callee$(_context) {
70
42
  while (1) switch (_context.prev = _context.next) {
@@ -99,69 +71,12 @@ var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
99
71
  }
100
72
  }, _callee, null, [[1,, 9, 12]]);
101
73
  })), [document, instanceMatchesClient, isOwner, memberIndex, onRevoke, onRevokeSelf, sharingId]);
102
- var setType = useCallback( /*#__PURE__*/function () {
103
- var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(newType) {
104
- return _regeneratorRuntime.wrap(function _callee2$(_context2) {
105
- while (1) switch (_context2.prev = _context2.next) {
106
- case 0:
107
- if (!(newType === type)) {
108
- _context2.next = 3;
109
- break;
110
- }
111
-
112
- hideMenu();
113
- return _context2.abrupt("return");
114
-
115
- case 3:
116
- hideMenu();
117
- _context2.prev = 4;
118
- _context2.next = 7;
119
- return updateSharingMemberType(sharingId, memberIndex, newType);
120
-
121
- case 7:
122
- _context2.next = 13;
123
- break;
124
-
125
- case 9:
126
- _context2.prev = 9;
127
- _context2.t0 = _context2["catch"](4);
128
- log.error('Failed to change member permission type', _context2.t0);
129
- showAlert({
130
- message: t('Share.members.error.changePermission'),
131
- severity: 'error',
132
- variant: 'filled'
133
- });
134
-
135
- case 13:
136
- case "end":
137
- return _context2.stop();
138
- }
139
- }, _callee2, null, [[4, 9]]);
140
- }));
141
-
142
- return function (_x) {
143
- return _ref3.apply(this, arguments);
144
- };
145
- }(), [hideMenu, memberIndex, sharingId, showAlert, t, type, updateSharingMemberType]);
146
- var actions = makeActions([setReadOnlySharedPermission, setReadWriteSharedPermission], {
147
- t: t,
148
- type: type !== null && type !== void 0 ? type : 'one-way',
149
- setType: setType
150
- });
151
74
  return /*#__PURE__*/React.createElement(React.Fragment, null, revoking && /*#__PURE__*/React.createElement(Spinner, null), !shouldShowMenu && /*#__PURE__*/React.createElement(Typography, {
152
75
  variant: "body2"
153
- }, contactIsOwner ? t("Share.status.".concat(status)) : t("Share.type.".concat(type))), shouldShowMenu && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DropdownButton, {
154
- ref: buttonRef,
155
- "aria-controls": "simple-menu",
156
- "aria-haspopup": "true",
157
- onClick: toggleMenu,
158
- textVariant: "body2"
159
- }, t("Share.type.".concat(type))), /*#__PURE__*/React.createElement(ActionsMenu, {
160
- ref: buttonRef,
161
- open: isMenuDisplayed,
162
- actions: actions,
163
- autoClose: true,
164
- onClose: hideMenu
76
+ }, contactIsOwner ? t("Share.status.".concat(status)) : t("Share.type.".concat(type))), shouldShowMenu && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(PermissionTypeMenu, {
77
+ sharingId: sharingId,
78
+ memberIndex: memberIndex,
79
+ type: type !== null && type !== void 0 ? type : 'one-way'
165
80
  }), /*#__PURE__*/React.createElement(IconButton, {
166
81
  onClick: handleRevocation,
167
82
  size: "small",
@@ -1,7 +1,5 @@
1
- import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
1
  import _extends from "@babel/runtime/helpers/extends";
3
- import _regeneratorRuntime from "@babel/runtime/regenerator";
4
- import { render, fireEvent, screen, waitFor } from '@testing-library/react';
2
+ import { render } from '@testing-library/react';
5
3
  import React from 'react';
6
4
  import { createMockClient } from 'cozy-client';
7
5
  import MemberRecipientPermissions from './MemberRecipientPermissions';
@@ -79,121 +77,12 @@ describe('MemberRecipientPermissions component', function () {
79
77
  afterAll(function () {
80
78
  global.document.createRange = createRangeBackup;
81
79
  });
82
- it('should render the current permission type', function () {
83
- setup();
84
- expect(screen.getByRole('button', {
85
- name: 'Editor'
86
- })).toBeInTheDocument();
87
- });
88
- it('should call updateSharingMemberType when changing to one-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
89
- var _setup, getByRole;
90
-
91
- return _regeneratorRuntime.wrap(function _callee$(_context) {
92
- while (1) switch (_context.prev = _context.next) {
93
- case 0:
94
- mockUpdateSharingMemberType.mockResolvedValue(undefined);
95
- _setup = setup({
96
- type: 'two-way'
97
- }), getByRole = _setup.getByRole;
98
- fireEvent.click(getByRole('button', {
99
- name: 'Editor'
100
- }));
101
- fireEvent.click(getByRole('menuitem', {
102
- name: 'Viewer'
103
- }));
104
- expect(mockUpdateSharingMemberType).toHaveBeenCalledWith('sharing-123', 1, 'one-way');
105
-
106
- case 5:
107
- case "end":
108
- return _context.stop();
109
- }
110
- }, _callee);
111
- })));
112
- it('should call updateSharingMemberType when changing to two-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
113
- var _setup2, getByRole;
114
-
115
- return _regeneratorRuntime.wrap(function _callee2$(_context2) {
116
- while (1) switch (_context2.prev = _context2.next) {
117
- case 0:
118
- mockUpdateSharingMemberType.mockResolvedValue(undefined);
119
- _setup2 = setup({
120
- type: 'one-way'
121
- }), getByRole = _setup2.getByRole;
122
- fireEvent.click(getByRole('button', {
123
- name: 'Viewer'
124
- }));
125
- fireEvent.click(getByRole('menuitem', {
126
- name: 'Editor'
127
- }));
128
- expect(mockUpdateSharingMemberType).toHaveBeenCalledWith('sharing-123', 1, 'two-way');
129
-
130
- case 5:
131
- case "end":
132
- return _context2.stop();
133
- }
134
- }, _callee2);
135
- })));
136
- it('should show error alert when updateSharingMemberType fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
137
- var _setup3, getByRole;
138
-
139
- return _regeneratorRuntime.wrap(function _callee3$(_context3) {
140
- while (1) switch (_context3.prev = _context3.next) {
141
- case 0:
142
- mockUpdateSharingMemberType.mockRejectedValue(new Error('Network error'));
143
- _setup3 = setup({
144
- type: 'two-way'
145
- }), getByRole = _setup3.getByRole;
146
- fireEvent.click(getByRole('button', {
147
- name: 'Editor'
148
- }));
149
- fireEvent.click(getByRole('menuitem', {
150
- name: 'Viewer'
151
- }));
152
- _context3.next = 6;
153
- return waitFor(function () {
154
- expect(mockShowAlert).toHaveBeenCalledWith({
155
- message: 'An error occurred while changing the permissions. Please try again.',
156
- severity: 'error',
157
- variant: 'filled'
158
- });
159
- });
160
-
161
- case 6:
162
- case "end":
163
- return _context3.stop();
164
- }
165
- }, _callee3);
166
- })));
167
- it('should not call updateSharingMemberType when selecting same type', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
168
- var _setup4, getByRole;
169
-
170
- return _regeneratorRuntime.wrap(function _callee4$(_context4) {
171
- while (1) switch (_context4.prev = _context4.next) {
172
- case 0:
173
- mockUpdateSharingMemberType.mockResolvedValue(undefined);
174
- _setup4 = setup({
175
- type: 'two-way'
176
- }), getByRole = _setup4.getByRole;
177
- fireEvent.click(getByRole('button', {
178
- name: 'Editor'
179
- }));
180
- fireEvent.click(getByRole('menuitem', {
181
- name: 'Editor'
182
- }));
183
- expect(mockUpdateSharingMemberType).not.toHaveBeenCalled();
184
-
185
- case 5:
186
- case "end":
187
- return _context4.stop();
188
- }
189
- }, _callee4);
190
- })));
191
80
  it('should not render menu when user is not owner and not the shared drive contact', function () {
192
- var _setup5 = setup({
81
+ var _setup = setup({
193
82
  isOwner: false,
194
83
  instance: 'http://other-cozy.local:8080'
195
84
  }),
196
- queryByRole = _setup5.queryByRole;
85
+ queryByRole = _setup.queryByRole;
197
86
 
198
87
  expect(queryByRole('button', {
199
88
  name: 'Editor'
@@ -203,21 +92,21 @@ describe('MemberRecipientPermissions component', function () {
203
92
  })).not.toBeInTheDocument();
204
93
  });
205
94
  it('should render menu for owner', function () {
206
- var _setup6 = setup({
95
+ var _setup2 = setup({
207
96
  isOwner: true
208
97
  }),
209
- getByRole = _setup6.getByRole;
98
+ getByRole = _setup2.getByRole;
210
99
 
211
100
  expect(getByRole('button', {
212
101
  name: 'Editor'
213
102
  })).toBeInTheDocument();
214
103
  });
215
104
  it('should render menu for the user themselves', function () {
216
- var _setup7 = setup({
105
+ var _setup3 = setup({
217
106
  isOwner: false,
218
107
  instance: 'http://cozy.local:8080'
219
108
  }),
220
- getByRole = _setup7.getByRole;
109
+ getByRole = _setup3.getByRole;
221
110
 
222
111
  expect(getByRole('button', {
223
112
  name: 'Editor'
@@ -0,0 +1,104 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
4
+ import React, { useCallback, useRef, useState } from 'react';
5
+ import minilog from 'cozy-minilog';
6
+ import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu';
7
+ import { makeActions } from 'cozy-ui/transpiled/react/ActionsMenu/Actions';
8
+ import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton';
9
+ import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert';
10
+ import { useI18n } from 'twake-i18n';
11
+ import { setReadOnlySharedPermission } from './actions/setReadOnlySharedPermission';
12
+ import { setReadWriteSharedPermission } from './actions/setReadWriteSharedPermission';
13
+ import withLocales from '../../hoc/withLocales';
14
+ import { useSharingContext } from '../../hooks/useSharingContext';
15
+ var log = minilog('PermissionTypeMenu');
16
+
17
+ var PermissionTypeMenuComponent = function PermissionTypeMenuComponent(_ref) {
18
+ var sharingId = _ref.sharingId,
19
+ memberIndex = _ref.memberIndex,
20
+ type = _ref.type;
21
+
22
+ var _useI18n = useI18n(),
23
+ t = _useI18n.t;
24
+
25
+ var buttonRef = useRef();
26
+
27
+ var _useSharingContext = useSharingContext(),
28
+ updateSharingMemberType = _useSharingContext.updateSharingMemberType;
29
+
30
+ var _useAlert = useAlert(),
31
+ showAlert = _useAlert.showAlert;
32
+
33
+ var _useState = useState(false),
34
+ _useState2 = _slicedToArray(_useState, 2),
35
+ isMenuDisplayed = _useState2[0],
36
+ setMenuDisplayed = _useState2[1];
37
+
38
+ var hideMenu = useCallback(function () {
39
+ setMenuDisplayed(false);
40
+ }, []);
41
+ var setType = useCallback( /*#__PURE__*/function () {
42
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(newType) {
43
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
44
+ while (1) switch (_context.prev = _context.next) {
45
+ case 0:
46
+ hideMenu();
47
+
48
+ if (!(newType !== type)) {
49
+ _context.next = 11;
50
+ break;
51
+ }
52
+
53
+ _context.prev = 2;
54
+ _context.next = 5;
55
+ return updateSharingMemberType(sharingId, memberIndex, newType);
56
+
57
+ case 5:
58
+ _context.next = 11;
59
+ break;
60
+
61
+ case 7:
62
+ _context.prev = 7;
63
+ _context.t0 = _context["catch"](2);
64
+ log.error('Failed to change member permission type', _context.t0);
65
+ showAlert({
66
+ message: t('Share.members.error.changePermission'),
67
+ severity: 'error',
68
+ variant: 'filled'
69
+ });
70
+
71
+ case 11:
72
+ case "end":
73
+ return _context.stop();
74
+ }
75
+ }, _callee, null, [[2, 7]]);
76
+ }));
77
+
78
+ return function (_x) {
79
+ return _ref2.apply(this, arguments);
80
+ };
81
+ }(), [hideMenu, memberIndex, sharingId, showAlert, t, type, updateSharingMemberType]);
82
+ var actions = makeActions([setReadOnlySharedPermission, setReadWriteSharedPermission], {
83
+ t: t,
84
+ type: type !== null && type !== void 0 ? type : 'one-way',
85
+ setType: setType
86
+ });
87
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DropdownButton, {
88
+ ref: buttonRef,
89
+ "aria-controls": "simple-menu",
90
+ "aria-haspopup": "true",
91
+ onClick: function onClick() {
92
+ return setMenuDisplayed(true);
93
+ },
94
+ textVariant: "body2"
95
+ }, t("Share.type.".concat(type))), /*#__PURE__*/React.createElement(ActionsMenu, {
96
+ ref: buttonRef,
97
+ open: isMenuDisplayed,
98
+ actions: actions,
99
+ autoClose: true,
100
+ onClose: hideMenu
101
+ }));
102
+ };
103
+
104
+ export var PermissionTypeMenu = withLocales(PermissionTypeMenuComponent);
@@ -0,0 +1,180 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _extends from "@babel/runtime/helpers/extends";
3
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
4
+ import { render, fireEvent, screen, waitFor } from '@testing-library/react';
5
+ import React from 'react';
6
+ import { createMockClient } from 'cozy-client';
7
+ import { PermissionTypeMenu } from './PermissionTypeMenu';
8
+ import AppLike from '../../../test/AppLike';
9
+ var mockUpdateSharingMemberType = jest.fn();
10
+ var mockShowAlert = jest.fn();
11
+ jest.mock('../../hooks/useSharingContext', function () {
12
+ return {
13
+ useSharingContext: function useSharingContext() {
14
+ return {
15
+ updateSharingMemberType: mockUpdateSharingMemberType
16
+ };
17
+ }
18
+ };
19
+ });
20
+ jest.mock('cozy-ui/transpiled/react/providers/Alert', function () {
21
+ return {
22
+ __esModule: true,
23
+ default: function _default(_ref) {
24
+ var children = _ref.children;
25
+ return children;
26
+ },
27
+ useAlert: function useAlert() {
28
+ return {
29
+ showAlert: mockShowAlert
30
+ };
31
+ }
32
+ };
33
+ });
34
+ describe('PermissionTypeMenu component', function () {
35
+ var client = createMockClient({});
36
+ client.options = {
37
+ uri: 'http://cozy.local:8080'
38
+ };
39
+ var defaultProps = {
40
+ sharingId: 'sharing-123',
41
+ memberIndex: 1,
42
+ type: 'two-way'
43
+ };
44
+ beforeEach(function () {
45
+ jest.clearAllMocks();
46
+ });
47
+
48
+ var setup = function setup(props) {
49
+ return render( /*#__PURE__*/React.createElement(AppLike, {
50
+ client: client
51
+ }, /*#__PURE__*/React.createElement(PermissionTypeMenu, _extends({}, defaultProps, props))));
52
+ };
53
+
54
+ var createRangeBackup;
55
+ beforeAll(function () {
56
+ createRangeBackup = global.document.createRange;
57
+ global.document.createRange = jest.fn(function () {
58
+ return {
59
+ setStart: function setStart() {},
60
+ setEnd: function setEnd() {},
61
+ commonAncestorContainer: {
62
+ nodeName: 'BODY',
63
+ ownerDocument: document
64
+ }
65
+ };
66
+ });
67
+ });
68
+ afterAll(function () {
69
+ global.document.createRange = createRangeBackup;
70
+ });
71
+ it('should render the current permission type', function () {
72
+ setup();
73
+ expect(screen.getByRole('button', {
74
+ name: 'Editor'
75
+ })).toBeInTheDocument();
76
+ });
77
+ it('should call updateSharingMemberType when changing to one-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
78
+ var _setup, getByRole;
79
+
80
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
81
+ while (1) switch (_context.prev = _context.next) {
82
+ case 0:
83
+ mockUpdateSharingMemberType.mockResolvedValue(undefined);
84
+ _setup = setup({
85
+ type: 'two-way'
86
+ }), getByRole = _setup.getByRole;
87
+ fireEvent.click(getByRole('button', {
88
+ name: 'Editor'
89
+ }));
90
+ fireEvent.click(getByRole('menuitem', {
91
+ name: 'Viewer'
92
+ }));
93
+ expect(mockUpdateSharingMemberType).toHaveBeenCalledWith('sharing-123', 1, 'one-way');
94
+
95
+ case 5:
96
+ case "end":
97
+ return _context.stop();
98
+ }
99
+ }, _callee);
100
+ })));
101
+ it('should call updateSharingMemberType when changing to two-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
102
+ var _setup2, getByRole;
103
+
104
+ return _regeneratorRuntime.wrap(function _callee2$(_context2) {
105
+ while (1) switch (_context2.prev = _context2.next) {
106
+ case 0:
107
+ mockUpdateSharingMemberType.mockResolvedValue(undefined);
108
+ _setup2 = setup({
109
+ type: 'one-way'
110
+ }), getByRole = _setup2.getByRole;
111
+ fireEvent.click(getByRole('button', {
112
+ name: 'Viewer'
113
+ }));
114
+ fireEvent.click(getByRole('menuitem', {
115
+ name: 'Editor'
116
+ }));
117
+ expect(mockUpdateSharingMemberType).toHaveBeenCalledWith('sharing-123', 1, 'two-way');
118
+
119
+ case 5:
120
+ case "end":
121
+ return _context2.stop();
122
+ }
123
+ }, _callee2);
124
+ })));
125
+ it('should show error alert when updateSharingMemberType fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
126
+ var _setup3, getByRole;
127
+
128
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
129
+ while (1) switch (_context3.prev = _context3.next) {
130
+ case 0:
131
+ mockUpdateSharingMemberType.mockRejectedValue(new Error('Network error'));
132
+ _setup3 = setup({
133
+ type: 'two-way'
134
+ }), getByRole = _setup3.getByRole;
135
+ fireEvent.click(getByRole('button', {
136
+ name: 'Editor'
137
+ }));
138
+ fireEvent.click(getByRole('menuitem', {
139
+ name: 'Viewer'
140
+ }));
141
+ _context3.next = 6;
142
+ return waitFor(function () {
143
+ expect(mockShowAlert).toHaveBeenCalledWith({
144
+ message: 'An error occurred while changing the permissions. Please try again.',
145
+ severity: 'error',
146
+ variant: 'filled'
147
+ });
148
+ });
149
+
150
+ case 6:
151
+ case "end":
152
+ return _context3.stop();
153
+ }
154
+ }, _callee3);
155
+ })));
156
+ it('should not call updateSharingMemberType when selecting same type', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
157
+ var _setup4, getByRole;
158
+
159
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
160
+ while (1) switch (_context4.prev = _context4.next) {
161
+ case 0:
162
+ mockUpdateSharingMemberType.mockResolvedValue(undefined);
163
+ _setup4 = setup({
164
+ type: 'two-way'
165
+ }), getByRole = _setup4.getByRole;
166
+ fireEvent.click(getByRole('button', {
167
+ name: 'Editor'
168
+ }));
169
+ fireEvent.click(getByRole('menuitem', {
170
+ name: 'Editor'
171
+ }));
172
+ expect(mockUpdateSharingMemberType).not.toHaveBeenCalled();
173
+
174
+ case 5:
175
+ case "end":
176
+ return _context4.stop();
177
+ }
178
+ }, _callee4);
179
+ })));
180
+ });
@@ -188,7 +188,8 @@ var ShareAutosuggest = function ShareAutosuggest(_ref) {
188
188
  id: "recipient_".concat(idx),
189
189
  key: "key_recipient_".concat(idx),
190
190
  avatar: isContactGroup ? /*#__PURE__*/React.createElement(GroupAvatar, {
191
- size: "xs"
191
+ size: "xs",
192
+ color: recipient.color
192
193
  }) : /*#__PURE__*/React.createElement(Avatar, {
193
194
  size: "xs"
194
195
  }, avatarText),
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ export { ShareButtonWithRecipients } from './ShareButtonWithRecipients';
30
30
  export { DOCUMENT_TYPE } from './helpers/documentType';
31
31
  export { getRecipientsFromSharing } from './state';
32
32
  export { default as OpenSharingLinkButton } from './OpenSharingLinkButton';
33
+ export { PermissionTypeMenu } from './components/Recipient/PermissionTypeMenu';
33
34
  export { default as OpenSharingLinkFabButton } from './OpenSharingLinkFabButton';
34
35
  export { NativeFileSharingProvider, useNativeFileSharing } from './providers/NativeFileSharingProvider';
35
36
  export { createCozySharingLink } from './actions/createCozySharingLink';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "36.4.2",
3
+ "version": "37.0.0",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -58,8 +58,8 @@
58
58
  "cozy-device-helper": "^4.0.0",
59
59
  "cozy-intent": "^2.31.1",
60
60
  "cozy-minilog": "^3.10.1",
61
- "cozy-ui": "^139.0.2",
62
- "cozy-ui-plus": "^12.0.0",
61
+ "cozy-ui": "^140.5.0",
62
+ "cozy-ui-plus": "^12.0.2",
63
63
  "jest": "30.3.0",
64
64
  "jest-environment-jsdom": "30.3.0",
65
65
  "react": "16.12.0",
@@ -75,7 +75,7 @@
75
75
  "cozy-flags": ">=4.8.1",
76
76
  "cozy-intent": ">=2.29.1",
77
77
  "cozy-minilog": ">=3.9.1",
78
- "cozy-ui": ">=139.0.2",
78
+ "cozy-ui": ">=140.5.0",
79
79
  "cozy-ui-plus": ">=6.0.0",
80
80
  "prop-types": ">=15.7.2",
81
81
  "react": ">=16.12.0",
@@ -85,5 +85,5 @@
85
85
  "sideEffects": [
86
86
  "*.css"
87
87
  ],
88
- "gitHead": "c0a92f4b635bf8a840c583841d75480f53de1b03"
88
+ "gitHead": "58606cf6a38bd7dbfcf6708135c2a9df4a31f284"
89
89
  }
@@ -92,7 +92,7 @@ const AvatarList = ({
92
92
  }`}
93
93
  key={recipient.index}
94
94
  >
95
- <GroupAvatar size={size} />
95
+ <GroupAvatar size={size} color={recipient.color} />
96
96
  </span>
97
97
  )
98
98
  }
@@ -1,12 +1,15 @@
1
- import { Icon, Team } from '@linagora/twake-icons'
2
1
  import React from 'react'
3
2
 
4
- import Avatar from 'cozy-ui/transpiled/react/Avatar'
3
+ import { Icon, Peoples } from '@linagora/twake-icons'
4
+ import Avatar, { colorToGradient } from 'cozy-ui/transpiled/react/Avatar'
5
+ import { COLORS } from 'cozy-ui/transpiled/react/ColorList/helpers'
5
6
 
6
- const GroupAvatar = ({ size, className }) => {
7
+ const DEFAULT_COLOR = COLORS[4]
8
+
9
+ const GroupAvatar = ({ size, color = DEFAULT_COLOR, className }) => {
7
10
  return (
8
- <Avatar size={size} className={className} border>
9
- <Icon icon={Team} />
11
+ <Avatar size={size} color={colorToGradient(color)} className={className}>
12
+ <Icon icon={Peoples} />
10
13
  </Avatar>
11
14
  )
12
15
  }
@@ -18,7 +18,7 @@ export const ContactSuggestion = ({ contactOrGroup }) => {
18
18
  return (
19
19
  <ListItem button>
20
20
  <ListItemIcon>
21
- <GroupAvatar size="m" />
21
+ <GroupAvatar size="m" color={contactOrGroup.color} />
22
22
  </ListItemIcon>
23
23
  <ListItemText
24
24
  primary={contactOrGroup.name}
@@ -19,7 +19,7 @@ import {
19
19
  import { GroupAvatar } from '../Avatar/GroupAvatar'
20
20
 
21
21
  const GroupRecipient = props => {
22
- const { name, members, fadeIn, owner, isOwner } = props
22
+ const { name, color, members, fadeIn, owner, isOwner } = props
23
23
  const { t } = useI18n()
24
24
  const client = useClient()
25
25
  const { isMobile } = useBreakpoints()
@@ -52,7 +52,7 @@ const GroupRecipient = props => {
52
52
  size="small"
53
53
  >
54
54
  <ListItemIcon>
55
- <GroupAvatar size="m" />
55
+ <GroupAvatar size="m" color={color} />
56
56
  </ListItemIcon>
57
57
  <ListItemText
58
58
  primary={<Typography variant="body1">{name}</Typography>}
@@ -1,6 +1,6 @@
1
- import { Icon, CrossCircleOutline } from '@linagora/twake-icons'
2
1
  import React, { useState, useRef } from 'react'
3
2
 
3
+ import { Icon, CrossCircleOutline } from '@linagora/twake-icons'
4
4
  import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
5
5
  import { makeActions } from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
6
6
  import ActionsMenuMobileHeader from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuMobileHeader'
@@ -18,6 +18,7 @@ import { GroupAvatar } from '../Avatar/GroupAvatar'
18
18
 
19
19
  const GroupRecipientPermissions = ({
20
20
  name,
21
+ color,
21
22
  isOwner,
22
23
  isReadOnly,
23
24
  sharingId,
@@ -83,7 +84,7 @@ const GroupRecipientPermissions = ({
83
84
  >
84
85
  <ActionsMenuMobileHeader>
85
86
  <ListItemIcon>
86
- <GroupAvatar size="m" />
87
+ <GroupAvatar size="m" color={color} />
87
88
  </ListItemIcon>
88
89
  <ListItemText
89
90
  primary={name}
@@ -1,22 +1,13 @@
1
- import { Icon, CrossCircleOutline } from '@linagora/twake-icons'
2
- import React, { useState, useRef, useCallback } from 'react'
1
+ import React, { useState, useCallback } from 'react'
3
2
 
3
+ import { Icon, CrossCircleOutline } from '@linagora/twake-icons'
4
4
  import { useClient } from 'cozy-client'
5
- import minilog from 'cozy-minilog'
6
- import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
7
- import { makeActions } from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
8
- import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
9
5
  import IconButton from 'cozy-ui/transpiled/react/IconButton'
10
6
  import Spinner from 'cozy-ui/transpiled/react/Spinner'
11
7
  import Typography from 'cozy-ui/transpiled/react/Typography'
12
- import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
13
8
  import { useI18n } from 'twake-i18n'
14
9
 
15
- import { setReadOnlySharedPermission } from './actions/setReadOnlySharedPermission'
16
- import { setReadWriteSharedPermission } from './actions/setReadWriteSharedPermission'
17
- import { useSharingContext } from '../../hooks/useSharingContext'
18
-
19
- const log = minilog('MemberRecipientPermissions')
10
+ import { PermissionTypeMenu } from './PermissionTypeMenu'
20
11
 
21
12
  const MemberRecipientPermissions = ({
22
13
  isOwner,
@@ -32,13 +23,9 @@ const MemberRecipientPermissions = ({
32
23
  memberIndex
33
24
  }) => {
34
25
  const { t } = useI18n()
35
- const buttonRef = useRef()
36
26
  const client = useClient()
37
- const { updateSharingMemberType } = useSharingContext()
38
- const { showAlert } = useAlert()
39
27
 
40
28
  const [revoking, setRevoking] = useState(false)
41
- const [isMenuDisplayed, setMenuDisplayed] = useState(false)
42
29
 
43
30
  const instanceMatchesClient =
44
31
  instance !== undefined && instance === client.options.uri
@@ -49,13 +36,6 @@ const MemberRecipientPermissions = ({
49
36
  !contactIsOwner &&
50
37
  ((instanceMatchesClient && !isOwner) || canManageSharing)
51
38
 
52
- const toggleMenu = useCallback(() => {
53
- setMenuDisplayed(displayed => !displayed)
54
- }, [])
55
- const hideMenu = useCallback(() => {
56
- setMenuDisplayed(false)
57
- }, [])
58
-
59
39
  const handleRevocation = useCallback(async () => {
60
40
  setRevoking(true)
61
41
  try {
@@ -77,44 +57,6 @@ const MemberRecipientPermissions = ({
77
57
  sharingId
78
58
  ])
79
59
 
80
- const setType = useCallback(
81
- async newType => {
82
- if (newType === type) {
83
- hideMenu()
84
- return
85
- }
86
- hideMenu()
87
- try {
88
- await updateSharingMemberType(sharingId, memberIndex, newType)
89
- } catch (error) {
90
- log.error('Failed to change member permission type', error)
91
- showAlert({
92
- message: t('Share.members.error.changePermission'),
93
- severity: 'error',
94
- variant: 'filled'
95
- })
96
- }
97
- },
98
- [
99
- hideMenu,
100
- memberIndex,
101
- sharingId,
102
- showAlert,
103
- t,
104
- type,
105
- updateSharingMemberType
106
- ]
107
- )
108
-
109
- const actions = makeActions(
110
- [setReadOnlySharedPermission, setReadWriteSharedPermission],
111
- {
112
- t,
113
- type: type ?? 'one-way',
114
- setType
115
- }
116
- )
117
-
118
60
  return (
119
61
  <>
120
62
  {revoking && <Spinner />}
@@ -127,21 +69,10 @@ const MemberRecipientPermissions = ({
127
69
  )}
128
70
  {shouldShowMenu && (
129
71
  <>
130
- <DropdownButton
131
- ref={buttonRef}
132
- aria-controls="simple-menu"
133
- aria-haspopup="true"
134
- onClick={toggleMenu}
135
- textVariant="body2"
136
- >
137
- {t(`Share.type.${type}`)}
138
- </DropdownButton>
139
- <ActionsMenu
140
- ref={buttonRef}
141
- open={isMenuDisplayed}
142
- actions={actions}
143
- autoClose
144
- onClose={hideMenu}
72
+ <PermissionTypeMenu
73
+ sharingId={sharingId}
74
+ memberIndex={memberIndex}
75
+ type={type ?? 'one-way'}
145
76
  />
146
77
  <IconButton
147
78
  onClick={handleRevocation}
@@ -1,4 +1,4 @@
1
- import { render, fireEvent, screen, waitFor } from '@testing-library/react'
1
+ import { render } from '@testing-library/react'
2
2
  import React from 'react'
3
3
 
4
4
  import { createMockClient } from 'cozy-client'
@@ -72,70 +72,6 @@ describe('MemberRecipientPermissions component', () => {
72
72
  global.document.createRange = createRangeBackup
73
73
  })
74
74
 
75
- it('should render the current permission type', () => {
76
- setup()
77
- expect(screen.getByRole('button', { name: 'Editor' })).toBeInTheDocument()
78
- })
79
-
80
- it('should call updateSharingMemberType when changing to one-way', async () => {
81
- mockUpdateSharingMemberType.mockResolvedValue(undefined)
82
-
83
- const { getByRole } = setup({ type: 'two-way' })
84
-
85
- fireEvent.click(getByRole('button', { name: 'Editor' }))
86
- fireEvent.click(getByRole('menuitem', { name: 'Viewer' }))
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 { getByRole } = setup({ type: 'one-way' })
99
-
100
- fireEvent.click(getByRole('button', { name: 'Viewer' }))
101
- fireEvent.click(getByRole('menuitem', { name: 'Editor' }))
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 { getByRole } = setup({ type: 'two-way' })
114
-
115
- fireEvent.click(getByRole('button', { name: 'Editor' }))
116
- fireEvent.click(getByRole('menuitem', { name: 'Viewer' }))
117
-
118
- await waitFor(() => {
119
- expect(mockShowAlert).toHaveBeenCalledWith({
120
- message:
121
- 'An error occurred while changing the permissions. Please try again.',
122
- severity: 'error',
123
- variant: 'filled'
124
- })
125
- })
126
- })
127
-
128
- it('should not call updateSharingMemberType when selecting same type', async () => {
129
- mockUpdateSharingMemberType.mockResolvedValue(undefined)
130
-
131
- const { getByRole } = setup({ type: 'two-way' })
132
-
133
- fireEvent.click(getByRole('button', { name: 'Editor' }))
134
- fireEvent.click(getByRole('menuitem', { name: 'Editor' }))
135
-
136
- expect(mockUpdateSharingMemberType).not.toHaveBeenCalled()
137
- })
138
-
139
75
  it('should not render menu when user is not owner and not the shared drive contact', () => {
140
76
  const { queryByRole } = setup({
141
77
  isOwner: false,
@@ -0,0 +1,87 @@
1
+ import React, { useCallback, useRef, useState } from 'react'
2
+
3
+ import minilog from 'cozy-minilog'
4
+ import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
5
+ import { makeActions } from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
6
+ import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
7
+ import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
8
+ import { useI18n } from 'twake-i18n'
9
+
10
+ import { setReadOnlySharedPermission } from './actions/setReadOnlySharedPermission'
11
+ import { setReadWriteSharedPermission } from './actions/setReadWriteSharedPermission'
12
+ import withLocales from '../../hoc/withLocales'
13
+ import { useSharingContext } from '../../hooks/useSharingContext'
14
+
15
+ const log = minilog('PermissionTypeMenu')
16
+
17
+ const PermissionTypeMenuComponent = ({ sharingId, memberIndex, type }) => {
18
+ const { t } = useI18n()
19
+ const buttonRef = useRef()
20
+ const { updateSharingMemberType } = useSharingContext()
21
+ const { showAlert } = useAlert()
22
+
23
+ const [isMenuDisplayed, setMenuDisplayed] = useState(false)
24
+
25
+ const hideMenu = useCallback(() => {
26
+ setMenuDisplayed(false)
27
+ }, [])
28
+
29
+ const setType = useCallback(
30
+ async newType => {
31
+ hideMenu()
32
+ if (newType !== type) {
33
+ try {
34
+ await updateSharingMemberType(sharingId, memberIndex, newType)
35
+ } catch (error) {
36
+ log.error('Failed to change member permission type', error)
37
+ showAlert({
38
+ message: t('Share.members.error.changePermission'),
39
+ severity: 'error',
40
+ variant: 'filled'
41
+ })
42
+ }
43
+ }
44
+ },
45
+ [
46
+ hideMenu,
47
+ memberIndex,
48
+ sharingId,
49
+ showAlert,
50
+ t,
51
+ type,
52
+ updateSharingMemberType
53
+ ]
54
+ )
55
+
56
+ const actions = makeActions(
57
+ [setReadOnlySharedPermission, setReadWriteSharedPermission],
58
+ {
59
+ t,
60
+ type: type ?? 'one-way',
61
+ setType
62
+ }
63
+ )
64
+
65
+ return (
66
+ <>
67
+ <DropdownButton
68
+ ref={buttonRef}
69
+ aria-controls="simple-menu"
70
+ aria-haspopup="true"
71
+ onClick={() => setMenuDisplayed(true)}
72
+ textVariant="body2"
73
+ >
74
+ {t(`Share.type.${type}`)}
75
+ </DropdownButton>
76
+ <ActionsMenu
77
+ ref={buttonRef}
78
+ open={isMenuDisplayed}
79
+ actions={actions}
80
+ autoClose
81
+ onClose={hideMenu}
82
+ />
83
+ </>
84
+ )
85
+ }
86
+
87
+ export const PermissionTypeMenu = withLocales(PermissionTypeMenuComponent)
@@ -0,0 +1,131 @@
1
+ import { render, fireEvent, screen, waitFor } from '@testing-library/react'
2
+ import React from 'react'
3
+
4
+ import { createMockClient } from 'cozy-client'
5
+
6
+ import { PermissionTypeMenu } from './PermissionTypeMenu'
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('PermissionTypeMenu component', () => {
27
+ const client = createMockClient({})
28
+ client.options = {
29
+ uri: 'http://cozy.local:8080'
30
+ }
31
+
32
+ const defaultProps = {
33
+ sharingId: 'sharing-123',
34
+ memberIndex: 1,
35
+ type: 'two-way'
36
+ }
37
+
38
+ beforeEach(() => {
39
+ jest.clearAllMocks()
40
+ })
41
+
42
+ const setup = props => {
43
+ return render(
44
+ <AppLike client={client}>
45
+ <PermissionTypeMenu {...defaultProps} {...props} />
46
+ </AppLike>
47
+ )
48
+ }
49
+
50
+ let createRangeBackup
51
+
52
+ beforeAll(() => {
53
+ createRangeBackup = global.document.createRange
54
+ global.document.createRange = jest.fn(() => ({
55
+ setStart: () => {},
56
+ setEnd: () => {},
57
+ commonAncestorContainer: {
58
+ nodeName: 'BODY',
59
+ ownerDocument: document
60
+ }
61
+ }))
62
+ })
63
+
64
+ afterAll(() => {
65
+ global.document.createRange = createRangeBackup
66
+ })
67
+
68
+ it('should render the current permission type', () => {
69
+ setup()
70
+ expect(screen.getByRole('button', { name: 'Editor' })).toBeInTheDocument()
71
+ })
72
+
73
+ it('should call updateSharingMemberType when changing to one-way', async () => {
74
+ mockUpdateSharingMemberType.mockResolvedValue(undefined)
75
+
76
+ const { getByRole } = setup({ type: 'two-way' })
77
+
78
+ fireEvent.click(getByRole('button', { name: 'Editor' }))
79
+ fireEvent.click(getByRole('menuitem', { name: 'Viewer' }))
80
+
81
+ expect(mockUpdateSharingMemberType).toHaveBeenCalledWith(
82
+ 'sharing-123',
83
+ 1,
84
+ 'one-way'
85
+ )
86
+ })
87
+
88
+ it('should call updateSharingMemberType when changing to two-way', async () => {
89
+ mockUpdateSharingMemberType.mockResolvedValue(undefined)
90
+
91
+ const { getByRole } = setup({ type: 'one-way' })
92
+
93
+ fireEvent.click(getByRole('button', { name: 'Viewer' }))
94
+ fireEvent.click(getByRole('menuitem', { name: 'Editor' }))
95
+
96
+ expect(mockUpdateSharingMemberType).toHaveBeenCalledWith(
97
+ 'sharing-123',
98
+ 1,
99
+ 'two-way'
100
+ )
101
+ })
102
+
103
+ it('should show error alert when updateSharingMemberType fails', async () => {
104
+ mockUpdateSharingMemberType.mockRejectedValue(new Error('Network error'))
105
+
106
+ const { getByRole } = setup({ type: 'two-way' })
107
+
108
+ fireEvent.click(getByRole('button', { name: 'Editor' }))
109
+ fireEvent.click(getByRole('menuitem', { name: 'Viewer' }))
110
+
111
+ await waitFor(() => {
112
+ expect(mockShowAlert).toHaveBeenCalledWith({
113
+ message:
114
+ 'An error occurred while changing the permissions. Please try again.',
115
+ severity: 'error',
116
+ variant: 'filled'
117
+ })
118
+ })
119
+ })
120
+
121
+ it('should not call updateSharingMemberType when selecting same type', async () => {
122
+ mockUpdateSharingMemberType.mockResolvedValue(undefined)
123
+
124
+ const { getByRole } = setup({ type: 'two-way' })
125
+
126
+ fireEvent.click(getByRole('button', { name: 'Editor' }))
127
+ fireEvent.click(getByRole('menuitem', { name: 'Editor' }))
128
+
129
+ expect(mockUpdateSharingMemberType).not.toHaveBeenCalled()
130
+ })
131
+ })
@@ -161,7 +161,7 @@ const ShareAutosuggest = ({
161
161
  key={`key_recipient_${idx}`}
162
162
  avatar={
163
163
  isContactGroup ? (
164
- <GroupAvatar size="xs" />
164
+ <GroupAvatar size="xs" color={recipient.color} />
165
165
  ) : (
166
166
  <Avatar size="xs">{avatarText}</Avatar>
167
167
  )
package/src/index.jsx CHANGED
@@ -34,6 +34,7 @@ export { ShareButtonWithRecipients } from './ShareButtonWithRecipients'
34
34
  export { DOCUMENT_TYPE } from './helpers/documentType'
35
35
  export { getRecipientsFromSharing } from './state'
36
36
  export { default as OpenSharingLinkButton } from './OpenSharingLinkButton'
37
+ export { PermissionTypeMenu } from './components/Recipient/PermissionTypeMenu'
37
38
  export { default as OpenSharingLinkFabButton } from './OpenSharingLinkFabButton'
38
39
  export {
39
40
  NativeFileSharingProvider,