cozy-ui 113.8.0 → 114.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/jest.config.js +1 -2
  3. package/package.json +6 -12
  4. package/react/AppSections/components/AppsSection.spec.jsx +10 -19
  5. package/react/AppSections/index.spec.jsx +98 -77
  6. package/react/ContactsList/ContactRow.spec.js +28 -53
  7. package/react/DateMonthPicker/index.spec.jsx +17 -45
  8. package/react/Field/index.spec.js +28 -5
  9. package/react/Figure/Figure.spec.jsx +9 -4
  10. package/react/Figure/__snapshots__/Figure.spec.jsx.snap +289 -225
  11. package/react/FileInput/index.jsx +1 -0
  12. package/react/FileInput/index.spec.jsx +16 -38
  13. package/react/Paywall/Paywall.spec.jsx +3 -5
  14. package/react/Popup/index.spec.jsx +90 -41
  15. package/react/QualificationIconStack/Readme.md +4 -1
  16. package/react/QualificationIconStack/index.jsx +28 -7
  17. package/react/QualificationModal/Readme.md +28 -0
  18. package/react/QualificationModal/index.jsx +94 -0
  19. package/react/QualificationModal/locales/en.json +5 -0
  20. package/react/QualificationModal/locales/fr.json +5 -0
  21. package/react/QualificationModal/locales/index.jsx +7 -0
  22. package/react/deprecated/ViewStack/example.jsx +1 -1
  23. package/react/hooks/useClientErrors.spec.jsx +16 -24
  24. package/react/index.js +2 -0
  25. package/react/providers/I18n/index.spec.jsx +13 -5
  26. package/react/providers/I18n/withLocales.spec.jsx +4 -4
  27. package/transpiled/react/FileInput/index.js +2 -1
  28. package/transpiled/react/QualificationIconStack/index.js +26 -6
  29. package/transpiled/react/QualificationModal/index.js +126 -0
  30. package/transpiled/react/QualificationModal/locales/index.js +14 -0
  31. package/transpiled/react/deprecated/ViewStack/example.js +1 -1
  32. package/transpiled/react/index.js +2 -0
  33. package/react/AppSections/__snapshots__/index.spec.jsx.snap +0 -1843
  34. package/react/AppSections/components/__snapshots__/AppsSection.spec.jsx.snap +0 -41
  35. package/react/ContactsList/__snapshots__/ContactRow.spec.js.snap +0 -69
  36. package/react/FileInput/__snapshots__/index.spec.jsx.snap +0 -86
  37. package/react/Input/__snapshots__/index.spec.jsx.snap +0 -11
  38. package/react/Input/index.spec.jsx +0 -12
  39. package/react/__snapshots__/examples.spec.jsx.snap +0 -3720
  40. package/react/deprecated/ActionMenu/__snapshots__/index.spec.jsx.snap +0 -157
  41. package/react/deprecated/ActionMenu/index.spec.jsx +0 -115
  42. package/react/deprecated/Alerter/__snapshots__/alerter.spec.js.snap +0 -88
  43. package/react/deprecated/Alerter/alerter.spec.js +0 -78
  44. package/react/deprecated/InfosCarrousel/index.spec.jsx +0 -71
  45. package/react/deprecated/Modal/index.spec.jsx +0 -70
  46. package/react/deprecated/ViewStack/index.spec.jsx +0 -64
  47. package/react/examples.spec.jsx +0 -67
@@ -1,3 +1,4 @@
1
+ import { fireEvent, render, screen } from '@testing-library/react'
1
2
  import uniqueId from 'lodash/uniqueId'
2
3
  import React from 'react'
3
4
 
@@ -20,66 +21,43 @@ describe('FileInput component', () => {
20
21
  })
21
22
 
22
23
  it('should render a file selector', () => {
23
- const component = shallow(
24
+ render(
24
25
  <FileInput onChange={onChangeSpy}>
25
26
  <span>Click me</span>
26
27
  </FileInput>
27
- ).getElement()
28
- expect(component).toMatchSnapshot()
29
- })
30
-
31
- it('should render a default file selector', () => {
32
- const component = shallow(
33
- <FileInput hidden={false} onChange={onChangeSpy} />
34
- ).getElement()
35
- expect(component).toMatchSnapshot()
36
- })
28
+ )
37
29
 
