cozy-ui 93.5.0 → 94.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/package.json +1 -1
  3. package/react/ContactsList/Contacts/ContactCozy.jsx +1 -1
  4. package/react/ContactsList/Contacts/ContactEmail.jsx +1 -1
  5. package/react/ContactsList/Contacts/ContactIdentity.jsx +1 -1
  6. package/react/ContactsList/Contacts/ContactPhone.jsx +1 -1
  7. package/react/ContactsList/__snapshots__/ContactRow.spec.js.snap +4 -4
  8. package/react/ContactsList/styles.styl +0 -4
  9. package/react/ContactsListModal/ContactsListContent.jsx +8 -3
  10. package/react/ContactsListModal/Readme.md +0 -3
  11. package/react/ContactsListModal/index.jsx +21 -10
  12. package/react/ContactsListModal/locales/en.json +4 -1
  13. package/react/ContactsListModal/locales/fr.json +4 -1
  14. package/react/Dialog/Readme.md +1 -1
  15. package/react/NestedSelect/BottomSheet.jsx +43 -0
  16. package/react/NestedSelect/ItemRow.jsx +3 -2
  17. package/react/NestedSelect/NestedSelect.jsx +16 -19
  18. package/react/NestedSelect/NestedSelect.md +57 -85
  19. package/react/NestedSelect/NestedSelectResponsive.jsx +15 -0
  20. package/react/NestedSelect/index.jsx +2 -0
  21. package/react/SelectBox/Readme.md +1 -1
  22. package/react/__snapshots__/examples.spec.jsx.snap +504 -504
  23. package/react/helpers/getRandomUUID.js +19 -2
  24. package/transpiled/react/ContactsList/Contacts/ContactCozy.js +1 -1
  25. package/transpiled/react/ContactsList/Contacts/ContactEmail.js +1 -1
  26. package/transpiled/react/ContactsList/Contacts/ContactIdentity.js +1 -1
  27. package/transpiled/react/ContactsList/Contacts/ContactPhone.js +1 -1
  28. package/transpiled/react/ContactsListModal/ContactsListContent.js +10 -3
  29. package/transpiled/react/ContactsListModal/index.js +29 -12
  30. package/transpiled/react/ContactsListModal/withContactsListLocales.js +8 -2
  31. package/transpiled/react/NestedSelect/BottomSheet.js +43 -0
  32. package/transpiled/react/NestedSelect/ItemRow.js +3 -2
  33. package/transpiled/react/NestedSelect/NestedSelect.js +19 -22
  34. package/transpiled/react/NestedSelect/NestedSelectResponsive.js +14 -0
  35. package/transpiled/react/NestedSelect/index.js +3 -1
  36. package/transpiled/react/helpers/getRandomUUID.js +19 -2
  37. package/transpiled/react/stylesheet.css +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ # [94.1.0](https://github.com/cozy/cozy-ui/compare/v94.0.0...v94.1.0) (2023-10-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * **NestedSelect:** Add NestedSelectResponsive and BottomSheet ([d1659fe](https://github.com/cozy/cozy-ui/commit/d1659fe))
