cozy-ui 62.7.0 → 62.9.1
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 +28 -0
- package/package.json +9 -3
- package/react/ContactsListModal/AddContactButton.jsx +3 -1
- package/react/FilePicker/FilePickerBody.jsx +34 -36
- package/react/FilePicker/FilePickerBodyItem.jsx +27 -23
- package/react/FilePicker/FilePickerBodyItem.spec.jsx +54 -22
- package/react/FilePicker/Readme.md +9 -12
- package/react/FilePicker/index.jsx +11 -26
- package/react/Viewer/AudioViewer.jsx +7 -2
- package/react/Viewer/Navigation.jsx +1 -1
- package/react/Viewer/__snapshots__/AudioViewer.spec.jsx.snap +1 -0
- package/react/helpers/acceptedTypes.js +50 -0
- package/react/helpers/acceptedTypes.spec.js +86 -0
- package/react/helpers/makeStyles.js +2 -0
- package/react/index.js +1 -0
- package/transpiled/react/ContactsListModal/AddContactButton.js +3 -1
- package/transpiled/react/FilePicker/FilePickerBody.js +33 -37
- package/transpiled/react/FilePicker/FilePickerBodyItem.js +21 -21
- package/transpiled/react/FilePicker/index.js +12 -25
- package/transpiled/react/Viewer/AudioViewer.js +4 -2
- package/transpiled/react/Viewer/Navigation.js +1 -1
- package/transpiled/react/helpers/acceptedTypes.js +42 -0
- package/transpiled/react/helpers/makeStyles.js +2 -0
- package/transpiled/react/index.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
## [62.9.1](https://github.com/cozy/cozy-ui/compare/v62.9.0...v62.9.1) (2022-04-21)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* bump convict from 6.0.0 to 6.2.2 ([403c38e](https://github.com/cozy/cozy-ui/commit/403c38e))
|
|
7
|
+
|
|
8
|
+
# [62.9.0](https://github.com/cozy/cozy-ui/compare/v62.8.0...v62.9.0) (2022-04-20)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* Add makeStyles helper from Mui ([7962227](https://github.com/cozy/cozy-ui/commit/7962227))
|
|
14
|
+
|
|
15
|
+
# [62.8.0](https://github.com/cozy/cozy-ui/compare/v62.7.0...v62.8.0) (2022-04-19)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **deps:** Piwik-react-router should be a dependency ([ba6130b](https://github.com/cozy/cozy-ui/commit/ba6130b))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* Add mime-types package ([cc96d4a](https://github.com/cozy/cozy-ui/commit/cc96d4a))
|
|
26
|
+
* Improve FilePicker component ([d52c45d](https://github.com/cozy/cozy-ui/commit/d52c45d)), closes [#2026](https://github.com/cozy/cozy-ui/issues/2026)
|
|
27
|
+
* Update documentation ([dbb6098](https://github.com/cozy/cozy-ui/commit/dbb6098))
|
|
28
|
+
|
|
1
29
|
# [62.7.0](https://github.com/cozy/cozy-ui/compare/v62.6.0...v62.7.0) (2022-04-14)
|
|
2
30
|
|
|
3
31
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-ui",
|
|
3
|
-
"version": "62.
|
|
3
|
+
"version": "62.9.1",
|
|
4
4
|
"description": "Cozy apps UI SDK",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -105,6 +105,7 @@
|
|
|
105
105
|
"enzyme-adapter-react-16": "1.15.6",
|
|
106
106
|
"enzyme-to-json": "3.6.2",
|
|
107
107
|
"eslint-config-cozy-app": "4.0.0",
|
|
108
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
108
109
|
"express": "^4.17.1",
|
|
109
110
|
"focus-trap-react": "^6.0.0",
|
|
110
111
|
"git-directory-deploy": "1.5.1",
|
|
@@ -121,7 +122,6 @@
|
|
|
121
122
|
"mini-css-extract-plugin": "0.6.0",
|
|
122
123
|
"nodemon": "1.19.4",
|
|
123
124
|
"npm-run-all": "4.1.5",
|
|
124
|
-
"piwik-react-router": "0.12.1",
|
|
125
125
|
"postcss-cli": "6.1.3",
|
|
126
126
|
"postcss-loader": "2.1.6",
|
|
127
127
|
"pretty": "2.0.0",
|
|
@@ -159,9 +159,11 @@
|
|
|
159
159
|
"filesize": "8.0.7",
|
|
160
160
|
"hammerjs": "^2.0.8",
|
|
161
161
|
"intersection-observer": "0.11.0",
|
|
162
|
+
"mime-types": "2.1.35",
|
|
162
163
|
"mui-bottom-sheet": "https://github.com/cozy/mui-bottom-sheet.git#v1.0.6",
|
|
163
164
|
"node-polyglot": "^2.2.2",
|
|
164
165
|
"normalize.css": "^8.0.0",
|
|
166
|
+
"piwik-react-router": "0.12.1",
|
|
165
167
|
"react-markdown": "^4.0.8",
|
|
166
168
|
"react-pdf": "^4.0.5",
|
|
167
169
|
"react-popper": "^2.2.3",
|
|
@@ -177,11 +179,15 @@
|
|
|
177
179
|
"cozy-harvest-lib": "^6.7.3",
|
|
178
180
|
"cozy-intent": ">=1.3.0",
|
|
179
181
|
"cozy-sharing": "^3.10.0",
|
|
180
|
-
"piwik-react-router": "0.12.1",
|
|
181
182
|
"puppeteer": "^1.20.0",
|
|
182
183
|
"react": "^16.8.6",
|
|
183
184
|
"react-dom": "^16.8.6"
|
|
184
185
|
},
|
|
186
|
+
"peerDependenciesMeta ": {
|
|
187
|
+
"puppeteer": {
|
|
188
|
+
"optional": true
|
|
189
|
+
}
|
|
190
|
+
},
|
|
185
191
|
"eslintConfig": {
|
|
186
192
|
"extends": [
|
|
187
193
|
"eslint-config-cozy-app"
|
|
@@ -40,7 +40,9 @@ const DumbAddContactButton = props => {
|
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<AppLinker
|
|
43
|
-
|
|
43
|
+
app={{
|
|
44
|
+
slug: installedApp ? installedApp.attributes.slug : 'store'
|
|
45
|
+
}}
|
|
44
46
|
href={href}
|
|
45
47
|
>
|
|
46
48
|
{({ onClick, href }) => (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback
|
|
1
|
+
import React, { useCallback } from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
|
|
4
4
|
import { models, useQuery } from 'cozy-client'
|
|
@@ -7,17 +7,18 @@ import LoadMore from '../LoadMore'
|
|
|
7
7
|
|
|
8
8
|
import { buildContentFolderQuery } from './queries'
|
|
9
9
|
import FilePickerBodyItem from './FilePickerBodyItem'
|
|
10
|
+
import { isValidFile } from '../helpers/acceptedTypes'
|
|
10
11
|
|
|
11
12
|
const {
|
|
12
|
-
file: { isDirectory
|
|
13
|
+
file: { isDirectory }
|
|
13
14
|
} = models
|
|
14
15
|
|
|
15
16
|
const FilePickerBody = ({
|
|
16
17
|
navigateTo,
|
|
17
18
|
folderId,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
onSelectItemId,
|
|
20
|
+
itemsIdsSelected,
|
|
21
|
+
itemTypesAccepted,
|
|
21
22
|
multiple
|
|
22
23
|
}) => {
|
|
23
24
|
const contentFolderQuery = buildContentFolderQuery(folderId)
|
|
@@ -27,68 +28,68 @@ const FilePickerBody = ({
|
|
|
27
28
|
)
|
|
28
29
|
|
|
29
30
|
const onCheck = useCallback(
|
|
30
|
-
|
|
31
|
-
const isChecked =
|
|
32
|
-
fileIdSelected => fileIdSelected ===
|
|
31
|
+
itemId => {
|
|
32
|
+
const isChecked = itemsIdsSelected.some(
|
|
33
|
+
fileIdSelected => fileIdSelected === itemId
|
|
33
34
|
)
|
|
34
35
|
if (isChecked) {
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
onSelectItemId(
|
|
37
|
+
itemsIdsSelected.filter(fileIdSelected => fileIdSelected !== itemId)
|
|
37
38
|
)
|
|
38
|
-
} else
|
|
39
|
+
} else onSelectItemId(prev => [...prev, itemId])
|
|
39
40
|
},
|
|
40
|
-
[
|
|
41
|
+
[itemsIdsSelected, onSelectItemId]
|
|
41
42
|
)
|
|
42
43
|
|
|
43
44
|
// When click on checkbox/radio area...
|
|
44
45
|
const handleChoiceClick = useCallback(
|
|
45
|
-
|
|
46
|
-
if (multiple) onCheck(
|
|
47
|
-
else
|
|
46
|
+
item => () => {
|
|
47
|
+
if (multiple) onCheck(item._id)
|
|
48
|
+
else onSelectItemId(item._id)
|
|
48
49
|
},
|
|
49
|
-
[multiple, onCheck,
|
|
50
|
+
[multiple, onCheck, onSelectItemId]
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
// ...when click anywhere on the rest of the line
|
|
53
54
|
const handleListItemClick = useCallback(
|
|
54
|
-
|
|
55
|
-
if (isDirectory(
|
|
56
|
-
navigateTo(contentFolder.find(
|
|
55
|
+
item => () => {
|
|
56
|
+
if (isDirectory(item)) {
|
|
57
|
+
navigateTo(contentFolder.find(it => it._id === item._id))
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
if (
|
|
60
|
-
if (multiple) onCheck(
|
|
61
|
-
else
|
|
60
|
+
if (isValidFile(item, itemTypesAccepted)) {
|
|
61
|
+
if (multiple) onCheck(item._id)
|
|
62
|
+
else onSelectItemId(item._id)
|
|
62
63
|
}
|
|
63
64
|
},
|
|
64
65
|
[
|
|
65
66
|
contentFolder,
|
|
66
|
-
|
|
67
|
+
itemTypesAccepted,
|
|
67
68
|
multiple,
|
|
68
69
|
navigateTo,
|
|
69
70
|
onCheck,
|
|
70
|
-
|
|
71
|
+
onSelectItemId
|
|
71
72
|
]
|
|
72
73
|
)
|
|
73
74
|
|
|
74
75
|
return (
|
|
75
76
|
<List>
|
|
76
77
|
{contentFolder &&
|
|
77
|
-
contentFolder.map((
|
|
78
|
+
contentFolder.map((item, idx) => {
|
|
78
79
|
const hasDivider = contentFolder
|
|
79
80
|
? idx !== contentFolder.length - 1
|
|
80
81
|
: false
|
|
81
82
|
|
|
82
83
|
return (
|
|
83
84
|
<FilePickerBodyItem
|
|
84
|
-
key={
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
key={item._id}
|
|
86
|
+
item={item}
|
|
87
|
+
itemTypesAccepted={itemTypesAccepted}
|
|
87
88
|
multiple={multiple}
|
|
88
89
|
handleChoiceClick={handleChoiceClick}
|
|
89
90
|
handleListItemClick={handleListItemClick}
|
|
90
91
|
onCheck={onCheck}
|
|
91
|
-
|
|
92
|
+
itemsIdsSelected={itemsIdsSelected}
|
|
92
93
|
hasDivider={hasDivider}
|
|
93
94
|
/>
|
|
94
95
|
)
|
|
@@ -99,14 +100,11 @@ const FilePickerBody = ({
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
FilePickerBody.propTypes = {
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
onSelectItemId: PropTypes.func.isRequired,
|
|
104
|
+
itemsIdsSelected: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
104
105
|
folderId: PropTypes.string.isRequired,
|
|
105
106
|
navigateTo: PropTypes.func.isRequired,
|
|
106
|
-
|
|
107
|
-
file: PropTypes.bool,
|
|
108
|
-
folder: PropTypes.bool
|
|
109
|
-
})
|
|
107
|
+
itemTypesAccepted: PropTypes.arrayOf(PropTypes.string).isRequired
|
|
110
108
|
}
|
|
111
109
|
|
|
112
|
-
export default
|
|
110
|
+
export default FilePickerBody
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import cx from 'classnames'
|
|
4
4
|
import filesize from 'filesize'
|
|
@@ -17,6 +17,8 @@ import Checkbox from '../Checkbox'
|
|
|
17
17
|
import Radio from '../Radios'
|
|
18
18
|
import { useI18n } from '../I18n'
|
|
19
19
|
|
|
20
|
+
import { isValidFile, isValidFolder } from '../helpers/acceptedTypes'
|
|
21
|
+
|
|
20
22
|
import styles from './styles.styl'
|
|
21
23
|
|
|
22
24
|
const {
|
|
@@ -37,49 +39,54 @@ const useStyles = makeStyles(() => ({
|
|
|
37
39
|
}))
|
|
38
40
|
|
|
39
41
|
const FilePickerBodyItem = ({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
item,
|
|
43
|
+
itemTypesAccepted,
|
|
42
44
|
multiple,
|
|
43
45
|
handleChoiceClick,
|
|
44
46
|
handleListItemClick,
|
|
45
|
-
|
|
47
|
+
itemsIdsSelected,
|
|
46
48
|
hasDivider
|
|
47
49
|
}) => {
|
|
48
50
|
const classes = useStyles()
|
|
49
51
|
const { f } = useI18n()
|
|
50
52
|
const hasChoice =
|
|
51
|
-
(
|
|
52
|
-
(
|
|
53
|
+
isValidFile(item, itemTypesAccepted) ||
|
|
54
|
+
isValidFolder(item, itemTypesAccepted)
|
|
53
55
|
|
|
54
56
|
const Input = multiple ? Checkbox : Radio
|
|
55
57
|
|
|
56
|
-
const listItemSecondaryContent = isFile(
|
|
57
|
-
? `${f(
|
|
58
|
+
const listItemSecondaryContent = isFile(item)
|
|
59
|
+
? `${f(item.updated_at, 'DD MMM YYYY')} - ${filesize(item.size, {
|
|
58
60
|
base: 10
|
|
59
61
|
})}`
|
|
60
62
|
: null
|
|
61
63
|
|
|
62
64
|
return (
|
|
63
65
|
<>
|
|
64
|
-
<ListItem
|
|
66
|
+
<ListItem
|
|
67
|
+
disabled={!hasChoice && isFile(item)}
|
|
68
|
+
button
|
|
69
|
+
className="u-p-0"
|
|
70
|
+
data-testid="list-item"
|
|
71
|
+
>
|
|
65
72
|
<div
|
|
66
73
|
data-testid="listitem-onclick"
|
|
67
74
|
className={styles['filePickerBreadcrumb-wrapper']}
|
|
68
|
-
onClick={handleListItemClick(
|
|
75
|
+
onClick={handleListItemClick(item)}
|
|
69
76
|
>
|
|
70
77
|
<ListItemIcon className={classes.listItemIcon}>
|
|
71
78
|
<Icon
|
|
72
|
-
icon={isDirectory(
|
|
79
|
+
icon={isDirectory(item) ? FileTypeFolder : FileTypeText}
|
|
73
80
|
width="32"
|
|
74
81
|
height="32"
|
|
75
82
|
/>
|
|
76
83
|
</ListItemIcon>
|
|
77
84
|
<ListItemText
|
|
78
|
-
primary={
|
|
85
|
+
primary={item.name}
|
|
79
86
|
secondary={listItemSecondaryContent}
|
|
80
87
|
/>
|
|
81
88
|
</div>
|
|
82
|
-
{isDirectory(
|
|
89
|
+
{isDirectory(item) && hasChoice && (
|
|
83
90
|
<Divider
|
|
84
91
|
orientation="vertical"
|
|
85
92
|
flexItem
|
|
@@ -89,15 +96,15 @@ const FilePickerBodyItem = ({
|
|
|
89
96
|
<div
|
|
90
97
|
data-testid="choice-onclick"
|
|
91
98
|
className="u-ph-1 u-pv-half u-h-2 u-flex u-flex-items-center"
|
|
92
|
-
onClick={hasChoice ? handleChoiceClick(
|
|
99
|
+
onClick={hasChoice ? handleChoiceClick(item) : undefined}
|
|
93
100
|
>
|
|
94
101
|
<Input
|
|
95
102
|
data-testid={multiple ? 'checkbox-btn' : 'radio-btn'}
|
|
96
103
|
onChange={() => {
|
|
97
104
|
// handled by onClick on the container
|
|
98
105
|
}}
|
|
99
|
-
checked={
|
|
100
|
-
value={
|
|
106
|
+
checked={itemsIdsSelected.includes(item._id)}
|
|
107
|
+
value={item._id}
|
|
101
108
|
className={cx('u-p-0', {
|
|
102
109
|
'u-o-100': hasChoice,
|
|
103
110
|
'u-o-0': !hasChoice
|
|
@@ -112,16 +119,13 @@ const FilePickerBodyItem = ({
|
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
FilePickerBodyItem.propTypes = {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
file: PropTypes.bool,
|
|
118
|
-
folder: PropTypes.bool
|
|
119
|
-
}),
|
|
122
|
+
item: PropTypes.object.isRequired,
|
|
123
|
+
itemTypesAccepted: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
120
124
|
multiple: PropTypes.bool,
|
|
121
125
|
handleChoiceClick: PropTypes.func.isRequired,
|
|
122
126
|
handleListItemClick: PropTypes.func.isRequired,
|
|
123
|
-
|
|
127
|
+
itemsIdsSelected: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
124
128
|
hasDivider: PropTypes.bool.isRequired
|
|
125
129
|
}
|
|
126
130
|
|
|
127
|
-
export default
|
|
131
|
+
export default FilePickerBodyItem
|
|
@@ -4,11 +4,13 @@ import filesize from 'filesize'
|
|
|
4
4
|
|
|
5
5
|
import DemoProvider from './docs/DemoProvider'
|
|
6
6
|
import FilePickerBodyItem from './FilePickerBodyItem'
|
|
7
|
+
import { isValidFile, isValidFolder } from '../helpers/acceptedTypes'
|
|
7
8
|
|
|
8
9
|
const mockFile01 = {
|
|
9
10
|
_id: '001',
|
|
10
11
|
type: 'file',
|
|
11
|
-
name: 'Filename',
|
|
12
|
+
name: 'Filename.pdf',
|
|
13
|
+
mime: 'application/pdf',
|
|
12
14
|
updated_at: '2021-01-01T12:00:00.000000+01:00'
|
|
13
15
|
}
|
|
14
16
|
const mockFolder01 = {
|
|
@@ -19,6 +21,11 @@ const mockFolder01 = {
|
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
jest.mock('filesize', () => jest.fn())
|
|
24
|
+
jest.mock('../helpers/acceptedTypes', () => ({
|
|
25
|
+
...jest.requireActual('../helpers/acceptedTypes'),
|
|
26
|
+
isValidFile: jest.fn(),
|
|
27
|
+
isValidFolder: jest.fn()
|
|
28
|
+
}))
|
|
22
29
|
|
|
23
30
|
describe('FilePickerBodyItem components:', () => {
|
|
24
31
|
const mockHandleChoiceClick = jest.fn()
|
|
@@ -26,19 +33,23 @@ describe('FilePickerBodyItem components:', () => {
|
|
|
26
33
|
filesize.mockReturnValue('111Ko')
|
|
27
34
|
|
|
28
35
|
const setup = ({
|
|
29
|
-
|
|
36
|
+
item = mockFile01,
|
|
30
37
|
multiple = false,
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
validFile = false,
|
|
39
|
+
validFolder = false
|
|
40
|
+
} = {}) => {
|
|
41
|
+
isValidFile.mockReturnValue(validFile)
|
|
42
|
+
isValidFolder.mockReturnValue(validFolder)
|
|
43
|
+
|
|
33
44
|
return render(
|
|
34
45
|
<DemoProvider>
|
|
35
46
|
<FilePickerBodyItem
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
item={item}
|
|
48
|
+
itemTypesAccepted={[]}
|
|
38
49
|
multiple={multiple}
|
|
39
50
|
handleChoiceClick={mockHandleChoiceClick}
|
|
40
51
|
handleListItemClick={mockHandleListItemClick}
|
|
41
|
-
|
|
52
|
+
itemsIdsSelected={[]}
|
|
42
53
|
hasDivider={false}
|
|
43
54
|
/>
|
|
44
55
|
</DemoProvider>
|
|
@@ -50,48 +61,71 @@ describe('FilePickerBodyItem components:', () => {
|
|
|
50
61
|
})
|
|
51
62
|
|
|
52
63
|
it('should be rendered correctly', () => {
|
|
53
|
-
const { container } = setup(
|
|
64
|
+
const { container } = setup()
|
|
54
65
|
|
|
55
66
|
expect(container).toBeDefined()
|
|
56
67
|
})
|
|
57
68
|
|
|
58
69
|
it('should display filename', () => {
|
|
59
|
-
const { getByText } = setup(
|
|
70
|
+
const { getByText } = setup()
|
|
60
71
|
|
|
61
|
-
expect(getByText('Filename'))
|
|
72
|
+
expect(getByText('Filename.pdf'))
|
|
62
73
|
})
|
|
63
74
|
|
|
64
75
|
it('should display foldername', () => {
|
|
65
|
-
const { getByText } = setup({
|
|
76
|
+
const { getByText } = setup({ item: mockFolder01 })
|
|
66
77
|
|
|
67
78
|
expect(getByText('Foldername'))
|
|
68
79
|
})
|
|
69
80
|
|
|
81
|
+
it("should item's line is not disabled when has not valid type & is File", () => {
|
|
82
|
+
const { getByTestId } = setup({
|
|
83
|
+
item: mockFile01,
|
|
84
|
+
validFile: false,
|
|
85
|
+
validFolder: false
|
|
86
|
+
})
|
|
87
|
+
const listItem = getByTestId('list-item')
|
|
88
|
+
|
|
89
|
+
expect(listItem).toHaveAttribute('aria-disabled', 'true')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("should item's line is not disabled when has not valid type & is Folder", () => {
|
|
93
|
+
const { getByTestId } = setup({
|
|
94
|
+
item: mockFolder01,
|
|
95
|
+
validFile: false,
|
|
96
|
+
validFolder: false
|
|
97
|
+
})
|
|
98
|
+
const listItem = getByTestId('list-item')
|
|
99
|
+
|
|
100
|
+
expect(listItem).toHaveAttribute('aria-disabled', 'false')
|
|
101
|
+
})
|
|
102
|
+
|
|
70
103
|
describe('Functions called', () => {
|
|
71
104
|
it('should call "handleChoiceClick" function when click on checkbox/radio area', () => {
|
|
72
|
-
const { getByTestId } = setup({})
|
|
105
|
+
const { getByTestId } = setup({ validFile: true })
|
|
73
106
|
fireEvent.click(getByTestId('choice-onclick'))
|
|
74
107
|
|
|
75
108
|
expect(mockHandleChoiceClick).toHaveBeenCalled()
|
|
76
109
|
})
|
|
77
110
|
|
|
78
111
|
it('should NOT call "handleChoiceClick" function when click on checkbox/radio area, if is Folder & not accepted', () => {
|
|
79
|
-
const { getByTestId } = setup({
|
|
112
|
+
const { getByTestId } = setup({
|
|
113
|
+
item: mockFolder01,
|
|
114
|
+
validFolder: false
|
|
115
|
+
})
|
|
80
116
|
fireEvent.click(getByTestId('choice-onclick'))
|
|
81
117
|
|
|
82
118
|
expect(mockHandleChoiceClick).not.toHaveBeenCalled()
|
|
83
119
|
})
|
|
84
120
|
it('should NOT call "handleChoiceClick" function when click on checkbox/radio area, if is File & not accepted', () => {
|
|
85
|
-
const { getByTestId } = setup({
|
|
86
|
-
fileTypesAccepted: { file: false, folder: true }
|
|
87
|
-
})
|
|
121
|
+
const { getByTestId } = setup({ validFile: false })
|
|
88
122
|
fireEvent.click(getByTestId('choice-onclick'))
|
|
89
123
|
|
|
90
124
|
expect(mockHandleChoiceClick).not.toHaveBeenCalled()
|
|
91
125
|
})
|
|
92
126
|
|
|
93
127
|
it('should call "handleListItemClick" function when click on ListItem node', () => {
|
|
94
|
-
const { getByTestId } = setup(
|
|
128
|
+
const { getByTestId } = setup()
|
|
95
129
|
fireEvent.click(getByTestId('listitem-onclick'))
|
|
96
130
|
|
|
97
131
|
expect(mockHandleListItemClick).toHaveBeenCalled()
|
|
@@ -100,7 +134,7 @@ describe('FilePickerBodyItem components:', () => {
|
|
|
100
134
|
|
|
101
135
|
describe('Attribute "multiple"', () => {
|
|
102
136
|
it('should radio button exists if "multiple" atribute is False', () => {
|
|
103
|
-
const { getByTestId } = setup(
|
|
137
|
+
const { getByTestId } = setup()
|
|
104
138
|
const radioBtn = getByTestId('radio-btn')
|
|
105
139
|
expect(radioBtn).not.toBeNull()
|
|
106
140
|
})
|
|
@@ -114,16 +148,14 @@ describe('FilePickerBodyItem components:', () => {
|
|
|
114
148
|
|
|
115
149
|
describe('Radio/Checkbox button', () => {
|
|
116
150
|
it('should disable and not display the Radio button if it is a File and is not accepted', () => {
|
|
117
|
-
const { getByTestId } = setup({
|
|
118
|
-
fileTypesAccepted: { file: false }
|
|
119
|
-
})
|
|
151
|
+
const { getByTestId } = setup({ validFile: false })
|
|
120
152
|
const radioBtn = getByTestId('radio-btn')
|
|
121
153
|
|
|
122
154
|
expect(radioBtn.getAttribute('disabled')).toBe(null)
|
|
123
155
|
})
|
|
124
156
|
|
|
125
157
|
it('should disable and not display the Radio button if it is a Folder and is not accepted', () => {
|
|
126
|
-
const { getByTestId } = setup({
|
|
158
|
+
const { getByTestId } = setup({ item: mockFolder01 })
|
|
127
159
|
const radioBtn = getByTestId('radio-btn')
|
|
128
160
|
|
|
129
161
|
expect(radioBtn.getAttribute('disabled')).toBe(null)
|
|
@@ -12,7 +12,7 @@ initialState = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
const initialVariants = [
|
|
15
|
-
{
|
|
15
|
+
{ acceptFileTXT: false, acceptFileMD: false, acceptFolder: false, multiple: false }
|
|
16
16
|
];
|
|
17
17
|
|
|
18
18
|
const toggleFilePicker = () => setState({ filePickerOpened: !state.filePickerOpened });
|
|
@@ -20,18 +20,15 @@ const onChange = (fileId) => alert(`ID of file selected : [${fileId}]`);
|
|
|
20
20
|
<DemoProvider>
|
|
21
21
|
<Variants initialVariants={initialVariants} screenshotAllVariants>
|
|
22
22
|
{variant => {
|
|
23
|
-
|
|
24
|
-
if (variant.
|
|
25
|
-
acceptRule
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
const acceptRule = []
|
|
24
|
+
if (variant.acceptFileMD) {
|
|
25
|
+
acceptRule.push('.md')
|
|
26
|
+
}
|
|
27
|
+
if (variant.acceptFileTXT) {
|
|
28
|
+
acceptRule.push('.txt')
|
|
29
29
|
}
|
|
30
30
|
if (variant.acceptFolder) {
|
|
31
|
-
acceptRule
|
|
32
|
-
if (variant.acceptFile) {
|
|
33
|
-
acceptRule = 'folder,file'
|
|
34
|
-
}
|
|
31
|
+
acceptRule.push('folder')
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
return (
|
|
@@ -41,7 +38,7 @@ const onChange = (fileId) => alert(`ID of file selected : [${fileId}]`);
|
|
|
41
38
|
<FilePicker
|
|
42
39
|
onClose={toggleFilePicker}
|
|
43
40
|
onChange={onChange}
|
|
44
|
-
accept={acceptRule}
|
|
41
|
+
accept={acceptRule.toString()}
|
|
45
42
|
multiple={variant.multiple}
|
|
46
43
|
/>
|
|
47
44
|
)}
|
|
@@ -7,22 +7,7 @@ import FilePickerHeader from './FilePickerHeader'
|
|
|
7
7
|
import FilePickerFooter from './FilePickerFooter'
|
|
8
8
|
import FilePickerBody from './FilePickerBody'
|
|
9
9
|
import useBreakpoints from '../hooks/useBreakpoints'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @param {string} fileType - Defines the file types that should be accepted ("file" and/or "folder"), separated by commas
|
|
13
|
-
* @returns {{ file: boolean, folder: boolean }}
|
|
14
|
-
*/
|
|
15
|
-
const getFileTypesAccepted = fileType => {
|
|
16
|
-
// If accept is falsy, set default to file
|
|
17
|
-
const acceptedFileType = fileType
|
|
18
|
-
? fileType.replaceAll(' ', '').split(',')
|
|
19
|
-
: ['file']
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
file: acceptedFileType.includes('file'),
|
|
23
|
-
folder: acceptedFileType.includes('folder')
|
|
24
|
-
}
|
|
25
|
-
}
|
|
10
|
+
import { getCompliantTypes } from '../helpers/acceptedTypes'
|
|
26
11
|
|
|
27
12
|
const useStyles = makeStyles(() => ({
|
|
28
13
|
paper: {
|
|
@@ -36,23 +21,23 @@ const FilePicker = ({ onClose, onChange, accept, multiple }) => {
|
|
|
36
21
|
const { isMobile } = useBreakpoints()
|
|
37
22
|
const classes = useStyles()
|
|
38
23
|
const [folderId, setFolderId] = useState(ROOT_DIR_ID)
|
|
39
|
-
const [
|
|
24
|
+
const [itemsIdsSelected, setItemsIdsSelected] = useState([])
|
|
40
25
|
|
|
41
|
-
const
|
|
26
|
+
const onSelectItemId = fileId => {
|
|
42
27
|
if (!multiple) {
|
|
43
28
|
handleConfirm(null, fileId)
|
|
44
29
|
} else {
|
|
45
|
-
|
|
30
|
+
setItemsIdsSelected(fileId)
|
|
46
31
|
}
|
|
47
32
|
}
|
|
48
33
|
|
|
49
34
|
const navigateTo = folder => setFolderId(folder.id)
|
|
50
35
|
|
|
51
36
|
const handleConfirm = (_, fileId) => {
|
|
52
|
-
onChange(fileId ? fileId :
|
|
37
|
+
onChange(fileId ? fileId : itemsIdsSelected)
|
|
53
38
|
onClose()
|
|
54
39
|
}
|
|
55
|
-
const
|
|
40
|
+
const itemTypesAccepted = getCompliantTypes(accept)
|
|
56
41
|
|
|
57
42
|
return (
|
|
58
43
|
<FixedDialog
|
|
@@ -73,10 +58,10 @@ const FilePicker = ({ onClose, onChange, accept, multiple }) => {
|
|
|
73
58
|
content={
|
|
74
59
|
<FilePickerBody
|
|
75
60
|
navigateTo={navigateTo}
|
|
76
|
-
|
|
77
|
-
|
|
61
|
+
onSelectItemId={onSelectItemId}
|
|
62
|
+
itemsIdsSelected={itemsIdsSelected}
|
|
78
63
|
folderId={folderId}
|
|
79
|
-
|
|
64
|
+
itemTypesAccepted={itemTypesAccepted}
|
|
80
65
|
multiple={multiple}
|
|
81
66
|
/>
|
|
82
67
|
}
|
|
@@ -85,7 +70,7 @@ const FilePicker = ({ onClose, onChange, accept, multiple }) => {
|
|
|
85
70
|
<FilePickerFooter
|
|
86
71
|
onClose={onClose}
|
|
87
72
|
onConfirm={handleConfirm}
|
|
88
|
-
disabledConfirm={
|
|
73
|
+
disabledConfirm={itemsIdsSelected.length === 0}
|
|
89
74
|
/>
|
|
90
75
|
) : null
|
|
91
76
|
}
|
|
@@ -101,7 +86,7 @@ FilePicker.propTypes = {
|
|
|
101
86
|
}
|
|
102
87
|
|
|
103
88
|
FilePicker.defaultProps = {
|
|
104
|
-
accept: '
|
|
89
|
+
accept: '',
|
|
105
90
|
multiple: false
|
|
106
91
|
}
|
|
107
92
|
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import Icon from '../Icon'
|
|
3
|
+
import FileTypeAudioIcon from '../Icons/FileTypeAudio'
|
|
3
4
|
|
|
4
5
|
import withFileUrl from './withFileUrl'
|
|
5
6
|
import styles from './styles.styl'
|
|
6
7
|
|
|
7
|
-
import
|
|
8
|
+
import isTesting from '../../react/helpers/isTesting'
|
|
8
9
|
|
|
9
10
|
const AudioViewer = ({ file, url }) => (
|
|
10
11
|
<div className={styles['viewer-audioviewer']}>
|
|
11
12
|
<Icon icon={FileTypeAudioIcon} width={160} height={140} />
|
|
12
13
|
<p className={styles['viewer-filename']}>{file.name}</p>
|
|
13
|
-
<audio
|
|
14
|
+
<audio
|
|
15
|
+
src={url}
|
|
16
|
+
controls="controls"
|
|
17
|
+
preload={isTesting() ? 'none' : 'auto'}
|
|
18
|
+
/>
|
|
14
19
|
</div>
|
|
15
20
|
)
|
|
16
21
|
|
|
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
|
|
|
3
3
|
import cx from 'classnames'
|
|
4
4
|
|
|
5
5
|
import Icon from '../Icon'
|
|
6
|
-
import { default as ArrowIcon } from '
|
|
6
|
+
import { default as ArrowIcon } from '../Icons/DropdownClose'
|
|
7
7
|
|
|
8
8
|
import styles from './styles.styl'
|
|
9
9
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import mimeTypes from 'mime-types'
|
|
2
|
+
|
|
3
|
+
import { models } from 'cozy-client'
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
file: { isDirectory, isFile }
|
|
7
|
+
} = models
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} types - Types we wish to accept ("folder" and/or "extensions/mime" of file), separated by commas
|
|
11
|
+
* @returns {string[]} All the valid types, if the parameter is undefined or if no type is valid, return an empty array
|
|
12
|
+
*/
|
|
13
|
+
export const getCompliantTypes = types => {
|
|
14
|
+
if (types) {
|
|
15
|
+
return types
|
|
16
|
+
.replaceAll(' ', '')
|
|
17
|
+
.split(',')
|
|
18
|
+
.filter(type =>
|
|
19
|
+
type !== 'folder' ? !!mimeTypes.contentType(type) : true
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return []
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if Item is a file with accepted extension/mime
|
|
28
|
+
*
|
|
29
|
+
* @param {object} item - file or folder
|
|
30
|
+
* @param {string[]} validTypes - List of accepted types
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
export const isValidFile = (item, validTypes) => {
|
|
34
|
+
const fileTypesAccepted =
|
|
35
|
+
validTypes.includes(`.${item.name.split('.').pop()}`) ||
|
|
36
|
+
validTypes.includes(item.mime)
|
|
37
|
+
|
|
38
|
+
return isFile(item) && (fileTypesAccepted || validTypes.length === 0)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if Item is a folder with accepted type
|
|
43
|
+
*
|
|
44
|
+
* @param {object} item - file or folder
|
|
45
|
+
* @param {string[]} validTypes - List of accepted types
|
|
46
|
+
* @returns {boolean}
|
|
47
|
+
*/
|
|
48
|
+
export const isValidFolder = (item, validTypes) => {
|
|
49
|
+
return isDirectory(item) && validTypes.includes(`folder`)
|
|
50
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { isValidFile, isValidFolder, getCompliantTypes } from './acceptedTypes'
|
|
2
|
+
|
|
3
|
+
const makeMockFile = ({
|
|
4
|
+
extension = '.pdf',
|
|
5
|
+
mime = 'application/pdf'
|
|
6
|
+
} = {}) => ({
|
|
7
|
+
_id: '123',
|
|
8
|
+
type: 'file',
|
|
9
|
+
mime: mime,
|
|
10
|
+
name: `mockFile${extension}`
|
|
11
|
+
})
|
|
12
|
+
const makeMockFolder = () => ({
|
|
13
|
+
_id: '789',
|
|
14
|
+
type: 'directory',
|
|
15
|
+
name: 'mockDir'
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('acceptedTypes', () => {
|
|
19
|
+
describe('getCompliantTypes', () => {
|
|
20
|
+
it('should an array with all valid types', () => {
|
|
21
|
+
const res = getCompliantTypes('.pdf, text/plain, .not')
|
|
22
|
+
|
|
23
|
+
expect(res).toStrictEqual(['.pdf', 'text/plain'])
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('isValidFile', () => {
|
|
28
|
+
it('should be valid when item has no type providen', () => {
|
|
29
|
+
const item = makeMockFile()
|
|
30
|
+
const validTypesAccepted = []
|
|
31
|
+
|
|
32
|
+
expect(isValidFile(item, validTypesAccepted)).toBe(true)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should be valid when item has an accepted extension', () => {
|
|
36
|
+
const item = makeMockFile({ extension: '.png' })
|
|
37
|
+
const validTypesAccepted = ['.png', '.pdf']
|
|
38
|
+
|
|
39
|
+
expect(isValidFile(item, validTypesAccepted)).toBe(true)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should not be valid when item has not an accepted extension', () => {
|
|
43
|
+
const item = makeMockFile({ extension: '.png' })
|
|
44
|
+
const validTypesAccepted = ['.pdf']
|
|
45
|
+
|
|
46
|
+
expect(isValidFile(item, validTypesAccepted)).toBe(false)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should be valid when item has an accepted mime', () => {
|
|
50
|
+
const item = makeMockFile({ mime: 'image/png' })
|
|
51
|
+
const validTypesAccepted = ['application/pdf', 'image/png']
|
|
52
|
+
|
|
53
|
+
expect(isValidFile(item, validTypesAccepted)).toBe(true)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should not be valid when item has not an accepted mime', () => {
|
|
57
|
+
const item = makeMockFile({ mime: 'image/png' })
|
|
58
|
+
const validTypesAccepted = ['application/pdf']
|
|
59
|
+
|
|
60
|
+
expect(isValidFile(item, validTypesAccepted)).toBe(false)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should not be valid when item is not an file', () => {
|
|
64
|
+
const item = makeMockFolder()
|
|
65
|
+
const validTypesAccepted = []
|
|
66
|
+
|
|
67
|
+
expect(isValidFile(item, validTypesAccepted)).toBe(false)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('isValidFolder', () => {
|
|
72
|
+
it('should be valid when item is an folder', () => {
|
|
73
|
+
const item = makeMockFolder()
|
|
74
|
+
const validTypesAccepted = ['folder']
|
|
75
|
+
|
|
76
|
+
expect(isValidFolder(item, validTypesAccepted)).toBe(true)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should not be valid when item is not an folder', () => {
|
|
80
|
+
const item = makeMockFile()
|
|
81
|
+
const validTypesAccepted = ['folder']
|
|
82
|
+
|
|
83
|
+
expect(isValidFolder(item, validTypesAccepted)).toBe(false)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
})
|
package/react/index.js
CHANGED
|
@@ -28,6 +28,7 @@ export { default as AccordionSummary } from './MuiCozyTheme/AccordionSummary'
|
|
|
28
28
|
export { default as AccordionDetails } from './MuiCozyTheme/AccordionDetails'
|
|
29
29
|
export { default as Toggle } from './Toggle'
|
|
30
30
|
export { default as withBreakpoints } from './helpers/withBreakpoints'
|
|
31
|
+
export { default as makeStyles } from './helpers/makeStyles'
|
|
31
32
|
export { default as useBreakpoints } from './hooks/useBreakpoints'
|
|
32
33
|
export { default as useBrowserOffline } from './hooks/useBrowserOffline'
|
|
33
34
|
export { Media, Img, Bd } from './Media'
|
|
@@ -39,7 +39,9 @@ var DumbAddContactButton = function DumbAddContactButton(props) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
return /*#__PURE__*/React.createElement(AppLinker, {
|
|
42
|
-
|
|
42
|
+
app: {
|
|
43
|
+
slug: installedApp ? installedApp.attributes.slug : 'store'
|
|
44
|
+
},
|
|
43
45
|
href: href
|
|
44
46
|
}, function (_ref) {
|
|
45
47
|
var onClick = _ref.onClick,
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
-
import React, { useCallback
|
|
2
|
+
import React, { useCallback } from 'react';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import { models, useQuery } from 'cozy-client';
|
|
5
5
|
import List from "cozy-ui/transpiled/react/MuiCozyTheme/List";
|
|
6
6
|
import LoadMore from "cozy-ui/transpiled/react/LoadMore";
|
|
7
7
|
import { buildContentFolderQuery } from "cozy-ui/transpiled/react/FilePicker/queries";
|
|
8
8
|
import FilePickerBodyItem from "cozy-ui/transpiled/react/FilePicker/FilePickerBodyItem";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
isFile = _models$file.isFile;
|
|
9
|
+
import { isValidFile } from "cozy-ui/transpiled/react/helpers/acceptedTypes";
|
|
10
|
+
var isDirectory = models.file.isDirectory;
|
|
12
11
|
|
|
13
12
|
var FilePickerBody = function FilePickerBody(_ref) {
|
|
14
13
|
var navigateTo = _ref.navigateTo,
|
|
15
14
|
folderId = _ref.folderId,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
onSelectItemId = _ref.onSelectItemId,
|
|
16
|
+
itemsIdsSelected = _ref.itemsIdsSelected,
|
|
17
|
+
itemTypesAccepted = _ref.itemTypesAccepted,
|
|
19
18
|
multiple = _ref.multiple;
|
|
20
19
|
var contentFolderQuery = buildContentFolderQuery(folderId);
|
|
21
20
|
|
|
@@ -24,50 +23,50 @@ var FilePickerBody = function FilePickerBody(_ref) {
|
|
|
24
23
|
hasMore = _useQuery.hasMore,
|
|
25
24
|
fetchMore = _useQuery.fetchMore;
|
|
26
25
|
|
|
27
|
-
var onCheck = useCallback(function (
|
|
28
|
-
var isChecked =
|
|
29
|
-
return fileIdSelected ===
|
|
26
|
+
var onCheck = useCallback(function (itemId) {
|
|
27
|
+
var isChecked = itemsIdsSelected.some(function (fileIdSelected) {
|
|
28
|
+
return fileIdSelected === itemId;
|
|
30
29
|
});
|
|
31
30
|
|
|
32
31
|
if (isChecked) {
|
|
33
|
-
|
|
34
|
-
return fileIdSelected !==
|
|
32
|
+
onSelectItemId(itemsIdsSelected.filter(function (fileIdSelected) {
|
|
33
|
+
return fileIdSelected !== itemId;
|
|
35
34
|
}));
|
|
36
|
-
} else
|
|
37
|
-
return [].concat(_toConsumableArray(prev), [
|
|
35
|
+
} else onSelectItemId(function (prev) {
|
|
36
|
+
return [].concat(_toConsumableArray(prev), [itemId]);
|
|
38
37
|
});
|
|
39
|
-
}, [
|
|
38
|
+
}, [itemsIdsSelected, onSelectItemId]); // When click on checkbox/radio area...
|
|
40
39
|
|
|
41
|
-
var handleChoiceClick = useCallback(function (
|
|
40
|
+
var handleChoiceClick = useCallback(function (item) {
|
|
42
41
|
return function () {
|
|
43
|
-
if (multiple) onCheck(
|
|
42
|
+
if (multiple) onCheck(item._id);else onSelectItemId(item._id);
|
|
44
43
|
};
|
|
45
|
-
}, [multiple, onCheck,
|
|
44
|
+
}, [multiple, onCheck, onSelectItemId]); // ...when click anywhere on the rest of the line
|
|
46
45
|
|
|
47
|
-
var handleListItemClick = useCallback(function (
|
|
46
|
+
var handleListItemClick = useCallback(function (item) {
|
|
48
47
|
return function () {
|
|
49
|
-
if (isDirectory(
|
|
50
|
-
navigateTo(contentFolder.find(function (
|
|
51
|
-
return
|
|
48
|
+
if (isDirectory(item)) {
|
|
49
|
+
navigateTo(contentFolder.find(function (it) {
|
|
50
|
+
return it._id === item._id;
|
|
52
51
|
}));
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
if (
|
|
56
|
-
if (multiple) onCheck(
|
|
54
|
+
if (isValidFile(item, itemTypesAccepted)) {
|
|
55
|
+
if (multiple) onCheck(item._id);else onSelectItemId(item._id);
|
|
57
56
|
}
|
|
58
57
|
};
|
|
59
|
-
}, [contentFolder,
|
|
60
|
-
return /*#__PURE__*/React.createElement(List, null, contentFolder && contentFolder.map(function (
|
|
58
|
+
}, [contentFolder, itemTypesAccepted, multiple, navigateTo, onCheck, onSelectItemId]);
|
|
59
|
+
return /*#__PURE__*/React.createElement(List, null, contentFolder && contentFolder.map(function (item, idx) {
|
|
61
60
|
var hasDivider = contentFolder ? idx !== contentFolder.length - 1 : false;
|
|
62
61
|
return /*#__PURE__*/React.createElement(FilePickerBodyItem, {
|
|
63
|
-
key:
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
key: item._id,
|
|
63
|
+
item: item,
|
|
64
|
+
itemTypesAccepted: itemTypesAccepted,
|
|
66
65
|
multiple: multiple,
|
|
67
66
|
handleChoiceClick: handleChoiceClick,
|
|
68
67
|
handleListItemClick: handleListItemClick,
|
|
69
68
|
onCheck: onCheck,
|
|
70
|
-
|
|
69
|
+
itemsIdsSelected: itemsIdsSelected,
|
|
71
70
|
hasDivider: hasDivider
|
|
72
71
|
});
|
|
73
72
|
}), hasMore && /*#__PURE__*/React.createElement(LoadMore, {
|
|
@@ -77,13 +76,10 @@ var FilePickerBody = function FilePickerBody(_ref) {
|
|
|
77
76
|
};
|
|
78
77
|
|
|
79
78
|
FilePickerBody.propTypes = {
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
onSelectItemId: PropTypes.func.isRequired,
|
|
80
|
+
itemsIdsSelected: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
82
81
|
folderId: PropTypes.string.isRequired,
|
|
83
82
|
navigateTo: PropTypes.func.isRequired,
|
|
84
|
-
|
|
85
|
-
file: PropTypes.bool,
|
|
86
|
-
folder: PropTypes.bool
|
|
87
|
-
})
|
|
83
|
+
itemTypesAccepted: PropTypes.arrayOf(PropTypes.string).isRequired
|
|
88
84
|
};
|
|
89
|
-
export default
|
|
85
|
+
export default FilePickerBody;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import cx from 'classnames';
|
|
4
4
|
import filesize from 'filesize';
|
|
@@ -14,6 +14,7 @@ import FileTypeFolder from "cozy-ui/transpiled/react/Icons/FileTypeFolder";
|
|
|
14
14
|
import Checkbox from "cozy-ui/transpiled/react/Checkbox";
|
|
15
15
|
import Radio from "cozy-ui/transpiled/react/Radios";
|
|
16
16
|
import { useI18n } from "cozy-ui/transpiled/react/I18n";
|
|
17
|
+
import { isValidFile, isValidFolder } from "cozy-ui/transpiled/react/helpers/acceptedTypes";
|
|
17
18
|
var styles = {
|
|
18
19
|
"filePickerBreadcrumb-previousPath": "styles__filePickerBreadcrumb-previousPath___3LKJH",
|
|
19
20
|
"filePickerBreadcrumb-icon": "styles__filePickerBreadcrumb-icon___3aFyd",
|
|
@@ -38,53 +39,55 @@ var useStyles = makeStyles(function () {
|
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
var FilePickerBodyItem = function FilePickerBodyItem(_ref) {
|
|
41
|
-
var
|
|
42
|
-
|
|
42
|
+
var item = _ref.item,
|
|
43
|
+
itemTypesAccepted = _ref.itemTypesAccepted,
|
|
43
44
|
multiple = _ref.multiple,
|
|
44
45
|
handleChoiceClick = _ref.handleChoiceClick,
|
|
45
46
|
handleListItemClick = _ref.handleListItemClick,
|
|
46
|
-
|
|
47
|
+
itemsIdsSelected = _ref.itemsIdsSelected,
|
|
47
48
|
hasDivider = _ref.hasDivider;
|
|
48
49
|
var classes = useStyles();
|
|
49
50
|
|
|
50
51
|
var _useI18n = useI18n(),
|
|
51
52
|
f = _useI18n.f;
|
|
52
53
|
|
|
53
|
-
var hasChoice =
|
|
54
|
+
var hasChoice = isValidFile(item, itemTypesAccepted) || isValidFolder(item, itemTypesAccepted);
|
|
54
55
|
var Input = multiple ? Checkbox : Radio;
|
|
55
|
-
var listItemSecondaryContent = isFile(
|
|
56
|
+
var listItemSecondaryContent = isFile(item) ? "".concat(f(item.updated_at, 'DD MMM YYYY'), " - ").concat(filesize(item.size, {
|
|
56
57
|
base: 10
|
|
57
58
|
})) : null;
|
|
58
59
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ListItem, {
|
|
60
|
+
disabled: !hasChoice && isFile(item),
|
|
59
61
|
button: true,
|
|
60
|
-
className: "u-p-0"
|
|
62
|
+
className: "u-p-0",
|
|
63
|
+
"data-testid": "list-item"
|
|
61
64
|
}, /*#__PURE__*/React.createElement("div", {
|
|
62
65
|
"data-testid": "listitem-onclick",
|
|
63
66
|
className: styles['filePickerBreadcrumb-wrapper'],
|
|
64
|
-
onClick: handleListItemClick(
|
|
67
|
+
onClick: handleListItemClick(item)
|
|
65
68
|
}, /*#__PURE__*/React.createElement(ListItemIcon, {
|
|
66
69
|
className: classes.listItemIcon
|
|
67
70
|
}, /*#__PURE__*/React.createElement(Icon, {
|
|
68
|
-
icon: isDirectory(
|
|
71
|
+
icon: isDirectory(item) ? FileTypeFolder : FileTypeText,
|
|
69
72
|
width: "32",
|
|
70
73
|
height: "32"
|
|
71
74
|
})), /*#__PURE__*/React.createElement(ListItemText, {
|
|
72
|
-
primary:
|
|
75
|
+
primary: item.name,
|
|
73
76
|
secondary: listItemSecondaryContent
|
|
74
|
-
})), isDirectory(
|
|
77
|
+
})), isDirectory(item) && hasChoice && /*#__PURE__*/React.createElement(Divider, {
|
|
75
78
|
orientation: "vertical",
|
|
76
79
|
flexItem: true,
|
|
77
80
|
className: classes.verticalDivider
|
|
78
81
|
}), /*#__PURE__*/React.createElement("div", {
|
|
79
82
|
"data-testid": "choice-onclick",
|
|
80
83
|
className: "u-ph-1 u-pv-half u-h-2 u-flex u-flex-items-center",
|
|
81
|
-
onClick: hasChoice ? handleChoiceClick(
|
|
84
|
+
onClick: hasChoice ? handleChoiceClick(item) : undefined
|
|
82
85
|
}, /*#__PURE__*/React.createElement(Input, {
|
|
83
86
|
"data-testid": multiple ? 'checkbox-btn' : 'radio-btn',
|
|
84
87
|
onChange: function onChange() {// handled by onClick on the container
|
|
85
88
|
},
|
|
86
|
-
checked:
|
|
87
|
-
value:
|
|
89
|
+
checked: itemsIdsSelected.includes(item._id),
|
|
90
|
+
value: item._id,
|
|
88
91
|
className: cx('u-p-0', {
|
|
89
92
|
'u-o-100': hasChoice,
|
|
90
93
|
'u-o-0': !hasChoice
|
|
@@ -96,15 +99,12 @@ var FilePickerBodyItem = function FilePickerBodyItem(_ref) {
|
|
|
96
99
|
};
|
|
97
100
|
|
|
98
101
|
FilePickerBodyItem.propTypes = {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
file: PropTypes.bool,
|
|
102
|
-
folder: PropTypes.bool
|
|
103
|
-
}),
|
|
102
|
+
item: PropTypes.object.isRequired,
|
|
103
|
+
itemTypesAccepted: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
104
104
|
multiple: PropTypes.bool,
|
|
105
105
|
handleChoiceClick: PropTypes.func.isRequired,
|
|
106
106
|
handleListItemClick: PropTypes.func.isRequired,
|
|
107
|
-
|
|
107
|
+
itemsIdsSelected: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
108
108
|
hasDivider: PropTypes.bool.isRequired
|
|
109
109
|
};
|
|
110
|
-
export default
|
|
110
|
+
export default FilePickerBodyItem;
|
|
@@ -7,20 +7,7 @@ import FilePickerHeader from "cozy-ui/transpiled/react/FilePicker/FilePickerHead
|
|
|
7
7
|
import FilePickerFooter from "cozy-ui/transpiled/react/FilePicker/FilePickerFooter";
|
|
8
8
|
import FilePickerBody from "cozy-ui/transpiled/react/FilePicker/FilePickerBody";
|
|
9
9
|
import useBreakpoints from "cozy-ui/transpiled/react/hooks/useBreakpoints";
|
|
10
|
-
|
|
11
|
-
* @param {string} fileType - Defines the file types that should be accepted ("file" and/or "folder"), separated by commas
|
|
12
|
-
* @returns {{ file: boolean, folder: boolean }}
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
var getFileTypesAccepted = function getFileTypesAccepted(fileType) {
|
|
16
|
-
// If accept is falsy, set default to file
|
|
17
|
-
var acceptedFileType = fileType ? fileType.replaceAll(' ', '').split(',') : ['file'];
|
|
18
|
-
return {
|
|
19
|
-
file: acceptedFileType.includes('file'),
|
|
20
|
-
folder: acceptedFileType.includes('folder')
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
|
|
10
|
+
import { getCompliantTypes } from "cozy-ui/transpiled/react/helpers/acceptedTypes";
|
|
24
11
|
var useStyles = makeStyles(function () {
|
|
25
12
|
return {
|
|
26
13
|
paper: {
|
|
@@ -48,14 +35,14 @@ var FilePicker = function FilePicker(_ref) {
|
|
|
48
35
|
|
|
49
36
|
var _useState3 = useState([]),
|
|
50
37
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
itemsIdsSelected = _useState4[0],
|
|
39
|
+
setItemsIdsSelected = _useState4[1];
|
|
53
40
|
|
|
54
|
-
var
|
|
41
|
+
var onSelectItemId = function onSelectItemId(fileId) {
|
|
55
42
|
if (!multiple) {
|
|
56
43
|
handleConfirm(null, fileId);
|
|
57
44
|
} else {
|
|
58
|
-
|
|
45
|
+
setItemsIdsSelected(fileId);
|
|
59
46
|
}
|
|
60
47
|
};
|
|
61
48
|
|
|
@@ -64,11 +51,11 @@ var FilePicker = function FilePicker(_ref) {
|
|
|
64
51
|
};
|
|
65
52
|
|
|
66
53
|
var handleConfirm = function handleConfirm(_, fileId) {
|
|
67
|
-
onChange(fileId ? fileId :
|
|
54
|
+
onChange(fileId ? fileId : itemsIdsSelected);
|
|
68
55
|
onClose();
|
|
69
56
|
};
|
|
70
57
|
|
|
71
|
-
var
|
|
58
|
+
var itemTypesAccepted = getCompliantTypes(accept);
|
|
72
59
|
return /*#__PURE__*/React.createElement(FixedDialog, {
|
|
73
60
|
open: true,
|
|
74
61
|
disableGutters: true,
|
|
@@ -84,16 +71,16 @@ var FilePicker = function FilePicker(_ref) {
|
|
|
84
71
|
}),
|
|
85
72
|
content: /*#__PURE__*/React.createElement(FilePickerBody, {
|
|
86
73
|
navigateTo: navigateTo,
|
|
87
|
-
|
|
88
|
-
|
|
74
|
+
onSelectItemId: onSelectItemId,
|
|
75
|
+
itemsIdsSelected: itemsIdsSelected,
|
|
89
76
|
folderId: folderId,
|
|
90
|
-
|
|
77
|
+
itemTypesAccepted: itemTypesAccepted,
|
|
91
78
|
multiple: multiple
|
|
92
79
|
}),
|
|
93
80
|
actions: multiple ? /*#__PURE__*/React.createElement(FilePickerFooter, {
|
|
94
81
|
onClose: onClose,
|
|
95
82
|
onConfirm: handleConfirm,
|
|
96
|
-
disabledConfirm:
|
|
83
|
+
disabledConfirm: itemsIdsSelected.length === 0
|
|
97
84
|
}) : null
|
|
98
85
|
});
|
|
99
86
|
};
|
|
@@ -105,7 +92,7 @@ FilePicker.propTypes = {
|
|
|
105
92
|
multiple: PropTypes.bool
|
|
106
93
|
};
|
|
107
94
|
FilePicker.defaultProps = {
|
|
108
|
-
accept: '
|
|
95
|
+
accept: '',
|
|
109
96
|
multiple: false
|
|
110
97
|
};
|
|
111
98
|
export default /*#__PURE__*/memo(FilePicker);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import Icon from "cozy-ui/transpiled/react/Icon";
|
|
3
|
+
import FileTypeAudioIcon from "cozy-ui/transpiled/react/Icons/FileTypeAudio";
|
|
3
4
|
import withFileUrl from "cozy-ui/transpiled/react/Viewer/withFileUrl";
|
|
4
5
|
var styles = {
|
|
5
6
|
"CozyTheme--inverted": "styles__CozyTheme--inverted___1II8o",
|
|
@@ -31,7 +32,7 @@ var styles = {
|
|
|
31
32
|
"viewer-pdfMobile": "styles__viewer-pdfMobile___2V75t",
|
|
32
33
|
"viewer-pdfMobile--image": "styles__viewer-pdfMobile--image___2xo_a"
|
|
33
34
|
};
|
|
34
|
-
import
|
|
35
|
+
import isTesting from "cozy-ui/transpiled/react/helpers/isTesting";
|
|
35
36
|
|
|
36
37
|
var AudioViewer = function AudioViewer(_ref) {
|
|
37
38
|
var file = _ref.file,
|
|
@@ -46,7 +47,8 @@ var AudioViewer = function AudioViewer(_ref) {
|
|
|
46
47
|
className: styles['viewer-filename']
|
|
47
48
|
}, file.name), /*#__PURE__*/React.createElement("audio", {
|
|
48
49
|
src: url,
|
|
49
|
-
controls: "controls"
|
|
50
|
+
controls: "controls",
|
|
51
|
+
preload: isTesting() ? 'none' : 'auto'
|
|
50
52
|
}));
|
|
51
53
|
};
|
|
52
54
|
|
|
@@ -3,7 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import cx from 'classnames';
|
|
5
5
|
import Icon from "cozy-ui/transpiled/react/Icon";
|
|
6
|
-
import { default as ArrowIcon } from
|
|
6
|
+
import { default as ArrowIcon } from "cozy-ui/transpiled/react/Icons/DropdownClose";
|
|
7
7
|
var styles = {
|
|
8
8
|
"CozyTheme--inverted": "styles__CozyTheme--inverted___1II8o",
|
|
9
9
|
"CozyTheme--normal": "styles__CozyTheme--normal___382qj",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import mimeTypes from 'mime-types';
|
|
2
|
+
import { models } from 'cozy-client';
|
|
3
|
+
var _models$file = models.file,
|
|
4
|
+
isDirectory = _models$file.isDirectory,
|
|
5
|
+
isFile = _models$file.isFile;
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} types - Types we wish to accept ("folder" and/or "extensions/mime" of file), separated by commas
|
|
8
|
+
* @returns {string[]} All the valid types, if the parameter is undefined or if no type is valid, return an empty array
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export var getCompliantTypes = function getCompliantTypes(types) {
|
|
12
|
+
if (types) {
|
|
13
|
+
return types.replaceAll(' ', '').split(',').filter(function (type) {
|
|
14
|
+
return type !== 'folder' ? !!mimeTypes.contentType(type) : true;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return [];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Check if Item is a file with accepted extension/mime
|
|
22
|
+
*
|
|
23
|
+
* @param {object} item - file or folder
|
|
24
|
+
* @param {string[]} validTypes - List of accepted types
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export var isValidFile = function isValidFile(item, validTypes) {
|
|
29
|
+
var fileTypesAccepted = validTypes.includes(".".concat(item.name.split('.').pop())) || validTypes.includes(item.mime);
|
|
30
|
+
return isFile(item) && (fileTypesAccepted || validTypes.length === 0);
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Check if Item is a folder with accepted type
|
|
34
|
+
*
|
|
35
|
+
* @param {object} item - file or folder
|
|
36
|
+
* @param {string[]} validTypes - List of accepted types
|
|
37
|
+
* @returns {boolean}
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
export var isValidFolder = function isValidFolder(item, validTypes) {
|
|
41
|
+
return isDirectory(item) && validTypes.includes("folder");
|
|
42
|
+
};
|
|
@@ -21,6 +21,7 @@ export { default as AccordionSummary } from './MuiCozyTheme/AccordionSummary';
|
|
|
21
21
|
export { default as AccordionDetails } from './MuiCozyTheme/AccordionDetails';
|
|
22
22
|
export { default as Toggle } from './Toggle';
|
|
23
23
|
export { default as withBreakpoints } from './helpers/withBreakpoints';
|
|
24
|
+
export { default as makeStyles } from './helpers/makeStyles';
|
|
24
25
|
export { default as useBreakpoints } from './hooks/useBreakpoints';
|
|
25
26
|
export { default as useBrowserOffline } from './hooks/useBrowserOffline';
|
|
26
27
|
export { Media, Img, Bd } from './Media';
|