cozy-sharing 36.4.1 → 36.5.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 +10 -0
- package/dist/components/Recipient/MemberRecipientPermissions.js +6 -91
- package/dist/components/Recipient/MemberRecipientPermissions.spec.js +7 -118
- package/dist/components/Recipient/PermissionTypeMenu.js +104 -0
- package/dist/components/Recipient/PermissionTypeMenu.spec.js +180 -0
- package/dist/index.js +1 -0
- package/package.json +3 -3
- package/src/components/Recipient/MemberRecipientPermissions.jsx +7 -76
- package/src/components/Recipient/MemberRecipientPermissions.spec.jsx +1 -65
- package/src/components/Recipient/PermissionTypeMenu.jsx +87 -0
- package/src/components/Recipient/PermissionTypeMenu.spec.jsx +131 -0
- package/src/index.jsx +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
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
|
+
# [36.5.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@36.4.2...cozy-sharing@36.5.0) (2026-07-09)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- Extract PermissionTypeMenu and export it ([a7ee85a](https://github.com/cozy/cozy-libs/commit/a7ee85a4b87295b6c4276710f3e8090f95a6dbba))
|
|
11
|
+
|
|
12
|
+
## [36.4.2](https://github.com/cozy/cozy-libs/compare/cozy-sharing@36.4.1...cozy-sharing@36.4.2) (2026-07-09)
|
|
13
|
+
|
|
14
|
+
**Note:** Version bump only for package cozy-sharing
|
|
15
|
+
|
|
6
16
|
## [36.4.1](https://github.com/cozy/cozy-libs/compare/cozy-sharing@36.4.0...cozy-sharing@36.4.1) (2026-07-08)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package cozy-sharing
|
|
@@ -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 {
|
|
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(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
|
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
|
|
81
|
+
var _setup = setup({
|
|
193
82
|
isOwner: false,
|
|
194
83
|
instance: 'http://other-cozy.local:8080'
|
|
195
84
|
}),
|
|
196
|
-
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
|
|
95
|
+
var _setup2 = setup({
|
|
207
96
|
isOwner: true
|
|
208
97
|
}),
|
|
209
|
-
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
|
|
105
|
+
var _setup3 = setup({
|
|
217
106
|
isOwner: false,
|
|
218
107
|
instance: 'http://cozy.local:8080'
|
|
219
108
|
}),
|
|
220
|
-
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
|
+
});
|
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.
|
|
3
|
+
"version": "36.5.0",
|
|
4
4
|
"description": "Provides sharing login for React applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Cozy",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"cozy-intent": "^2.31.1",
|
|
60
60
|
"cozy-minilog": "^3.10.1",
|
|
61
61
|
"cozy-ui": "^139.0.2",
|
|
62
|
-
"cozy-ui-plus": "^
|
|
62
|
+
"cozy-ui-plus": "^12.0.1",
|
|
63
63
|
"jest": "30.3.0",
|
|
64
64
|
"jest-environment-jsdom": "30.3.0",
|
|
65
65
|
"react": "16.12.0",
|
|
@@ -85,5 +85,5 @@
|
|
|
85
85
|
"sideEffects": [
|
|
86
86
|
"*.css"
|
|
87
87
|
],
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "8f766c3da5a35109325e1a3451e78e7c1b30e515"
|
|
89
89
|
}
|
|
@@ -1,22 +1,13 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
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
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
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
|
+
})
|
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,
|