38
- it('should render a disabled file selector', () => {
39
- const component = shallow(
40
- <FileInput disabled onChange={onChangeSpy}>
41
- <span>Click me</span>
42
- </FileInput>
43
- ).getElement()
44
- expect(component).toMatchSnapshot()
45
- })
30
+ const button = screen.getByText('Click me')
46
31
 
47
- it('should pass props to the input', () => {
48
- const component = shallow(
49
- <FileInput accept="image/*" onChange={onChangeSpy}>
50
- <span>Click me</span>
51
- </FileInput>
52
- ).getElement()
53
- expect(component).toMatchSnapshot()
32
+ expect(button).toBeInTheDocument()
33
+ expect(screen.getByTestId('file-input')).toBeInTheDocument()
54
34
  })
55
35
 
56
36
  it('should process selected file on change', () => {
57
37
  const filelist = [pic1]
58
- const component = shallow(
38
+ render(
59
39
  <FileInput accept="image/*" onChange={onChangeSpy}>
60
40
  <span>Click me</span>
61
41
  </FileInput>
62
42
  )
63
- component.find('input').simulate('change', {
64
- target: {
65
- files: filelist
66
- }
67
- })
43
+
44
+ const input = screen.getByTestId('file-input')
45
+ fireEvent.change(input, { target: { files: filelist } })
46
+
68
47
  expect(onChangeSpy).toHaveBeenCalledWith(pic1)
69
48
  })
70
49
 
71
50
  it('should process selected files on change if it is multiple', () => {
72
51
  const filelist = [pic1, pic2]
73
- const component = shallow(
52
+ render(
74
53
  <FileInput accept="image/*" multiple onChange={onChangeSpy}>
75
54
  <span>Click me</span>
76
55
  </FileInput>
77
56
  )
78
- component.find('input').simulate('change', {
79
- target: {
80
- files: filelist
81
- }
82
- })
57
+
58
+ const input = screen.getByTestId('file-input')
59
+ fireEvent.change(input, { target: { files: filelist } })
60
+
83
61
  expect(onChangeSpy).toHaveBeenCalledWith([pic1, pic2])
84
62
  })
85
63
  })
