Skip to content

Commit 1fac117

Browse files
authored
[Chakra v3 upgrade] @W-18672580 update: migrate offline components from v2 to v3 (#2684)
* update: migrate offline components from v2 to v3 * add unit test coverage report & cleanup * class name clean up * use renderWithRouter to test offline-boundary * use newer chakra UI component instead of basic html tags * wrap user.click with act * clean up comments
1 parent 76b2e61 commit 1fac117

File tree

5 files changed

+111
-125
lines changed

5 files changed

+111
-125
lines changed

packages/extension-chakra-storefront/jest.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ module.exports = {
4949
createTestGlob('pages/home'),
5050
createTestGlob('pages/cart'),
5151
createTestGlob('pages/product-list'),
52+
createTestGlob('components/offline-banner'),
53+
createTestGlob('components/offline-boundary'),
5254
// createTestGlob('pages/login'), // TODO: enable after Account page has been migrated
5355
createTestGlob('pages/login-redirect'),
5456
createTestGlob('pages/social-login-redirect'),
@@ -99,6 +101,7 @@ module.exports = {
99101
'non-pwa/**/*.{js,jsx}',
100102
'worker/**/*.{js,jsx}',
101103
'scripts/generator/*.{js,jsx}',
104+
'src/**/*.{js,jsx}',
102105
'!app/pages/test-container/**/*.{js,jsx}',
103106
'!app/utils/test-utils.js',
104107
'!app/mocks/*.js',

packages/extension-chakra-storefront/src/components/offline-banner/index.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import React from 'react'
99
import {useIntl} from 'react-intl'
1010

1111
// Components
12-
import {Alert, AlertDescription, Flex} from '@chakra-ui/react'
12+
import {Alert, Flex} from '@chakra-ui/react'
1313

1414
// Icons
1515
import {AlertIcon} from '../../components/icons'
@@ -20,17 +20,17 @@ import {AlertIcon} from '../../components/icons'
2020
const OfflineBanner = ({...props}) => {
2121
const intl = useIntl()
2222
return (
23-
<Alert status="warning" {...props}>
23+
<Alert.Root status="warning" colorPalette="blue" {...props}>
2424
<Flex align="center">
2525
<AlertIcon mr={2} />
26-
<AlertDescription>
26+
<Alert.Title>
2727
{intl.formatMessage({
2828
id: 'offline_banner.description.browsing_offline_mode',
2929
defaultMessage: "You're currently browsing in offline mode"
3030
})}
31-
</AlertDescription>
31+
</Alert.Title>
3232
</Flex>
33-
</Alert>
33+
</Alert.Root>
3434
)
3535
}
3636

packages/extension-chakra-storefront/src/components/offline-boundary/index.jsx

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@
77
import React from 'react'
88
import PropTypes from 'prop-types'
99
import {withRouter} from 'react-router-dom'
10-
import {Button} from '@chakra-ui/react'
10+
import {Button, Box, Heading, Text} from '@chakra-ui/react'
1111
import {AlertIcon} from '../../components/icons'
1212

13-
// import Button from '@salesforce/pwa-kit-react-sdk/components/button'
14-
// import Icon from '@salesforce/pwa-kit-react-sdk/components/icon'
15-
1613
/**
1714
* OfflineBoundary is a React Error boundary that catches errors thrown when
1815
* dynamically loading pages and renders a fallback.
@@ -68,24 +65,14 @@ class OfflineBoundary extends React.Component {
6865
return (
6966
<React.Fragment>
7067
{chunkLoadError ? (
71-
<div className="c-offline-boundary u-direction-column u-text-align-center u-padding-top u-padding-bottom">
68+
<Box>
7269
<AlertIcon />
73-
74-
<h1 className="u-margin-bottom-md u-text-family">
75-
You are currently offline
76-
</h1>
77-
78-
<p className="u-margin-bottom-lg">
70+
<Heading>You are currently offline</Heading>
71+
<Text>
7972
{"We couldn't load the next page on this connection. Please try again."}
80-
</p>
81-
82-
<Button
83-
className="u-width-block-full pw--primary qa-retry-button"
84-
onClick={() => this.clearError()}
85-
>
86-
Retry Connection
87-
</Button>
88-
</div>
73+
</Text>
74+
<Button onClick={() => this.clearError()}>Retry Connection</Button>
75+
</Box>
8976
) : (
9077
children
9178
)}

packages/extension-chakra-storefront/src/components/offline-boundary/index.test.js

Lines changed: 90 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,27 @@
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77
import React from 'react'
8-
import {screen} from '@testing-library/react'
9-
// import userEvent from '@testing-library/user-event'
10-
11-
import OfflineBoundary from '../../components/offline-boundary/index'
8+
import {screen, act} from '@testing-library/react'
9+
import userEvent from '@testing-library/user-event'
10+
import {ChakraProvider} from '@chakra-ui/react'
11+
import theme from '../../theme'
1212
import {renderWithRouter} from '../../utils/test-utils'
1313

14-
// class ChunkLoadError extends Error {
15-
// constructor(...params) {
16-
// // Pass remaining arguments (including vendor specific ones) to parent constructor
17-
// super(...params)
18-
// this.name = 'ChunkLoadError'
19-
// }
20-
// }
14+
import OfflineBoundary, {UnwrappedOfflineBoundary} from '../../components/offline-boundary/index'
15+
16+
// Custom render function that combines Router and Chakra contexts
17+
const renderWithRouterAndChakra = (component) => {
18+
const ComponentWithChakra = () => <ChakraProvider value={theme}>{component}</ChakraProvider>
19+
return renderWithRouter(<ComponentWithChakra />)
20+
}
21+
22+
class ChunkLoadError extends Error {
23+
constructor(...params) {
24+
// Pass remaining arguments (including vendor specific ones) to parent constructor
25+
super(...params)
26+
this.name = 'ChunkLoadError'
27+
}
28+
}
2129

2230
describe('The OfflineBoundary', () => {
2331
beforeEach(() => {
@@ -31,7 +39,7 @@ describe('The OfflineBoundary', () => {
3139
})
3240

3341
test('should render its children', () => {
34-
renderWithRouter(
42+
renderWithRouterAndChakra(
3543
<OfflineBoundary isOnline={true}>
3644
<div id="child">child</div>
3745
</OfflineBoundary>
@@ -40,84 +48,74 @@ describe('The OfflineBoundary', () => {
4048
expect(screen.getByText(/child/i)).toBeInTheDocument()
4149
})
4250

43-
// TODO: Fix flaky/broken test
44-
// eslint-disable-next-line jest/no-commented-out-tests
45-
// test('should render the error splash when a child throws a chunk load error', () => {
46-
// const ThrowingComponent = () => {
47-
// throw new ChunkLoadError()
48-
// }
49-
// renderWithRouter(
50-
// <OfflineBoundary isOnline={true}>
51-
// <div>
52-
// <ThrowingComponent />
53-
// <div id="child">child</div>
54-
// </div>
55-
// </OfflineBoundary>
56-
// )
57-
58-
// expect(screen.getByRole('img', {name: /offline cloud/i})).toBeInTheDocument()
59-
// expect(
60-
// screen.getByRole('heading', {name: /you are currently offline/i})
61-
// ).toBeInTheDocument()
62-
// expect(screen.queryByText(/child/i)).not.toBeInTheDocument()
63-
// })
64-
65-
// TODO: Fix flaky/broken test
66-
// eslint-disable-next-line jest/no-commented-out-tests
67-
// test('should re-throw errors that are not chunk load errors', () => {
68-
// const ThrowingComponent = () => {
69-
// throw new Error('Anything else')
70-
// }
71-
// expect(() => {
72-
// renderWithRouter(
73-
// <OfflineBoundary isOnline={true}>
74-
// <div>
75-
// <ThrowingComponent />
76-
// <div id="child">child</div>
77-
// </div>
78-
// </OfflineBoundary>
79-
// )
80-
// }).toThrow()
81-
// })
82-
83-
// TODO: Fix flaky/broken test
84-
// eslint-disable-next-line jest/no-commented-out-tests
85-
// test('should attempt to reload the page when the user clicks retry', () => {
86-
// let firstRender = true
87-
// const ThrowingOnceComponent = () => {
88-
// if (firstRender) {
89-
// firstRender = false
90-
// throw new ChunkLoadError()
91-
// } else {
92-
// return <div id="child">child</div>
93-
// }
94-
// }
95-
// renderWithRouter(
96-
// <OfflineBoundary isOnline={true}>
97-
// <ThrowingOnceComponent />
98-
// </OfflineBoundary>
99-
// )
100-
101-
// expect(screen.getByRole('img', {name: /offline cloud/i})).toBeInTheDocument()
102-
// expect(
103-
// screen.getByRole('heading', {name: /you are currently offline/i})
104-
// ).toBeInTheDocument()
105-
// expect(screen.queryByText(/child/i)).not.toBeInTheDocument()
106-
107-
// userEvent.click(screen.getByRole('button', {name: /retry connection/i}))
108-
// expect(screen.getByText(/child/i)).toBeInTheDocument()
109-
// expect(screen.queryByRole('img', {name: /offline cloud/i})).not.toBeInTheDocument()
110-
// expect(
111-
// screen.queryByRole('heading', {name: /you are currently offline/i})
112-
// ).not.toBeInTheDocument()
113-
// })
114-
115-
// TODO: Fix flaky/broken test
116-
// eslint-disable-next-line jest/no-commented-out-tests
117-
// test('should derive state from a chunk load error', () => {
118-
// const derived = UnwrappedOfflineBoundary.getDerivedStateFromError(
119-
// new ChunkLoadError('test')
120-
// )
121-
// expect(derived).toEqual({chunkLoadError: true})
122-
// })
51+
test('should render the error splash when a child throws a chunk load error', () => {
52+
const ThrowingComponent = () => {
53+
throw new ChunkLoadError()
54+
}
55+
renderWithRouterAndChakra(
56+
<OfflineBoundary isOnline={true}>
57+
<div>
58+
<ThrowingComponent />
59+
<div id="child">child</div>
60+
</div>
61+
</OfflineBoundary>
62+
)
63+
64+
expect(screen.getByRole('img', {hidden: true})).toBeInTheDocument()
65+
expect(
66+
screen.getByRole('heading', {name: /you are currently offline/i})
67+
).toBeInTheDocument()
68+
expect(screen.queryByText(/child/i)).not.toBeInTheDocument()
69+
})
70+
71+
test('should re-throw errors that are not chunk load errors', () => {
72+
const ThrowingComponent = () => {
73+
throw new Error('Anything else')
74+
}
75+
expect(() => {
76+
renderWithRouterAndChakra(
77+
<OfflineBoundary isOnline={true}>
78+
<div>
79+
<ThrowingComponent />
80+
<div id="child">child</div>
81+
</div>
82+
</OfflineBoundary>
83+
)
84+
}).toThrow()
85+
})
86+
87+
test('should call clearError when retry button is clicked', async () => {
88+
const user = userEvent.setup()
89+
const ThrowingComponent = () => {
90+
throw new ChunkLoadError()
91+
}
92+
const clearErrorSpy = jest.spyOn(UnwrappedOfflineBoundary.prototype, 'clearError')
93+
94+
renderWithRouterAndChakra(
95+
<OfflineBoundary isOnline={true}>
96+
<ThrowingComponent />
97+
</OfflineBoundary>
98+
)
99+
100+
expect(screen.getByRole('img', {hidden: true})).toBeInTheDocument()
101+
expect(
102+
screen.getByRole('heading', {name: /you are currently offline/i})
103+
).toBeInTheDocument()
104+
105+
const retryButton = screen.getByRole('button', {name: /retry connection/i})
106+
await act(async () => {
107+
await user.click(retryButton)
108+
})
109+
110+
expect(clearErrorSpy).toHaveBeenCalled()
111+
112+
clearErrorSpy.mockRestore()
113+
})
114+
115+
test('should derive state from a chunk load error', () => {
116+
const derived = UnwrappedOfflineBoundary.getDerivedStateFromError(
117+
new ChunkLoadError('test')
118+
)
119+
expect(derived).toEqual({chunkLoadError: true})
120+
})
123121
})

packages/extension-chakra-storefront/src/components/with-layout/with-layout.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ import CheckoutHeader from '../../pages/checkout/partials/checkout-header'
4242
import CheckoutFooter from '../../pages/checkout/partials/checkout-footer'
4343
import Footer from '../footer'
4444
import Header from '../header'
45-
// import OfflineBanner from '../offline-banner'
46-
// import OfflineBoundary from '../offline-boundary'
45+
import OfflineBanner from '../offline-banner'
46+
import OfflineBoundary from '../offline-boundary'
4747
import Seo from '../seo'
4848
import ScrollToTop from '../scroll-to-top'
4949

@@ -274,7 +274,6 @@ const withLayout = <P extends object>(WrappedComponent: React.ComponentType<P>)
274274
</Seo>
275275

276276
<ScrollToTop />
277-
278277
<Box id="app" display="flex" flexDirection="column" flex={1}>
279278
{/*TODO: recreating this component because @chakra-ui/skip-nav does not have V3 version*/}
280279
{/*<SkipNavLink zIndex="skipLink">Skip to Content</SkipNavLink>*/}
@@ -314,7 +313,7 @@ const withLayout = <P extends object>(WrappedComponent: React.ComponentType<P>)
314313
<CheckoutHeader />
315314
)}
316315
</Box>
317-
{/*{!isOnline && <OfflineBanner />}*/}
316+
{!isOnline && <OfflineBanner />}
318317
<AddToCartModalProvider>
319318
{/*TODO: recreating this component because @chakra-ui/skip-nav does not have V3 version*/}
320319
{/*<SkipNavContent*/}
@@ -333,10 +332,9 @@ const withLayout = <P extends object>(WrappedComponent: React.ComponentType<P>)
333332
flexDirection="column"
334333
flex="1"
335334
>
336-
{/*<OfflineBoundary isOnline={false}>*/}
337-
338-
<WrappedComponent {...(props as P)} />
339-
{/*</OfflineBoundary>*/}
335+
<OfflineBoundary isOnline={isOnline}>
336+
<WrappedComponent {...(props as P)} />
337+
</OfflineBoundary>
340338
</Box>
341339
{/*</SkipNavContent>*/}
342340

0 commit comments

Comments
 (0)