Skip to content

Commit

Permalink
Fix bug where offline warning message was not shown in the AppBar. (#33)
Browse files Browse the repository at this point in the history
* Fix bug where offline warning message was not shown in the AppBar.  Also moved AppBar to using useScrollTrigger for hiding.

* Fetch search suggestions the first time it opens.

* Fix failing test

* Update src/AppBar.js

Co-Authored-By: Leo <[email protected]>

* added test for profile

* Fix for homepage with query params

* fetchFromAPI tests

* Add PWA tests

* SearchProvider test change

Co-authored-by: Leo <[email protected]>
  • Loading branch information
markbrocato and leotoll authored Feb 3, 2020
1 parent 68c6950 commit 590cd22
Show file tree
Hide file tree
Showing 13 changed files with 332 additions and 367 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-storefront",
"version": "7.2.0",
"version": "7.2.1",
"description": "Build and deploy e-commerce progressive web apps (PWAs) in record time.",
"module": "./index.js",
"license": "Apache-2.0",
Expand Down
197 changes: 51 additions & 146 deletions src/AppBar.js
Original file line number Diff line number Diff line change
@@ -1,179 +1,84 @@
import React, { useState, useEffect, useRef, useContext } from 'react'
import PropTypes from 'prop-types'
import clsx from 'clsx'
import { Toolbar } from '@material-ui/core'
import React, { useContext } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import { useAmp } from 'next/amp'
import { AppBar as MUIAppBar, Container, Toolbar, useScrollTrigger, Slide } from '@material-ui/core'
import PropTypes from 'prop-types'
import PWAContext from './PWAContext'

export const styles = theme => ({
const useStyles = makeStyles(theme => ({
/**
* Styles applied to the root element when user is not `offline`.
* Styles applied to the root element.
*/
root: {
height: 64,
boxSizing: 'content-box',
position: 'relative',
boxSizing: 'border-box',
backgroundColor: theme.palette.background.default,
boxShadow: 'none',
borderBottom: `1px solid ${theme.palette.divider}`,
zIndex: theme.zIndex.modal + 10,
height: theme.headerHeight,
display: 'flex',
flexDirection: 'column',
alignItems: 'stretch',
},

/**
* Styles applied to the root element when Amp is used.
* Styles applied to the spacer that fills the height behind the floating toolbar.
*/
withAmp: {
zIndex: theme.zIndex.amp.modal + 1,
spacer: {
boxSizing: 'border-box',
height: theme.headerHeight,
},

/**
* Styles applied to the offline warning element.
*/
offline: {
textAlign: 'center',
backgroundColor: '#f34c4c',
color: 'white',
},

/**
* Styles applied to the `Toolbar` element.
*/
toolbar: {
height: '64px',
maxWidth: theme.maxWidth,
justifyContent: 'space-between',
alignItems: 'center',
flex: 1,
},

/**
* Styles applied to the element wrapped around the `Toolbar`.
*/
wrap: {
borderBottom: `1px solid ${theme.palette.divider}`,
position: 'absolute',
top: 0,
left: 0,
right: 0,
backgroundColor: theme.palette.background.paper,
zIndex: theme.zIndex.modal + 10,
display: 'flex',
justifyContent: 'center',
},

/**
* Styles applied to the `Toolbar` wrapper when the `AppBar` is unstuck.
*/
unstuck: {
transform: 'translateY(-100%)',
},

/**
* Styles applied to the `Toolbar` wrapper element when the user has scrolled and the `AppBar`
* will animate back into place.
*/
animate: {
transition: 'transform .15s ease-in',
},

/**
* Styles applied to the `Toolbar` wrapper element the user has scrolled and the `AppBar` is hidden.
*/
hidden: {
position: 'fixed',
zIndex: theme.zIndex.modal + 10,
boxShadow: theme.shadows[2],
top: 0,
left: 0,
right: 0,
},

/**
* Styles applied to the `Toolbar` wrapper element when [`fixed`](#prop-fixed) is `true`.
* Styles applied to the offline warning element.
*/
fixed: {
position: 'fixed',
offline: {
textAlign: 'center',
backgroundColor: '#f34c4c',
zIndex: 999999,
width: '100vw',
color: 'white',
},
})

const useStyles = makeStyles(styles, { name: 'RSFAppBar' })
}))

export default function AppBar({ classes, children, fixed, offlineWarning }) {
export default function AppBar({ children, style, fixed, offlineWarning, classes }) {
const trigger = useScrollTrigger()
classes = useStyles({ classes })

const [state, applyState] = useState({
stuck: false,
hidden: false,
animate: false,
})
const { stuck, hidden, animate } = state
const setState = newState => applyState({ ...state, ...newState })
const lastScrollY = useRef()
const unstickAt = useRef()
const stickAt = useRef()
const { offline } = useContext(PWAContext)
const items = React.Children.toArray(children)

const onScroll = () => {
const height = 64,
{ scrollY } = window,
unstickBufferZone = 30

if (scrollY === 0) {
setState({ hidden: false, stuck: false, animate: false })
} else if (scrollY > height && !hidden) {
setState({ hidden: true })
}

if (scrollY < stickAt.current && !stuck) {
stickAt.current = null
unstickAt.current = scrollY + unstickBufferZone
setState({ stuck: true })
} else if (scrollY > unstickAt.current && stuck) {
unstickAt.current = null
stickAt.current = scrollY - unstickBufferZone
setState({ stuck: false })
}

if (lastScrollY.current > scrollY && stuck) {
unstickAt.current = scrollY + unstickBufferZone
}

if (lastScrollY.current < scrollY && !stuck) {
stickAt.current = scrollY - unstickBufferZone
}
let appBar = (
<MUIAppBar
className={classes.root}
style={{
...style,
}}
>
<Toolbar disableGutters className={classes.toolbar}>
{children}
</Toolbar>
</MUIAppBar>
)

lastScrollY.current = scrollY
if (!fixed) {
appBar = (
<Slide in={!trigger}>
{appBar}
</Slide>
)
}

useEffect(() => {
if (!fixed) {
window.addEventListener('scroll', onScroll, { passive: true })
return () => window.removeEventListener('scroll', onScroll, { passive: true })
}
}, [state])

useEffect(() => {
if (hidden && !animate) {
setTimeout(() => setState({ animate: true }), 100)
}
}, [hidden])

return (
<div>
<>
<div className={classes.spacer} />
{offline && <div className={classes.offline}>{offlineWarning}</div>}
<div className={clsx({ [classes.root]: true, [classes.withAmp]: useAmp() })}>
<div
className={clsx({
[classes.wrap]: true,
[classes.fixed]: fixed,
[classes.hidden]: hidden,
[classes.unstuck]: hidden && !stuck,
[classes.animate]: animate && window.scrollY > 0,
})}
>
<Toolbar disableGutters classes={{ root: classes.toolbar }}>
{items}
</Toolbar>
</div>
</div>
</div>
{appBar}
</>
)
}

Expand Down
23 changes: 12 additions & 11 deletions src/PWA.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useRef } from 'react'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import PWAContext from './PWAContext'
import PropTypes from 'prop-types'
import ErrorBoundary from './ErrorBoundary'
Expand Down Expand Up @@ -28,12 +28,14 @@ const useStyles = makeStyles(styles, { name: 'RSFPWA' })
export default function PWA({ children, errorReporter }) {
useStyles()
const thumbnail = useRef(null)
const [offline, setOffline] = useState(typeof navigator !== 'undefined' && !navigator.onLine)

const context = useMemo(
() => ({
thumbnail,
offline,
}),
[],
[offline],
)

// enable client-side navigation when the user clicks a simple HTML anchor element
Expand All @@ -42,17 +44,16 @@ export default function PWA({ children, errorReporter }) {
useEffect(() => {
context.hydrating = false

// const handleOnline = () => (app.offline = false)
// const handleOffline = () => (app.offline = true)
const handleOnline = () => setOffline(false)
const handleOffline = () => setOffline(true)

// app.offline = !navigator.onLine
// window.addEventListener('online', handleOnline)
// window.addEventListener('offline', handleOffline)
window.addEventListener('online', handleOnline)
window.addEventListener('offline', handleOffline)

// return () => {
// window.removeEventListener('online', handleOnline)
// window.removeEventListener('offline', handleOffline)
// }
return () => {
window.removeEventListener('online', handleOnline)
window.removeEventListener('offline', handleOffline)
}
}, [])

return (
Expand Down
1 change: 1 addition & 0 deletions src/drawer/DrawerCloseButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const styles = theme => ({
right: '10px',
top: '-28px',
zIndex: 1,
color: 'white',
},
/**
* Styles applied to hide the `Fab` button when [`open`](#prop-open) is `false`.
Expand Down
1 change: 1 addition & 0 deletions src/props/fetchFromAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default function fetchFromAPI({ req, asPath }) {
const protocol = server ? (host.startsWith('localhost') ? 'http://' : 'https://') : ''

if (asPath === '/') asPath = ''
if (asPath.startsWith('/?')) asPath = asPath.substring(1)

let uri = `/api${asPath}`

Expand Down
2 changes: 1 addition & 1 deletion src/search/SearchDrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function SearchDrawer({ DrawerComponent, classes, open, onClose,
classes = useStyles({ classes })

return (
<SearchProvider onClose={onClose}>
<SearchProvider onClose={onClose} open={open}>
<DrawerComponent classes={classes} open={open} anchor="bottom" onClose={onClose} fullscreen>
{children}
</DrawerComponent>
Expand Down
7 changes: 4 additions & 3 deletions src/search/SearchProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import useNavigationEvent from '../hooks/useNavigationEvent'

const fetch = fetchLatest(_fetch)

export default function SearchProvider({ children, initialGroups, onClose }) {
export default function SearchProvider({ children, initialGroups, onClose, open }) {
const [state, setState] = useState({
groups: initialGroups,
loading: true,
})

useEffect(() => {
if (state.groups == null) {
if (open && state.groups == null) {
fetchSuggestions('')
}
}, [])
}, [open])

useNavigationEvent(onClose)

Expand Down Expand Up @@ -69,6 +69,7 @@ export default function SearchProvider({ children, initialGroups, onClose }) {
}

SearchProvider.propTypes = {
open: PropTypes.bool,
initialGroups: PropTypes.array,
onClose: PropTypes.func,
}
2 changes: 1 addition & 1 deletion src/search/SearchSuggestions.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, { useContext } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import PropTypes from 'prop-types'
import SearchSuggestionGroup from './SearchSuggestionGroup'
import SearchContext from './SearchContext'
import LoadMask from '../LoadMask'

export const styles = theme => ({
root: {
flex: 1,
position: 'relative',
overflowY: 'auto',
},
group: {
Expand Down
Loading

0 comments on commit 590cd22

Please sign in to comment.