@@ -42,15 +42,13 @@ describe('Paywall', () => {
42
42
  useInstanceInfo.mockReturnValue({
43
43
  context: {
44
44
  data: {
45
- attributes: {
46
- enable_premium_links: enablePremiumLinks,
47
- manager_url: 'http://mycozy.cloud'
48
- }
45
+ enable_premium_links: enablePremiumLinks,
46
+ manager_url: 'http://mycozy.cloud'
49
47
  }
50
48
  },
51
49
  instance: {
52
50
  data: {
53
- attributes: { uuid: hasUuid ? '123' : null }
51
+ uuid: hasUuid ? '123' : null
54
52
  }
55
53
  },
56
54
  isLoaded: true
@@ -1,4 +1,4 @@
1
- import { shallow } from 'enzyme'
1
+ import { render, fireEvent } from '@testing-library/react'
2
2
  import React from 'react'
3
3
 
4
4
  import { isMobileApp } from 'cozy-device-helper'
@@ -17,35 +17,56 @@ const props = {
17
17
  height: '200'
18
18
  }
19
19
 
20
- const popupMock = {}
21
- class MessageEventMock {
22
- constructor(options = {}) {
23
- this.source = options.source || popupMock
20
+ class MessageEventMock extends Event {
21
+ constructor(options = {}, mock) {
22
+ super('message', options)
23
+ this.source = options.source || mock
24
+ }
25
+ }
26
+
27
+ class LoadStartEventMock extends Event {
28
+ constructor(url) {
29
+ super('loadstart')
30
+ this.url = url
31
+ }
32
+ }
33
+
34
+ class ExitEventMock extends Event {
35
+ constructor() {
36
+ super('exit')
24
37
  }
25
38
  }
26
39
 
27
40
  describe('Popup', () => {
28
- beforeAll(() => {
29
- jest.useFakeTimers()
41
+ const setup = ({ mockAddEventListener = true }) => {
42
+ const popupMock = new EventTarget()
43
+
44
+ isMobileApp.mockReturnValue(false)
30
45
 
31
46
  jest.spyOn(global, 'open').mockReturnValue(popupMock)
32
47
  jest.spyOn(global, 'addEventListener')
33
- })
34
48
 
35
- afterAll(() => {
36
- global.open.mockRestore()
37
- global.addEventListener.mockRestore()
38
- })
49
+ if (mockAddEventListener) {
50
+ popupMock.addEventListener = jest.fn()
51
+ }
39
52
 
40
- beforeEach(() => {
41
- isMobileApp.mockReturnValue(false)
42
- popupMock.addEventListener = jest.fn()
43
53
  popupMock.close = jest.fn()
44
54
  popupMock.focus = jest.fn()
45
55
  popupMock.closed = false
46
56
  props.onClose = jest.fn()
47
57
  props.onMessage = jest.fn()
48
58
  props.onMobileUrlChange = jest.fn()
59
+
60
+ return popupMock
61
+ }
62
+
63
+ beforeAll(() => {
64
+ jest.useFakeTimers()
65
+ })
66
+
67
+ afterAll(() => {
68
+ global.open.mockRestore()
69
+ global.addEventListener.mockRestore()
49
70
  })
50
71
 
51
72
  afterEach(() => {
@@ -54,7 +75,10 @@ describe('Popup', () => {
54
75
  })
55
76
 
56
77
  it('should open new window', () => {
57
- shallow(<Popup {...props} />)
78
+ const popupMock = setup({})
79
+
80
+ render(<Popup {...props} />)
81
+
58
82
  expect(global.open).toHaveBeenCalledWith(
59
83
  props.initialUrl,
60
84
  props.title,
@@ -64,66 +88,91 @@ describe('Popup', () => {
64
88
  })
65
89
 
66
90
  it('should subscribe to message events', () => {
67
- const wrapper = shallow(<Popup {...props} />)
91
+ setup({})
92
+
93
+ render(<Popup {...props} />)
94
+
68
95
  expect(global.addEventListener).toHaveBeenCalledWith(
69
96
  'message',
70
- wrapper.instance().handleMessage
97
+ expect.any(Function)
71
98
  )
72
99
  })
73
100
 
74
101
  it('should subcribe to mobile events', () => {
102
+ const popupMock = setup({})
75
103
  isMobileApp.mockReturnValue(true)
76
- const wrapper = shallow(<Popup {...props} />)
104
+
105
+ render(<Popup {...props} />)
106
+
77
107
  expect(popupMock.addEventListener).toHaveBeenCalledWith(
78
108
  'loadstart',
79
- wrapper.instance().handleLoadStart
109
+ expect.any(Function)
80
110
  )
81
111
  expect(popupMock.addEventListener).toHaveBeenCalledWith(
82
112
  'exit',
83
- wrapper.instance().handleClose
113
+ expect.any(Function)
84
114
  )
85
115
  })
86
116
 
87
- describe('monitorClosing', () => {
88
- it('should detect closing', () => {
89
- const wrapper = shallow(<Popup {...props} />)
90
- jest.spyOn(wrapper.instance(), 'handleClose')
91
- popupMock.closed = true
92
- jest.runAllTimers()
93
- expect(wrapper.instance().handleClose).toHaveBeenCalled()
94
- })
95
- })
96
-
97
117
  describe('handleClose', () => {
98
118
  it('should call onClose', () => {
99
- const wrapper = shallow(<Popup {...props} />)
100
- wrapper.instance().handleClose()
119
+ const popupMock = setup({
120
+ mockAddEventListener: false
121
+ })
122
+ isMobileApp.mockReturnValue(true)
123
+
124
+ render(<Popup {...props} />)
125
+
126
+ const messageEvent = new ExitEventMock()
127
+ fireEvent(popupMock, messageEvent)
128
+
101
129
  expect(props.onClose).toHaveBeenCalled()
102
130
  })
103
131
  })
104
132
 
105
133
  describe('handleMessage', () => {
106
134
  it('should call onMessage', () => {
107
- const wrapper = shallow(<Popup {...props} />)
108
- const messageEvent = new MessageEventMock()
109
- wrapper.instance().handleMessage(messageEvent)
135
+ const popupMock = setup({
136
+ mockAddEventListener: false
137
+ })
138
+ isMobileApp.mockReturnValue(true)
139
+
140
+ render(<Popup {...props} />)
141
+
142
+ const messageEvent = new MessageEventMock({}, popupMock)
143
+ fireEvent(window, messageEvent)
144
+
110
145
  expect(props.onMessage).toHaveBeenCalledWith(messageEvent)
111
146
  })
112
147
 
113
148
  it('should ignore messageEvent from another window ', () => {
114
- const wrapper = shallow(<Popup {...props} />)
149
+ setup({
150
+ mockAddEventListener: false
151
+ })
152
+ isMobileApp.mockReturnValue(true)
153
+
154
+ render(<Popup {...props} />)
155
+
115
156
  const messageEvent = new MessageEventMock({ source: {} })
116
- wrapper.instance().handleMessage(messageEvent)
157
+ fireEvent(window, messageEvent)
158
+
117
159
  expect(props.onMessage).not.toHaveBeenCalled()
118
160
  })
119
161
  })
120
162
 
121
163
  describe('handleLoadStart', () => {
122
164
  it('should call onMobileUrlChange', () => {
123
- const wrapper = shallow(<Popup {...props} />)
165
+ const popupMock = setup({
166
+ mockAddEventListener: false
167
+ })
168
+ isMobileApp.mockReturnValue(true)
169
+
170
+ render(<Popup {...props} />)
171
+
124
172
  const url = 'https://cozy.io'
125
- const urlEvent = { url }
126
- wrapper.instance().handleLoadStart(urlEvent)
173
+ const messageEvent = new LoadStartEventMock(url)
174
+ fireEvent(popupMock, messageEvent)
175
+
127
176
  expect(props.onMobileUrlChange).toHaveBeenCalledWith(expect.any(URL))
128
177
  expect(props.onMobileUrlChange).toHaveBeenCalledWith(new URL(url))
129
178
  })
@@ -4,8 +4,11 @@ import QualificationIconStack from 'cozy-ui/transpiled/react/QualificationIconSt
4
4
  ;
5
5
 
6
6
  <>
7
+ <QualificationIconStack className="u-mr-1" />
7
8
  <QualificationIconStack className="u-mr-1" qualification="isp_invoice" />
8
9
  <QualificationIconStack className="u-mr-1" qualification="phone_invoice" />
9
- <QualificationIconStack qualification="family_record_book" />
10
+ <QualificationIconStack className="u-mr-1" qualification="family_record_book" />
11
+ <QualificationIconStack className="u-mr-1" theme="identity" />
12
+ <QualificationIconStack theme="transport" />
10
13
  </>
11
14
  ```
@@ -10,7 +10,9 @@ import BankCheckIcon from '../Icons/BankCheck'
10
10
  import BenefitIcon from '../Icons/Benefit'
11
11
  import BillIcon from '../Icons/Bill'
12
12
  import CarIcon from '../Icons/Car'
13
+ import ChessIcon from '../Icons/Chess'
13
14
  import ChildIcon from '../Icons/Child'
15
+ import DotsIcon from '../Icons/Dots'
14
16
  import EmailIcon from '../Icons/Email'
15
17
  import EuroIcon from '../Icons/Euro'
16
18
  import ExchangeIcon from '../Icons/Exchange'
@@ -51,13 +53,15 @@ function FileDuotoneWhite(props) {
51
53
  )
52
54
  }
53
55
 
54
- const qualificationIcon = {
56
+ const IconByLabel = {
55
57
  'bank-check': BankCheckIcon,
56
58
  bank: BankIcon,
57
59
  benefit: BenefitIcon,
58
60
  bill: BillIcon,
59
61
  car: CarIcon,
62
+ chess: ChessIcon,
60
63
  child: ChildIcon,
64
+ dots: DotsIcon,
61
65
  email: EmailIcon,
62
66
  euro: EuroIcon,
63
67
  exchange: ExchangeIcon,
@@ -84,8 +88,25 @@ const qualificationIcon = {
84
88
  work: WorkIcon
85
89
  }
86
90
 
87
- const QualificationIconStack = ({ qualification, ...props }) => {
88
- const QualificationIcon = qualificationIcon[getIconByLabel(qualification)]
91
+ const themeIconByLabel = {
92
+ identity: 'people',
93
+ family: 'team',
94
+ work_study: 'work',
95
+ health: 'heart',
96
+ home: 'home',
97
+ transport: 'car',
98
+ activity: 'chess',
99
+ finance: 'bank',
100
+ invoice: 'bill',
101
+ others: 'dots'
102
+ }
103
+
104
+ const QualificationIconStack = ({ theme, qualification, ...props }) => {
105
+ const ForegroundIcon = qualification
106
+ ? IconByLabel[getIconByLabel(qualification)]
107
+ : theme
108
+ ? IconByLabel[themeIconByLabel[theme]]
109
+ : null
89
110
 
90
111
  return (
91
112
  <IconStack
@@ -96,16 +117,16 @@ const QualificationIconStack = ({ qualification, ...props }) => {
96
117
  <Icon icon={FileDuotoneIcon} color="#E049BF" size={32} />
97
118
  </>
98
119
  }
99
- foregroundIcon={
100
- <Icon icon={QualificationIcon} color="#E049BF" size={16} />
101
- }
120
+ foregroundIcon={<Icon icon={ForegroundIcon} color="#E049BF" size={16} />}
102
121
  />
103
122
  )
104
123
  }
105
124
 
106
125
  QualificationIconStack.propTypes = {
107
126
  /** The name of the qualification (isp\_invoice, family\_record\_book, etc.) */
108
- qualification: PropTypes.string.isRequired
127
+ qualification: PropTypes.string,
128
+ /** The name of the qualification theme (indentity, family, etc.) */
129
+ theme: PropTypes.string
109
130
  }
110
131
 
111
132
  export default QualificationIconStack
@@ -0,0 +1,28 @@
1
+ ```jsx
2
+ import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
3
+ import QualificationModal from 'cozy-ui/transpiled/react/QualificationModal'
4
+ import Typography from 'cozy-ui/transpiled/react/Typography'
5
+ import CloudIcon from 'cozy-ui/transpiled/react/Icons/Cloud'
6
+ import FormControlLabel from 'cozy-ui/transpiled/react/FormControlLabel'
7
+ import RadioGroup from 'cozy-ui/transpiled/react/RadioGroup'
8
+ import Radio from 'cozy-ui/transpiled/react/Radios'
9
+ import FormControl from 'cozy-ui/transpiled/react/FormControl'
10
+ import Button from 'cozy-ui/transpiled/react/Buttons'
11
+
12
+ initialState = { show: isTesting() ? true : false }
13
+
14
+ const show = () => setState({show: true})
15
+ const hide = () => setState({show: false})
16
+
17
+ ;
18
+
19
+ <DemoProvider client={{ collection: () => ({ updateMetadataAttribute: () => {}}) }}>
20
+ <Button label={state.show ? "Close modal" : "Open modal"} variant="ghost" onClick={show} />
21
+ {state.show &&
22
+ <QualificationModal
23
+ file={{ name: "toto.txt", metadata: { qualification: { label: 'isp_invoice' } } }}
24
+ onClose={hide}
25
+ />
26
+ }
27
+ </DemoProvider>
28
+ ```
@@ -0,0 +1,94 @@
1
+ import PropTypes from 'prop-types'
2
+ import React, { useMemo } from 'react'
3
+
4
+ import { useClient } from 'cozy-client'
5
+ import { themesList } from 'cozy-client/dist/models/document/documentTypeData'
6
+ import { isQualificationNote } from 'cozy-client/dist/models/document/documentTypeDataHelpers'
7
+ import { getBoundT } from 'cozy-client/dist/models/document/locales'
8
+ import { getQualification } from 'cozy-client/dist/models/document/qualification'
9
+
10
+ import { locales } from './locales'
11
+ import Icon from '../Icon'
12
+ import FileTypeNoteIcon from '../Icons/FileTypeNote'
13
+ import NestedSelectResponsive from '../NestedSelect/NestedSelectResponsive'
14
+ import QualificationIconStack from '../QualificationIconStack'
15
+ import { useI18n, useExtendI18n } from '../providers/I18n'
16
+
17
+ const makeOptions = lang => {
18
+ const qualifT = getBoundT(lang)
19
+
20
+ return {
21
+ children: [
22
+ {
23
+ id: 'none',
24
+ title: qualifT('Scan.themes.none'),
25
+ icon: <QualificationIconStack />
26
+ },
27
+ ...themesList.map(theme => ({
28
+ id: theme.id,
29
+ title: qualifT(`Scan.themes.${theme.label}`),
30
+ icon: <QualificationIconStack theme={theme.label} />,
31
+ children: theme.items.map(item => ({
32
+ id: item.label,
33
+ item,
34
+ title: qualifT(`Scan.items.${item.label}`),
35
+ icon: isQualificationNote(item) ? (
36
+ <Icon icon={FileTypeNoteIcon} size={64} />
37
+ ) : (
38
+ <QualificationIconStack qualification={item.label} />
39
+ )
40
+ }))
41
+ }))
42
+ ]
43
+ }
44
+ }
45
+
46
+ const QualificationModal = ({ file, title, onClose }) => {
47
+ useExtendI18n(locales)
48
+ const client = useClient()
49
+ const { t, lang } = useI18n()
50
+
51
+ const qualificationLabel = getQualification(file)?.label
52
+ const options = useMemo(() => makeOptions(lang), [lang])
53
+
54
+ const isSelected = ({ id, item }) => {
55
+ return qualificationLabel
56
+ ? qualificationLabel === item?.label
57
+ : id === 'none'
58
+ }
59
+
60
+ const handleClick = async ({ id, item }) => {
61
+ const fileCollection = client.collection('io.cozy.files')
62
+ const removeQualification = qualificationLabel && id === 'none'
63
+
64
+ /*
65
+ In the case where we remove the qualification it's necessary to define the attribute to `null` and not `undefined`, with `undefined` the stack does not return the attribute and today the Redux store is not updated for a missing attribute.
66
+ As a result, the UI is not updated and continues to display the qualification on the document, even though it has been deleted in CouchDB.
67
+ */
68
+ await fileCollection.updateMetadataAttribute(file._id, {
69
+ qualification: removeQualification ? null : item
70
+ })
71
+
72
+ onClose()
73
+ }
74
+
75
+ return (
76
+ <NestedSelectResponsive
77
+ title={title || t('QualificationModal.title')}
78
+ options={options}
79
+ noDivider
80
+ document={file}
81
+ isSelected={isSelected}
82
+ onSelect={handleClick}
83
+ onClose={onClose}
84
+ />
85
+ )
86
+ }
87
+
88
+ QualificationModal.propTypes = {
89
+ file: PropTypes.object,
90
+ title: PropTypes.string,
91
+ onClose: PropTypes.func
92
+ }
93
+
94
+ export default QualificationModal
@@ -0,0 +1,5 @@
1
+ {
2
+ "QualificationModal": {
3
+ "title": "Document type"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "QualificationModal": {
3
+ "title": "Type de document"
4
+ }
5
+ }
@@ -0,0 +1,7 @@
1
+ import en from './en.json'
2
+ import fr from './fr.json'
3
+
4
+ export const locales = {
5
+ en,
6
+ fr
7
+ }
@@ -17,7 +17,7 @@ const Slide = ({ number }) => {
17
17
  const handleClickPop = async () => {
18
18
  await stackPop()
19
19
 
20
- // No alerts during enzyme tests
20
+ // No alerts during tests
21
21
  if (number === 2 && !global.mount) {
22
22
  alert('You went back to the first slide')
23
23
  }
@@ -1,11 +1,11 @@
1
- import { renderHook, act } from '@testing-library/react-hooks'
2
- import { shallow } from 'enzyme'
1
+ import { screen, render, act } from '@testing-library/react'
2
+ import { renderHook } from '@testing-library/react-hooks'
3
3
  import React from 'react'
4
4
 
5
- import { CozyProvider } from 'cozy-client'
6
5
  import { FetchError } from 'cozy-stack-client'
7
6
 
8
7
  import useClientErrors from './useClientErrors'
8
+ import DemoProvider from '../providers/DemoProvider'
9
9
 
10
10
  function createCozyClient() {
11
11
  return {
@@ -14,9 +14,15 @@ function createCozyClient() {
14
14
  }
15
15
  }
16
16
 
17
+ jest.mock('../deprecated/QuotaAlert', () => ({ onClose }) => (
18
+ <>
19
+ QuotaAlert <button onClick={onClose}>Dismiss</button>
20
+ </>
21
+ ))
22
+
17
23
  function createWrapper(client = createCozyClient()) {
18
24
  function Wrapper({ children }) {
19
- return <CozyProvider client={client}>{children}</CozyProvider>
25
+ return <DemoProvider client={client}>{children}</DemoProvider>
20
26
  }
21
27
  return Wrapper
22
28
  }
@@ -27,7 +33,7 @@ function renderWrappedHook(client) {
27
33
  }
28
34
 
29
35
  function wrappedShallow(tree, client) {
30
- return shallow(tree, { wrappingComponent: createWrapper(client) })
36
+ return render(tree, { wrapper: createWrapper(client) })
31
37
  }
32
38
 
33
39
  describe('useClientErrors', () => {
@@ -47,14 +53,12 @@ describe('useClientErrors', () => {
47
53
  it('displays nothing by default', () => {
48
54
  const { result } = renderWrappedHook()
49
55
  const { ClientErrors } = result.current
50
- const node = wrappedShallow(<ClientErrors />)
51
- expect(node).toHaveLength(0)
56
+ wrappedShallow(<ClientErrors />)
57
+
58
+ expect(screen.queryByText('QuotaAlert')).not.toBeInTheDocument()
52
59
  })
53
60
 
54
61
  describe('for quota errors', () => {
55
- const findQuotaAlert = node => {
56
- return node.at(0).dive()
57
- }
58
62
  const setup = async () => {
59
63
  const client = createCozyClient()
60
64
  const response = new Response(null, {
@@ -81,21 +85,9 @@ describe('useClientErrors', () => {
81
85
  }
82
86
 
83
87
  it('displays a a QuotaAlert', async () => {
84
- const { node } = await setup()
85
- expect(node).toHaveLength(1)
86
- expect(findQuotaAlert(node).type().name).toEqual('QuotaAlert')
87
- })
88
-
89
- it('can be dismissed', async () => {
90
- const { node, result, client } = await setup()
91
- const quotaAlert = findQuotaAlert(node)
92
- const onClose = quotaAlert.props().onClose
93
- act(() => onClose())
88
+ await setup()
94
89
 
95
- // re-render ClientErrors returned by the hook
96
- const { ClientErrors } = result.current
97
- const updatedNode = wrappedShallow(<ClientErrors />, client)
98
- expect(updatedNode.at(0).length).toBe(0)
90
+ expect(screen.queryByText('QuotaAlert')).toBeInTheDocument()
99
91
  })
100
92
  })
101
93
  })
package/react/index.js CHANGED
@@ -125,6 +125,8 @@ export { default as Thumbnail } from './Thumbnail'
125
125
  export { default as ButtonBase } from './ButtonBase'
126
126
  export { default as QualificationGrid } from './QualificationGrid'
127
127
  export { default as QualificationItem } from './QualificationItem'
128
+ export { default as QualificationIconStack } from './QualificationIconStack'
129
+ export { default as QualificationModal } from './QualificationModal'
128
130
  export { default as Timeline } from './Timeline'
129
131
  export { default as TimelineConnector } from './TimelineConnector'
130
132
  export { default as TimelineContent } from './TimelineContent'