cozy-bar 0.0.0-development
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/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/cozy-bar.min.js +77 -0
- package/dist/cozy-bar.min.js.map +1 -0
- package/package.json +165 -0
- package/src/assets/icons/16/icon-storage-16.svg +3 -0
- package/src/assets/icons/24/icon-arrow-left.svg +3 -0
- package/src/assets/icons/32/icon-claudy.svg +1 -0
- package/src/assets/icons/apps/icon-collect.svg +25 -0
- package/src/assets/icons/apps/icon-drive.svg +17 -0
- package/src/assets/icons/apps/icon-market-soon.svg +25 -0
- package/src/assets/icons/apps/icon-photos.svg +19 -0
- package/src/assets/icons/apps/icon-soon.svg +21 -0
- package/src/assets/icons/apps/icon-store.svg +19 -0
- package/src/assets/icons/claudyActions/icon-bills.svg +6 -0
- package/src/assets/icons/claudyActions/icon-laptop.svg +7 -0
- package/src/assets/icons/claudyActions/icon-phone.svg +8 -0
- package/src/assets/icons/claudyActions/icon-question-mark.svg +6 -0
- package/src/assets/icons/comingsoon/icon-bank.svg +12 -0
- package/src/assets/icons/comingsoon/icon-sante.svg +12 -0
- package/src/assets/icons/comingsoon/icon-store.svg +6 -0
- package/src/assets/icons/icon-cozy.svg +3 -0
- package/src/assets/icons/icon-shield.svg +3 -0
- package/src/assets/icons/spinner.svg +4 -0
- package/src/assets/sprites/icon-apps.svg +1 -0
- package/src/assets/sprites/icon-cozy-home.svg +16 -0
- package/src/components/Apps/AppItem.jsx +117 -0
- package/src/components/Apps/AppItemPlaceholder.jsx +12 -0
- package/src/components/Apps/AppNavButtons.jsx +94 -0
- package/src/components/Apps/AppsContent.jsx +91 -0
- package/src/components/Apps/ButtonCozyHome.jsx +30 -0
- package/src/components/Apps/ButtonCozyHome.spec.jsx +53 -0
- package/src/components/Apps/IconCozyHome.jsx +38 -0
- package/src/components/Apps/index.jsx +72 -0
- package/src/components/Banner.jsx +41 -0
- package/src/components/Bar.jsx +295 -0
- package/src/components/Bar.spec.jsx +133 -0
- package/src/components/Claudy.jsx +81 -0
- package/src/components/ClaudyIcon.jsx +18 -0
- package/src/components/Drawer.jsx +227 -0
- package/src/components/Drawer.spec.jsx +98 -0
- package/src/components/SearchBar.jsx +358 -0
- package/src/components/Settings/SettingsContent.jsx +163 -0
- package/src/components/Settings/StorageData.jsx +29 -0
- package/src/components/Settings/helper.js +8 -0
- package/src/components/Settings/index.jsx +220 -0
- package/src/components/StorageIcon.jsx +16 -0
- package/src/components/SupportModal.jsx +59 -0
- package/src/components/__snapshots__/Bar.spec.jsx.snap +302 -0
- package/src/config/claudyActions.json +20 -0
- package/src/config/persistWhitelist.json +4 -0
- package/src/dom.js +80 -0
- package/src/index.jsx +242 -0
- package/src/index.spec.jsx +34 -0
- package/src/lib/api/helpers.js +13 -0
- package/src/lib/api/index.jsx +145 -0
- package/src/lib/exceptions.js +89 -0
- package/src/lib/expiringMemoize.js +13 -0
- package/src/lib/icon.js +77 -0
- package/src/lib/intents.js +16 -0
- package/src/lib/logger.js +11 -0
- package/src/lib/middlewares/appsI18n.js +57 -0
- package/src/lib/realtime.js +43 -0
- package/src/lib/reducers/apps.js +175 -0
- package/src/lib/reducers/apps.spec.js +59 -0
- package/src/lib/reducers/content.js +50 -0
- package/src/lib/reducers/context.js +86 -0
- package/src/lib/reducers/index.js +73 -0
- package/src/lib/reducers/locale.js +22 -0
- package/src/lib/reducers/settings.js +111 -0
- package/src/lib/reducers/theme.js +48 -0
- package/src/lib/reducers/unserializable.js +26 -0
- package/src/lib/stack-client.js +401 -0
- package/src/lib/stack.js +79 -0
- package/src/lib/store/index.js +44 -0
- package/src/locales/de.json +57 -0
- package/src/locales/en.json +57 -0
- package/src/locales/es.json +57 -0
- package/src/locales/fr.json +57 -0
- package/src/locales/it.json +57 -0
- package/src/locales/ja.json +57 -0
- package/src/locales/nl_NL.json +57 -0
- package/src/locales/pl.json +57 -0
- package/src/locales/ru.json +57 -0
- package/src/locales/sq.json +57 -0
- package/src/locales/zh_CN.json +57 -0
- package/src/proptypes/index.js +10 -0
- package/src/queries/index.js +16 -0
- package/src/styles/apps.css +248 -0
- package/src/styles/banner.css +64 -0
- package/src/styles/bar.css +106 -0
- package/src/styles/base.css +21 -0
- package/src/styles/claudy.css +98 -0
- package/src/styles/drawer.css +126 -0
- package/src/styles/index.styl +33 -0
- package/src/styles/indicators.css +58 -0
- package/src/styles/nav.css +81 -0
- package/src/styles/navigation_item.css +34 -0
- package/src/styles/searchbar.css +156 -0
- package/src/styles/settings.css +34 -0
- package/src/styles/storage.css +22 -0
- package/src/styles/supportModal.css +20 -0
- package/src/styles/theme.styl +25 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/* global __PIWIK_TRACKER_URL__ __PIWIK_SITEID__ __PIWIK_DIMENSION_ID_APP__ */
|
|
2
|
+
|
|
3
|
+
import React, { Component } from 'react'
|
|
4
|
+
import { connect } from 'react-redux'
|
|
5
|
+
|
|
6
|
+
import { translate } from 'cozy-ui/react/I18n'
|
|
7
|
+
import Icon from 'cozy-ui/react/Icon'
|
|
8
|
+
import {
|
|
9
|
+
shouldEnableTracking,
|
|
10
|
+
getTracker,
|
|
11
|
+
configureTracker
|
|
12
|
+
} from 'cozy-ui/react/helpers/tracker'
|
|
13
|
+
import { isMobileApp } from 'cozy-device-helper'
|
|
14
|
+
|
|
15
|
+
import { ButtonCozyHome } from 'components/Apps/ButtonCozyHome'
|
|
16
|
+
import Banner from 'components/Banner'
|
|
17
|
+
import Drawer from 'components/Drawer'
|
|
18
|
+
import Settings from 'components/Settings'
|
|
19
|
+
import Apps from 'components/Apps'
|
|
20
|
+
import SearchBar from 'components/SearchBar'
|
|
21
|
+
import Claudy from 'components/Claudy'
|
|
22
|
+
import SupportModal from 'components/SupportModal'
|
|
23
|
+
import {
|
|
24
|
+
getTheme,
|
|
25
|
+
hasFetched,
|
|
26
|
+
getContent,
|
|
27
|
+
isCurrentApp,
|
|
28
|
+
fetchApps,
|
|
29
|
+
fetchContext,
|
|
30
|
+
fetchSettingsData,
|
|
31
|
+
shouldEnableClaudy,
|
|
32
|
+
getWebviewContext
|
|
33
|
+
} from 'lib/reducers'
|
|
34
|
+
|
|
35
|
+
/* Generated with node_modules/.bin/svgr src/assets/sprites/icon-apps.svg */
|
|
36
|
+
function SvgIconApps(props) {
|
|
37
|
+
return (
|
|
38
|
+
<svg width={16} height={16} {...props}>
|
|
39
|
+
<path
|
|
40
|
+
d="M0 0h4v4H0V0zm0 6h4v4H0V6zm0 6h4v4H0v-4zM6 0h4v4H6V0zm0 6h4v4H6V6zm0 6h4v4H6v-4zm6-12h4v4h-4V0zm0 6h4v4h-4V6zm0 6h4v4h-4v-4z"
|
|
41
|
+
fillRule="evenodd"
|
|
42
|
+
/>
|
|
43
|
+
</svg>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class Bar extends Component {
|
|
48
|
+
constructor(props) {
|
|
49
|
+
super(props)
|
|
50
|
+
this.state = {
|
|
51
|
+
claudyFired: false, // true to fire claudy (used by the drawer)
|
|
52
|
+
claudyOpened: false,
|
|
53
|
+
drawerVisible: false,
|
|
54
|
+
usageTracker: null,
|
|
55
|
+
supportDisplayed: false,
|
|
56
|
+
searchBarEnabled: props.isDrive && !props.isPublic && !isMobileApp()
|
|
57
|
+
}
|
|
58
|
+
this.fetchApps = this.fetchApps.bind(this)
|
|
59
|
+
this.fetchInitialData = this.fetchInitialData.bind(this)
|
|
60
|
+
this.handleTokenRefreshed = this.handleTokenRefreshed.bind(this)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
componentDidMount() {
|
|
64
|
+
if (shouldEnableTracking()) {
|
|
65
|
+
this.initPiwikTracker()
|
|
66
|
+
}
|
|
67
|
+
this.fetchInitialData()
|
|
68
|
+
|
|
69
|
+
const cozyClient = this.props.cozyClient
|
|
70
|
+
cozyClient.on('tokenRefreshed', this.handleTokenRefreshed)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
componentWillUnmount() {
|
|
74
|
+
const cozyClient = this.props.cozyClient
|
|
75
|
+
cozyClient.removeListener('tokenRefreshed', this.handleTokenRefreshed)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
componentDidUpdate(prevProps, prevState) {
|
|
79
|
+
if (
|
|
80
|
+
!this.props.hasFetchedApps &&
|
|
81
|
+
this.state.drawerVisible &&
|
|
82
|
+
prevState.drawerVisible !== this.state.drawerVisible
|
|
83
|
+
) {
|
|
84
|
+
this.fetchApps()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
handleTokenRefreshed() {
|
|
89
|
+
this.fetchInitialData()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
initPiwikTracker() {
|
|
93
|
+
const trackerInstance = getTracker(
|
|
94
|
+
__PIWIK_TRACKER_URL__,
|
|
95
|
+
__PIWIK_SITEID__,
|
|
96
|
+
false,
|
|
97
|
+
false
|
|
98
|
+
)
|
|
99
|
+
configureTracker({
|
|
100
|
+
appDimensionId: __PIWIK_DIMENSION_ID_APP__,
|
|
101
|
+
app: 'Cozy Bar',
|
|
102
|
+
heartbeat: 0
|
|
103
|
+
})
|
|
104
|
+
this.setState({ usageTracker: trackerInstance })
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
fetchApps() {
|
|
108
|
+
this.props.fetchApps()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fetchInitialData() {
|
|
112
|
+
if (this.props.isPublic) {
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
this.props.fetchContext()
|
|
116
|
+
this.props.fetchSettingsData(false)
|
|
117
|
+
this.fetchApps()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
toggleDrawer = () => {
|
|
121
|
+
// don't allow to toggle the drawer if claudy opened or is opening
|
|
122
|
+
if (this.state.claudyOpened || this.state.claudyFired) return
|
|
123
|
+
const drawerVisible = !this.state.drawerVisible
|
|
124
|
+
// don't wait for transitionend if displaying
|
|
125
|
+
if (drawerVisible) this.props.onDrawer(drawerVisible)
|
|
126
|
+
this.setState({ drawerVisible })
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
toggleClaudy = (isFromDrawer = false) => {
|
|
130
|
+
if (!this.props.claudyEnabled) return
|
|
131
|
+
const { usageTracker, claudyOpened } = this.state
|
|
132
|
+
if (isFromDrawer && !claudyOpened) {
|
|
133
|
+
// if opened from drawer
|
|
134
|
+
// reset to toggle via the Claudy component
|
|
135
|
+
return this.setState({ claudyFired: true })
|
|
136
|
+
}
|
|
137
|
+
if (this.state.claudyFired) this.setState({ claudyFired: false })
|
|
138
|
+
if (usageTracker) {
|
|
139
|
+
usageTracker.push([
|
|
140
|
+
'trackEvent',
|
|
141
|
+
'Claudy',
|
|
142
|
+
claudyOpened ? 'close' : 'open',
|
|
143
|
+
'claudy'
|
|
144
|
+
])
|
|
145
|
+
}
|
|
146
|
+
this.setState({ claudyOpened: !claudyOpened })
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
toggleSupport = () => {
|
|
150
|
+
const { supportDisplayed } = this.state
|
|
151
|
+
this.setState({ supportDisplayed: !supportDisplayed })
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
renderCenter() {
|
|
155
|
+
const { appName, appNamePrefix, appSlug, iconPath, isPublic } = this.props
|
|
156
|
+
return (
|
|
157
|
+
<Apps
|
|
158
|
+
appName={appName}
|
|
159
|
+
appNamePrefix={appNamePrefix}
|
|
160
|
+
appSlug={appSlug}
|
|
161
|
+
iconPath={iconPath}
|
|
162
|
+
isPublic={isPublic}
|
|
163
|
+
/>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
renderLeft = () => {
|
|
168
|
+
const { t, isPublic, webviewContext } = this.props
|
|
169
|
+
|
|
170
|
+
if (webviewContext) {
|
|
171
|
+
return <ButtonCozyHome webviewContext={webviewContext} />
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// data-tutorial attribute allows to be targeted in an application tutorial
|
|
175
|
+
return !isPublic ? (
|
|
176
|
+
<button
|
|
177
|
+
type="button"
|
|
178
|
+
className="coz-bar-btn coz-bar-burger"
|
|
179
|
+
onClick={this.toggleDrawer}
|
|
180
|
+
data-tutorial="apps-mobile"
|
|
181
|
+
>
|
|
182
|
+
<Icon icon={SvgIconApps} width={16} height={16} color="currentColor" />
|
|
183
|
+
<span className="coz-bar-hidden">{t('drawer')}</span>
|
|
184
|
+
</button>
|
|
185
|
+
) : null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
renderRight = () => {
|
|
189
|
+
const { isPublic } = this.props
|
|
190
|
+
return !isPublic ? (
|
|
191
|
+
<Settings
|
|
192
|
+
toggleSupport={this.toggleSupport}
|
|
193
|
+
onLogOut={this.props.onLogOut}
|
|
194
|
+
/>
|
|
195
|
+
) : null
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
render() {
|
|
199
|
+
const {
|
|
200
|
+
claudyFired,
|
|
201
|
+
claudyOpened,
|
|
202
|
+
drawerVisible,
|
|
203
|
+
searchBarEnabled,
|
|
204
|
+
supportDisplayed,
|
|
205
|
+
usageTracker
|
|
206
|
+
} = this.state
|
|
207
|
+
|
|
208
|
+
const {
|
|
209
|
+
theme,
|
|
210
|
+
themeOverrides,
|
|
211
|
+
barLeft,
|
|
212
|
+
barRight,
|
|
213
|
+
barCenter,
|
|
214
|
+
barSearch,
|
|
215
|
+
claudyEnabled,
|
|
216
|
+
onDrawer,
|
|
217
|
+
isPublic,
|
|
218
|
+
onLogOut,
|
|
219
|
+
userActionRequired
|
|
220
|
+
} = this.props
|
|
221
|
+
|
|
222
|
+
const {
|
|
223
|
+
primaryColor: pColor,
|
|
224
|
+
primaryContrastTextColor: pctColor
|
|
225
|
+
} = themeOverrides
|
|
226
|
+
const pStyle = pColor ? { '--cozBarThemePrimaryColor': pColor } : {}
|
|
227
|
+
const pctStyle = pctColor
|
|
228
|
+
? { '--cozBarThemePrimaryContrastTextColor': pctColor }
|
|
229
|
+
: {}
|
|
230
|
+
const themeStyle = { ...pStyle, ...pctStyle }
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<div className={`coz-bar-wrapper coz-theme-${theme}`} style={themeStyle}>
|
|
234
|
+
<div id="cozy-bar-modal-dom-place" />
|
|
235
|
+
<div className="coz-bar-container">
|
|
236
|
+
{barLeft || this.renderLeft()}
|
|
237
|
+
{barCenter || this.renderCenter()}
|
|
238
|
+
<div className="u-flex-grow">
|
|
239
|
+
{barSearch || (searchBarEnabled ? <SearchBar /> : null)}
|
|
240
|
+
</div>
|
|
241
|
+
{barRight || this.renderRight()}
|
|
242
|
+
{!isPublic ? (
|
|
243
|
+
<Drawer
|
|
244
|
+
visible={drawerVisible}
|
|
245
|
+
onClose={this.toggleDrawer}
|
|
246
|
+
onClaudy={
|
|
247
|
+
(claudyEnabled && (() => this.toggleClaudy(true))) || false
|
|
248
|
+
}
|
|
249
|
+
isClaudyLoading={claudyFired}
|
|
250
|
+
drawerListener={() => onDrawer(drawerVisible)}
|
|
251
|
+
toggleSupport={this.toggleSupport}
|
|
252
|
+
onLogOut={onLogOut}
|
|
253
|
+
/>
|
|
254
|
+
) : null}
|
|
255
|
+
{claudyEnabled && (
|
|
256
|
+
<Claudy
|
|
257
|
+
usageTracker={usageTracker}
|
|
258
|
+
claudyFired={claudyFired}
|
|
259
|
+
onToggle={() => this.toggleClaudy(false)}
|
|
260
|
+
opened={claudyOpened}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
{supportDisplayed && <SupportModal onClose={this.toggleSupport} />}
|
|
264
|
+
</div>
|
|
265
|
+
{userActionRequired && <Banner {...userActionRequired} />}
|
|
266
|
+
</div>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export const mapStateToProps = state => ({
|
|
272
|
+
theme: getTheme(state).name,
|
|
273
|
+
themeOverrides: getTheme(state).overrides,
|
|
274
|
+
barLeft: getContent(state, 'left'),
|
|
275
|
+
barRight: getContent(state, 'right'),
|
|
276
|
+
barCenter: getContent(state, 'center'),
|
|
277
|
+
barSearch: getContent(state, 'search'),
|
|
278
|
+
isDrive: isCurrentApp(state, { slug: 'drive' }),
|
|
279
|
+
claudyEnabled: shouldEnableClaudy(state),
|
|
280
|
+
hasFetchedApps: hasFetched(state),
|
|
281
|
+
webviewContext: getWebviewContext(state)
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
export const mapDispatchToProps = dispatch => ({
|
|
285
|
+
fetchApps: () => dispatch(fetchApps()),
|
|
286
|
+
fetchContext: () => dispatch(fetchContext()),
|
|
287
|
+
fetchSettingsData: displayBusy => dispatch(fetchSettingsData(displayBusy))
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
export default translate()(
|
|
291
|
+
connect(
|
|
292
|
+
mapStateToProps,
|
|
293
|
+
mapDispatchToProps
|
|
294
|
+
)(Bar)
|
|
295
|
+
)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { shallow } from 'enzyme'
|
|
3
|
+
import { isFlagshipApp, isMobileApp } from 'cozy-device-helper'
|
|
4
|
+
import toJson from 'enzyme-to-json'
|
|
5
|
+
import reducers from 'lib/reducers'
|
|
6
|
+
import CozyClient from 'cozy-client'
|
|
7
|
+
|
|
8
|
+
import { Bar, mapStateToProps, mapDispatchToProps } from './Bar'
|
|
9
|
+
|
|
10
|
+
jest.mock('cozy-device-helper', () => ({
|
|
11
|
+
...require.requireActual('cozy-device-helper'),
|
|
12
|
+
isMobileApp: jest.fn(),
|
|
13
|
+
isFlagshipApp: jest.fn()
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
describe('Bar', () => {
|
|
17
|
+
let props, client
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
jest.resetAllMocks()
|
|
20
|
+
jest.spyOn(Bar.prototype, 'fetchApps')
|
|
21
|
+
isMobileApp.mockReturnValue(false)
|
|
22
|
+
client = new CozyClient({})
|
|
23
|
+
props = {
|
|
24
|
+
fetchContext: jest.fn().mockResolvedValue({}),
|
|
25
|
+
fetchApps: jest.fn().mockResolvedValue([]),
|
|
26
|
+
fetchSettingsData: jest.fn().mockResolvedValue({}),
|
|
27
|
+
theme: 'default',
|
|
28
|
+
themeOverrides: {},
|
|
29
|
+
t: x => x,
|
|
30
|
+
cozyClient: client
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
Bar.prototype.fetchApps.mockRestore()
|
|
36
|
+
isFlagshipApp.mockClear()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should fetch data when mounted', () => {
|
|
40
|
+
shallow(<Bar {...props} />)
|
|
41
|
+
expect(props.fetchContext).toHaveBeenCalled()
|
|
42
|
+
expect(props.fetchApps).toHaveBeenCalled()
|
|
43
|
+
expect(props.fetchSettingsData).toHaveBeenCalled()
|
|
44
|
+
expect(Bar.prototype.fetchApps).toHaveBeenCalled()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should not fetch data if public', () => {
|
|
48
|
+
shallow(<Bar {...props} isPublic={true} />)
|
|
49
|
+
expect(props.fetchContext).not.toHaveBeenCalled()
|
|
50
|
+
expect(props.fetchApps).not.toHaveBeenCalled()
|
|
51
|
+
expect(props.fetchSettingsData).not.toHaveBeenCalled()
|
|
52
|
+
expect(Bar.prototype.fetchApps).not.toHaveBeenCalled()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should not fetch apps if hasFetchedApps is true', () => {
|
|
56
|
+
const barWrapper = shallow(<Bar {...props} hasFetchedApps={true} />)
|
|
57
|
+
jest.resetAllMocks()
|
|
58
|
+
barWrapper.setState({ drawerVisible: true }) // to call componentdidUpdate
|
|
59
|
+
expect(props.fetchApps).not.toHaveBeenCalled()
|
|
60
|
+
expect(Bar.prototype.fetchApps).not.toHaveBeenCalled()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should re-fetch apps if apps are not fetched and drawer opens', () => {
|
|
64
|
+
const barWrapper = shallow(<Bar {...props} />)
|
|
65
|
+
expect(Bar.prototype.fetchApps).toHaveBeenCalledTimes(1)
|
|
66
|
+
expect(props.fetchApps).toHaveBeenCalledTimes(1)
|
|
67
|
+
barWrapper.setState({ drawerVisible: true })
|
|
68
|
+
expect(props.fetchApps).toHaveBeenCalledTimes(2)
|
|
69
|
+
expect(Bar.prototype.fetchApps).toHaveBeenCalledTimes(2)
|
|
70
|
+
barWrapper.setState({ drawerVisible: true, usageTracker: {} })
|
|
71
|
+
expect(props.fetchApps).toHaveBeenCalledTimes(2)
|
|
72
|
+
expect(Bar.prototype.fetchApps).toHaveBeenCalledTimes(2)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should change theme', () => {
|
|
76
|
+
const barWrapper = shallow(<Bar {...props} theme="primary" />)
|
|
77
|
+
expect(toJson(barWrapper)).toMatchSnapshot()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should change allow theme overrides', () => {
|
|
81
|
+
const barWrapper = shallow(
|
|
82
|
+
<Bar
|
|
83
|
+
{...props}
|
|
84
|
+
theme="primary"
|
|
85
|
+
themeOverrides={{ primaryColor: 'red' }}
|
|
86
|
+
/>
|
|
87
|
+
)
|
|
88
|
+
expect(toJson(barWrapper)).toMatchSnapshot()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should display the Searchbar', () => {
|
|
92
|
+
const barWrapper = shallow(<Bar {...props} isDrive isPublic={false} />)
|
|
93
|
+
expect(toJson(barWrapper)).toMatchSnapshot()
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should not display searchbar if we are on mobile', () => {
|
|
97
|
+
isMobileApp.mockReturnValue(true)
|
|
98
|
+
const barWrapper = shallow(<Bar {...props} isDrive isPublic={false} />)
|
|
99
|
+
expect(toJson(barWrapper)).toMatchSnapshot()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should not display searchbar if we are not on Cozy Drive', () => {
|
|
103
|
+
isMobileApp.mockReturnValue(true)
|
|
104
|
+
const barWrapper = shallow(
|
|
105
|
+
<Bar {...props} isDrive={false} isPublic={false} />
|
|
106
|
+
)
|
|
107
|
+
expect(toJson(barWrapper)).toMatchSnapshot()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should not display searchbar if we are not on a public page', () => {
|
|
111
|
+
isMobileApp.mockReturnValue(true)
|
|
112
|
+
const barWrapper = shallow(<Bar {...props} isDrive isPublic={true} />)
|
|
113
|
+
expect(toJson(barWrapper)).toMatchSnapshot()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('should have correct state props provided by the store with the initial state', () => {
|
|
117
|
+
const initialState = reducers(undefined, {})
|
|
118
|
+
expect(mapStateToProps(initialState)).toMatchSnapshot()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should have correct dispatch props provided by the store', () => {
|
|
122
|
+
expect(mapDispatchToProps(jest.fn())).toMatchSnapshot()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should call re-fetch data when token is refreshed', () => {
|
|
126
|
+
shallow(<Bar {...props} isDrive={false} isPublic={false} />)
|
|
127
|
+
client.emit('tokenRefreshed')
|
|
128
|
+
expect(props.fetchContext).toHaveBeenCalledTimes(2)
|
|
129
|
+
expect(props.fetchApps).toHaveBeenCalledTimes(2)
|
|
130
|
+
expect(props.fetchSettingsData).toHaveBeenCalledTimes(2)
|
|
131
|
+
expect(Bar.prototype.fetchApps).toHaveBeenCalledTimes(2)
|
|
132
|
+
})
|
|
133
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React, { Component } from 'react'
|
|
2
|
+
import { getIntents } from 'lib/stack'
|
|
3
|
+
|
|
4
|
+
class Claudy extends Component {
|
|
5
|
+
constructor(props, context) {
|
|
6
|
+
super(props)
|
|
7
|
+
this.store = context.barStore
|
|
8
|
+
this.state = {
|
|
9
|
+
isLoading: false,
|
|
10
|
+
isActive: false
|
|
11
|
+
}
|
|
12
|
+
this.intents = getIntents()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
16
|
+
if (nextProps.claudyFired) this.toggle()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
componentWillReceiveProps(nextProps) {
|
|
20
|
+
return this.UNSAFE_componentWillReceiveProps(nextProps)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
toggle = () => {
|
|
24
|
+
if (!this.props.opened && !this.intentWrapperRef.childNodes.length) {
|
|
25
|
+
this.setState({ isLoading: true })
|
|
26
|
+
// init Claudy intent
|
|
27
|
+
this.intents
|
|
28
|
+
.create('CLAUDY', 'io.cozy.settings', {
|
|
29
|
+
exposeIntentFrameRemoval: true
|
|
30
|
+
})
|
|
31
|
+
.start(this.intentWrapperRef, () => {
|
|
32
|
+
this.setState({ isLoading: false, isActive: true })
|
|
33
|
+
this.props.onToggle() // toggle claudy when the intent is loaded
|
|
34
|
+
})
|
|
35
|
+
.then(({ removeIntentIframe }) => {
|
|
36
|
+
// exposeFrameRemoval intent event
|
|
37
|
+
// remove the intent frame at the end of the menu closing transition
|
|
38
|
+
const closedListener = e => {
|
|
39
|
+
if (e.propertyName === 'transform') {
|
|
40
|
+
removeIntentIframe()
|
|
41
|
+
this.setState({ isActive: false })
|
|
42
|
+
e.target.removeEventListener('transitionend', closedListener)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
this.intentWrapperRef.addEventListener(
|
|
46
|
+
'transitionend',
|
|
47
|
+
closedListener,
|
|
48
|
+
false
|
|
49
|
+
)
|
|
50
|
+
this.props.onToggle()
|
|
51
|
+
})
|
|
52
|
+
} else {
|
|
53
|
+
this.setState({ isActive: !this.state.isActive })
|
|
54
|
+
this.props.onToggle()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
render() {
|
|
59
|
+
const { opened } = this.props
|
|
60
|
+
const { isLoading, isActive } = this.state
|
|
61
|
+
return (
|
|
62
|
+
<div className={`coz-claudy ${opened ? 'coz-claudy--opened' : ''}`}>
|
|
63
|
+
<button
|
|
64
|
+
type="button"
|
|
65
|
+
className="coz-claudy-icon coz-bar-hide-sm"
|
|
66
|
+
data-claudy-opened={isActive}
|
|
67
|
+
data-claudy-loading={isLoading}
|
|
68
|
+
onClick={this.toggle}
|
|
69
|
+
/>
|
|
70
|
+
<div
|
|
71
|
+
className="coz-claudy-intent-wrapper"
|
|
72
|
+
ref={wrapper => {
|
|
73
|
+
this.intentWrapperRef = wrapper
|
|
74
|
+
}}
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default Claudy
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
function SvgIconClaudy(props) {
|
|
4
|
+
return (
|
|
5
|
+
<svg viewBox="0 0 32 32" {...props}>
|
|
6
|
+
<path
|
|
7
|
+
d="M22 12h-7c-.6 0-1 .4-1 1s.4 1 1 1h7c.6 0 1-.4 1-1s-.4-1-1-1zm3-5H15c-.5 0-1 .4-1 1s.4 1 1 1h10c.5 0 1-.4 1-1s-.4-1-1-1z"
|
|
8
|
+
fill="none"
|
|
9
|
+
/>
|
|
10
|
+
<path
|
|
11
|
+
fill="#FFF"
|
|
12
|
+
d="M31 1H9c-.6 0-1 .5-1 1v15c2.2 0 4.1 1.2 5.2 3H18v1.9c0 .6.4.7.8.4l3.3-2.3H31c.6 0 1-.5 1-1V2c0-.6-.4-1-1-1zm-9 13h-7c-.6 0-1-.4-1-1s.4-1 1-1h7c.6 0 1 .4 1 1s-.4 1-1 1zm3-5H15c-.6 0-1-.4-1-1s.5-1 1-1h10c.6 0 1 .4 1 1s-.5 1-1 1zM10.4 26.7c0 .1-.7 1.3-2.4 1.3-1.8 0-2.4-1.2-2.4-1.3-.1-.2 0-.5.2-.7.2-.1.5 0 .7.2 0 0 .4.7 1.6.7 1.1 0 1.5-.7 1.6-.7.1-.2.4-.3.7-.2.1.2.2.5 0 .7M12 23c0-2.2-1.8-4-4-4s-4 1.8-4 4c-2.2 0-4 1.8-4 4s1.8 4 4 4h8c2.2 0 4-1.8 4-4s-1.8-4-4-4"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default SvgIconClaudy
|