7
+
8
+ # [94.0.0](https://github.com/cozy/cozy-ui/compare/v93.5.0...v94.0.0) (2023-10-02)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **ContactsList:** Center the spinner when fetching data ([52791a1](https://github.com/cozy/cozy-ui/commit/52791a1))
14
+ * **ContactsList:** Remove padding on cells ([bb6c237](https://github.com/cozy/cozy-ui/commit/bb6c237))
15
+
16
+
17
+ ### Features
18
+
19
+ * **ContactsListModal:** Use default text on search, add button, result ([652d52f](https://github.com/cozy/cozy-ui/commit/652d52f))
20
+ * **getRandomUUID:** Returns something even without crypto API ([be65396](https://github.com/cozy/cozy-ui/commit/be65396))
21
+
22
+
23
+ ### BREAKING CHANGES
24
+
25
+ * **ContactsListModal:** If you don't want any text in `ContactsListModal` for search input, add button and empty result, use respectively these props : `placeholder={[secure]}, addContactLabel={[secure]}, emptyMessage={[secure]}` instead of `undefined` values
26
+
1
27
  # [93.5.0](https://github.com/cozy/cozy-ui/compare/v93.4.0...v93.5.0) (2023-09-29)
2
28
 
3
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui",
3
- "version": "93.5.0",
3
+ "version": "94.1.0",
4
4
  "description": "Cozy apps UI SDK",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -7,7 +7,7 @@ import styles from '../styles.styl'
7
7
  const ContactCozy = ({ cozyUrl }) => (
8
8
  <TableCell
9
9
  data-testid="ContactCozy" // used by a test in cozy-contacts
10
- className={`${styles['contact-cozyurl']} u-ellipsis`}
10
+ className={`${styles['contact-cozyurl']} u-ellipsis u-p-0`}
11
11
  >
12
12
  {cozyUrl}
13
13
  </TableCell>
@@ -7,7 +7,7 @@ import styles from '../styles.styl'
7
7
  const ContactEmail = ({ email }) => (
8
8
  <TableCell
9
9
  data-testid="ContactEmail" // used by a test in cozy-contacts
10
- className={`${styles['contact-email']} u-ellipsis`}
10
+ className={`${styles['contact-email']} u-ellipsis u-p-0`}
11
11
  >
12
12
  {email}
13
13
  </TableCell>
@@ -23,7 +23,7 @@ const ContactIdentity = ({ contact }) => {
23
23
  data-testid="ContactIdentity" // used by a test in cozy-contacts
24
24
  className={`${
25
25
  styles['contact-identity']
26
- } u-flex u-flex-items-center u-ellipsis`}
26
+ } u-flex u-flex-items-center u-ellipsis u-p-0`}
27
27
  >
28
28
  <Avatar text={getInitials(contact)} size="small" />
29
29
  <ContactName displayName={displayName} familyName={name.familyName} />
@@ -7,7 +7,7 @@ import styles from '../styles.styl'
7
7
  const ContactPhone = ({ phone }) => (
8
8
  <TableCell
9
9
  data-testid="ContactPhone" // used by a test in cozy-contacts
10
- className={`${styles['contact-phone']} u-ellipsis`}
10
+ className={`${styles['contact-phone']} u-ellipsis u-p-0`}
11
11
  >
12
12
  {phone}
13
13
  </TableCell>
@@ -9,7 +9,7 @@ exports[`ContactRow should match the contact snapshot 1`] = `
9
9
  size="medium"
10
10
  >
11
11
  <div
12
- className="styles__TableCell___yJCq7 styles__contact-identity___mL3IJ u-flex u-flex-items-center u-ellipsis"
12
+ className="styles__TableCell___yJCq7 styles__contact-identity___mL3IJ u-flex u-flex-items-center u-ellipsis u-p-0"
13
13
  data-testid="ContactIdentity"
14
14
  >
15
15
  <div
@@ -48,19 +48,19 @@ exports[`ContactRow should match the contact snapshot 1`] = `
48
48
  </p>
49
49
  </div>
50
50
  <div
51
- className="styles__TableCell___yJCq7 styles__contact-email___3n3q2 u-ellipsis"
51
+ className="styles__TableCell___yJCq7 styles__contact-email___3n3q2 u-ellipsis u-p-0"
52
52
  data-testid="ContactEmail"
53
53
  >
54
54
  johndoe@localhost
55
55
  </div>
56
56
  <div
57
- className="styles__TableCell___yJCq7 styles__contact-phone___1sA_m u-ellipsis"
57
+ className="styles__TableCell___yJCq7 styles__contact-phone___1sA_m u-ellipsis u-p-0"
58
58
  data-testid="ContactPhone"
59
59
  >
60
60
  0123456789
61
61
  </div>
62
62
  <div
63
- className="styles__TableCell___yJCq7 styles__contact-cozyurl___3kBp5 u-ellipsis"
63
+ className="styles__TableCell___yJCq7 styles__contact-cozyurl___3kBp5 u-ellipsis u-p-0"
64
64
  data-testid="ContactCozy"
65
65
  >
66
66
  http://johndoe.mycozy.cloud
@@ -11,15 +11,12 @@
11
11
 
12
12
  .contact-phone
13
13
  flex 0 0 12rem
14
- padding 0
15
14
 
16
15
  .contact-cozyurl
17
16
  flex 0 0 12rem
18
- padding 0
19
17
 
20
18
  .contact-email
21
19
  flex-basis 30%
22
- padding 0
23
20
 
24
21
  .contact-myself
25
22
  color var(--secondaryTextColor)
@@ -27,7 +24,6 @@
27
24
 
28
25
  .contact-identity
29
26
  flex-basis 30%
30
- padding 0
31
27
 
32
28
  +small-screen()
33
29
  flex 1 1 auto
@@ -5,7 +5,9 @@ const { getDisplayName } = models.contact
5
5
 
6
6
  import ContactsList from '../ContactsList'
7
7
  import Spinner from '../Spinner'
8
+ import { useI18n } from '../providers/I18n'
8
9
  import EmptyMessage from './EmptyMessage'
10
+ import { withContactsListLocales } from './withContactsListLocales'
9
11
 
10
12
  const mkFilter = filterStr => contacts => {
11
13
  if (!filterStr) {
@@ -33,6 +35,8 @@ const ContactsListContent = ({
33
35
  onItemClick,
34
36
  dismissAction
35
37
  }) => {
38
+ const { t } = useI18n()
39
+
36
40
  const loading =
37
41
  (contacts.fetchStatus === 'loading' ||
38
42
  contacts.fetchStatus === 'pending') &&
@@ -40,6 +44,7 @@ const ContactsListContent = ({
40
44
 
41
45
  const filterContacts = mkFilter(filter)
42
46
  const filteredContacts = filterContacts(contacts.data)
47
+ const selfEmptyMessage = emptyMessage ?? t('emptyContact')
43
48
 
44
49
  const handleItemClick = contact => {
45
50
  if (!onItemClick) {
@@ -52,14 +57,14 @@ const ContactsListContent = ({
52
57
 
53
58
  if (loading) {
54
59
  return (
55
- <div className="u-mv-2">
60
+ <div className="u-mv-2 u-ta-center">
56
61
  <Spinner size="xxlarge" />
57
62
  </div>
58
63
  )
59
64
  }
60
65
 
61
66
  if (filteredContacts.length === 0) {
62
- return <EmptyMessage>{emptyMessage}</EmptyMessage>
67
+ return <EmptyMessage>{selfEmptyMessage}</EmptyMessage>
63
68
  }
64
69
 
65
70
  return (
@@ -67,4 +72,4 @@ const ContactsListContent = ({
67
72
  )
68
73
  }
69
74
 
70
- export default ContactsListContent
75
+ export default withContactsListLocales(ContactsListContent)
@@ -37,11 +37,8 @@ initialState = { opened: isTesting() }
37
37
  </button>
38
38
  {state.opened && (
39
39
  <ContactsListModal
40
- placeholder="Search a contact"
41
40
  dismissAction={() => setState({ opened: false })}
42
41
  onItemClick={contact => alert(`Clicked on ${contact._id}`)}
43
- addContactLabel="Add a contact"
44
- emptyMessage="No contact"
45
42
  />
46
43
  )}
47
44
  </DemoProvider>
@@ -14,13 +14,15 @@ import {
14
14
  import useRealtime from '../hooks/useRealtime'
15
15
  import useEventListener from '../hooks/useEventListener'
16
16
  import useBreakpoints from '../providers/Breakpoints'
17
+ import { useI18n } from '../providers/I18n'
17
18
  import Button from '../Buttons'
18
19
  import PlusIcon from '../Icons/Plus'
19
20
  import Icon from '../Icon'
21
+ import TextField from '../TextField'
20
22
  import MobileHeader from './MobileHeader'
21
23
  import ContactsListContent from './ContactsListContent'
22
24
  import AddContactDialog from './AddContact/AddContactDialog'
23
- import TextField from '../TextField'
25
+ import { withContactsListLocales } from './withContactsListLocales'
24
26
 
25
27
  import styles from './styles.styl'
26
28
 
@@ -45,6 +47,7 @@ const ContactsListModal = ({
45
47
  const [filter, setFilter] = useState('')
46
48
  const [showAddDialog, setShowAddDialog] = useState(false)
47
49
  const { isMobile } = useBreakpoints()
50
+ const { t } = useI18n()
48
51
  const { dialogProps, dialogTitleProps } = useCozyDialog({
49
52
  size: 'large',
50
53
  open: true,
@@ -68,6 +71,9 @@ const ContactsListModal = ({
68
71
  setFilter(e.target.value)
69
72
  }
70
73
 
74
+ const selfAddContactLabel = addContactLabel ?? t('addContact')
75
+ const selfPlaceholder = placeholder ?? t('searchContact')
76
+
71
77
  return (
72
78
  <TopAnchoredDialog {...dialogProps}>
73
79
  <CozyTheme variant={isMobile ? 'inverted' : 'normal'}>
@@ -82,7 +88,7 @@ const ContactsListModal = ({
82
88
  {isMobile ? (
83
89
  <MobileHeader
84
90
  filter={filter}
85
- placeholder={placeholder}
91
+ placeholder={selfPlaceholder}
86
92
  onChange={handleFilterChange}
87
93
  onDismiss={dismissAction}
88
94
  />
@@ -90,8 +96,8 @@ const ContactsListModal = ({
90
96
  <TextField
91
97
  variant="outlined"
92
98
  type="text"
93
- label={placeholder}
94
- id={placeholder}
99
+ label={selfPlaceholder}
100
+ id="contactsListModal-search-id"
95
101
  fullWidth
96
102
  value={filter}
97
103
  onChange={handleFilterChange}
@@ -106,8 +112,10 @@ const ContactsListModal = ({
106
112
  className={isMobile && 'u-mt-1'}
107
113
  variant="secondary"
108
114
  theme="secondary"
109
- label={addContactLabel}
110
- startIcon={<Icon icon={PlusIcon} />}
115
+ label={selfAddContactLabel || <Icon icon={PlusIcon} />}
116
+ {...(selfAddContactLabel && {
117
+ startIcon: <Icon icon={PlusIcon} />
118
+ })}
111
119
  onClick={setShowAddDialog}
112
120
  />
113
121
  </div>
@@ -133,10 +141,13 @@ const ContactsListModal = ({
133
141
 
134
142
  ContactsListModal.propTypes = {
135
143
  onItemClick: PropTypes.func,
136
- placeholder: PropTypes.string,
137
- addContactLabel: PropTypes.string,
138
- emptyMessage: PropTypes.string,
144
+ /** Label to show in the search input */
145
+ placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
146
+ /** Label to show on the button to add a contact */
147
+ addContactLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
148
+ /** Message to show when no result */
149
+ emptyMessage: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
139
150
  dismissAction: PropTypes.func
140
151
  }
141
152
 
142
- export default ContactsListModal
153
+ export default withContactsListLocales(ContactsListModal)
@@ -4,5 +4,8 @@
4
4
  "newContact": "New contact",
5
5
  "coordinates": "Coordinates",
6
6
  "givenName": "Firstname",
7
- "familyName": "Lastname"
7
+ "familyName": "Lastname",
8
+ "addContact": "Add a contact",
9
+ "searchContact": "Search a contact",
10
+ "emptyContact": "No contact"
8
11
  }
@@ -4,5 +4,8 @@
4
4
  "newContact": "Nouveau contact",
5
5
  "coordinates": "Coordonnées",
6
6
  "givenName": "Prénom",
7
- "familyName": "Nom"
7
+ "familyName": "Nom",
8
+ "addContact": "Ajouter un contact",
9
+ "searchContact": "Rechercher un contact",
10
+ "emptyContact": "Aucun contact"
8
11
  }
@@ -9,7 +9,7 @@ will make sure that even your custom Dialogs will behave as CozyDialogs.
9
9
  import Dialog, { DialogTitle, DialogActions } from 'cozy-ui/transpiled/react/Dialog'
10
10
  import { DialogBackButton, DialogCloseButton, useCozyDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
11
11
  import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
12
- import DemoProvider from 'cozy-ui/transpiled/react/providers/DemoProvider'
12
+ import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
13
13
  import Alerter from 'cozy-ui/transpiled/react/deprecated/Alerter'
14
14
 
15
15
  import Divider from 'cozy-ui/transpiled/react/Divider'
@@ -0,0 +1,43 @@
1
+ import React from 'react'
2
+ import cx from 'classnames'
3
+
4
+ import Icon from '../Icon'
5
+ import IconButton from '../IconButton'
6
+ import ArrowUpIcon from '../Icons/ArrowUp'
7
+ import BottomSheet, { BottomSheetHeader, BottomSheetItem } from '../BottomSheet'
8
+ import Typography from '../Typography'
9
+ import Divider from '../Divider'
10
+
11
+ import NestedSelect from './NestedSelect'
12
+
13
+ const HeaderComponent = ({ title, showBack, onClickBack }) => {
14
+ return (
15
+ <>
16
+ <BottomSheetHeader
17
+ className={cx('u-h-3', { 'u-pl-half u-pr-3': showBack })}
18
+ >
19
+ {showBack && (
20
+ <IconButton onClick={onClickBack}>
21
+ <Icon icon={ArrowUpIcon} rotate={-90} />
22
+ </IconButton>
23
+ )}
24
+ <Typography className="u-w-100" variant="h6" align="center">
25
+ {title}
26
+ </Typography>
27
+ </BottomSheetHeader>
28
+ <Divider />
29
+ </>
30
+ )
31
+ }
32
+
33
+ const SelfBottomSheet = props => {
34
+ return (
35
+ <BottomSheet backdrop onClose={props.onClose}>
36
+ <BottomSheetItem disableGutters>
37
+ <NestedSelect HeaderComponent={HeaderComponent} {...props} />
38
+ </BottomSheetItem>
39
+ </BottomSheet>
40
+ )
41
+ }
42
+
43
+ export default SelfBottomSheet
@@ -18,7 +18,8 @@ const ItemRow = ({
18
18
  isSelected,
19
19
  radioPosition,
20
20
  isLast,
21
- ellipsis
21
+ ellipsis,
22
+ noDivider
22
23
  }) => {
23
24
  const { isDesktop } = useBreakpoints()
24
25
 
@@ -76,7 +77,7 @@ const ItemRow = ({
76
77
  ? item.action.Component({ item, ...item.action.props })
77
78
  : null}
78
79
  </ListItem>
79
- {!isLast && <Divider />}
80
+ {noDivider ? null : !isLast && <Divider />}
80
81
  </>
81
82
  )
82
83
  }
@@ -2,6 +2,7 @@ import React, { Component } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import omit from 'lodash/omit'
4
4
 
5
+ import List from '../List'
5
6
  import Input from '../Input'
6
7
  import Typography from '../Typography'
7
8
  import ItemRow from './ItemRow'
@@ -81,7 +82,8 @@ class NestedSelect extends Component {
81
82
  title,
82
83
  transformParentItem,
83
84
  radioPosition,
84
- ellipsis
85
+ ellipsis,
86
+ noDivider
85
87
  } = this.props
86
88
  const { history, searchValue, searchResult } = this.state
87
89
  const current = history[0]
@@ -119,6 +121,7 @@ class NestedSelect extends Component {
119
121
  onClick={this.handleClickItem}
120
122
  isSelected={isSelectedWithLevel(parentItem)}
121
123
  ellipsis={ellipsis}
124
+ noDivider={noDivider}
122
125
  />
123
126
  ) : null}
124
127
  {searchOptions && level === 0 && (
@@ -149,6 +152,7 @@ class NestedSelect extends Component {
149
152
  isSelected={isSelectedWithLevel(item)}
150
153
  isLast={index === searchResult.length - 1}
151
154
  ellipsis={ellipsis}
155
+ noDivider={noDivider}
152
156
  />
153
157
  ))
154
158
  )
@@ -162,6 +166,7 @@ class NestedSelect extends Component {
162
166
  isSelected={isSelectedWithLevel(item)}
163
167
  isLast={index === children.length - 1}
164
168
  ellipsis={ellipsis}
169
+ noDivider={noDivider}
165
170
  />
166
171
  ))
167
172
  )}
@@ -172,7 +177,7 @@ class NestedSelect extends Component {
172
177
  }
173
178
 
174
179
  NestedSelect.defaultProps = {
175
- ContentComponent: 'div',
180
+ ContentComponent: List,
176
181
  HeaderComponent: null,
177
182
  transformParentItem: x => x,
178
183
  radioPosition: 'right'
@@ -191,25 +196,16 @@ const ItemPropType = PropTypes.shape({
191
196
  })
192
197
 
193
198
  NestedSelect.propTypes = {
194
- /**
195
- * Can be set to "right" or "left". Defaults to "right"
196
- */
199
+ /** Can be set to "right" or "left". Defaults to "right" */
197
200
  radioPosition: PropTypes.oneOf(['left', 'right']),
198
201
 
199
- /**
200
- * The whole option item is passed to this function when selected
201
- */
202
+ /** The whole option item is passed to this function when selected */
202
203
  onSelect: PropTypes.func.isRequired,
203
204
 
204
- /**
205
- * Determines if the row looks selected. The `option` is
206
- * passed as an argument.
207
- */
205
+ /** Determines if the row looks selected. The `option` is passed as an argument. */
208
206
  isSelected: PropTypes.func.isRequired,
209
207
 
210
- /**
211
- * Options that will be rendered as nested lists of choices
212
- */
208
+ /** Options that will be rendered as nested lists of choices */
213
209
  options: PropTypes.shape({
214
210
  children: PropTypes.arrayOf(ItemPropType)
215
211
  }),
@@ -242,10 +238,11 @@ NestedSelect.propTypes = {
242
238
  onSearch: PropTypes.func.isRequired
243
239
  }),
244
240
 
245
- /**
246
- * To manage ellipsis on ItemRow
247
- */
248
- ellipsis: PropTypes.bool
241
+ /** To manage ellipsis on ItemRow */
242
+ ellipsis: PropTypes.bool,
243
+
244
+ /** Remove dividers after each row */
245
+ noDivider: PropTypes.bool
249
246
  }
250
247
 
251
248
  export default NestedSelect