-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
735435c
commit 3e393a7
Showing
11 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import Header from './components/Header/index' | ||
|
||
export default Header |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Header | ||
|
||
Header is a canonical component that any VTEX app can import. | ||
|
||
To import it into your code: | ||
|
||
```js | ||
import { Header } from 'vtex.store-components' | ||
``` | ||
Also, you can import as a dependency in your `manifest.json` | ||
```json | ||
dependencies: { | ||
"vtex.store-components: 1.x" | ||
} | ||
``` | ||
|
||
## Usage | ||
|
||
You can use it in your code like a React component with the jsx tag: `<Header />`. | ||
|
||
```html | ||
<Header /> | ||
``` | ||
|
||
Or, you can add in your `pages.json`: | ||
```json | ||
"store/header": { | ||
"component": "vtex.store-components/Header" | ||
} | ||
``` | ||
|
||
See an example at [Dreamstore](https://github.com/vtex-apps/dreamstore-theme/blob/master/pages/pages.json#L7) and [Store](https://github.com/vtex-apps/store/blob/master/react/StoreTemplate.js#L14) apps |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import React from 'react' | ||
import { mountWithIntl, loadTranslation, setLocale } from 'enzyme-react-intl' | ||
import Header from '../components/Header' | ||
|
||
describe('Header test', () => { | ||
let wrapperPT | ||
|
||
beforeEach(() => { | ||
global.__RUNTIME__ = { account: 'store' } | ||
loadTranslation('../locales/pt-BR.json') | ||
setLocale('pt-BR') | ||
wrapperPT = mountWithIntl(<Header />) | ||
}) | ||
|
||
it('should have 4 div elements', () => { | ||
expect(wrapperPT.find('div').length).toBe(4) | ||
}) | ||
|
||
it('should simulate search', done => { | ||
window.location.assign = jest.fn() | ||
const seachString = 'product' | ||
const input = wrapperPT.find('input') | ||
const button = wrapperPT.find('[data-test-id="search"]').first() | ||
process.nextTick(() => { | ||
try { | ||
input.simulate('change', { target: { value: seachString } }) | ||
button.simulate('click') | ||
wrapperPT.update() | ||
expect(window.location.assign).toBeCalledWith(`/${seachString}/s`) | ||
} catch (e) { | ||
return done(e) | ||
} | ||
done() | ||
}) | ||
}) | ||
|
||
it('should simulate click on cart', () => { | ||
window.location.assign = jest.fn() | ||
const button = wrapperPT.find('[data-test-id="cart"]').first() | ||
button.simulate('click') | ||
expect(window.location.assign).toBeCalledWith('/checkout/#/cart') | ||
}) | ||
|
||
it('should simulate filled input', () => { | ||
const seachString = 'product' | ||
const input = wrapperPT.find('input') | ||
input.simulate('change', { target: { value: seachString } }) | ||
expect(wrapperPT).toMatchSnapshot() | ||
}) | ||
|
||
it('should render correctly pt-BR', () => { | ||
expect(wrapperPT).toMatchSnapshot() | ||
}) | ||
|
||
it('should render correctly en-US', () => { | ||
loadTranslation('../locales/en-US.json') | ||
setLocale('en-US') | ||
const wrapperEN = mountWithIntl(<Header />) | ||
expect(wrapperEN).toMatchSnapshot() | ||
}) | ||
|
||
it('should render correctly es-AR', () => { | ||
loadTranslation('../locales/es-AR.json') | ||
setLocale('es-AR') | ||
const wrapperES = mountWithIntl(<Header />) | ||
expect(wrapperES).toMatchSnapshot() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Component } from 'react' | ||
import ReactDOM from 'react-dom' | ||
import PropTypes from 'prop-types' | ||
|
||
let _modalRoot | ||
|
||
const getModalRoot = () => { | ||
if (typeof _modalRoot === 'undefined') { | ||
_modalRoot = document.createElement('div') | ||
_modalRoot.classList.add('vtex-modal-root') | ||
|
||
document.body.appendChild(_modalRoot) | ||
} | ||
|
||
return _modalRoot | ||
} | ||
|
||
class Modal extends Component { | ||
static propTypes = { | ||
children: PropTypes.node, | ||
} | ||
|
||
constructor(props) { | ||
super(props) | ||
this.el = document.createElement('div') | ||
this.modalRoot = getModalRoot() | ||
} | ||
|
||
componentDidMount() { | ||
this.modalRoot.appendChild(this.el) | ||
} | ||
|
||
componentWillUnmount() { | ||
this.modalRoot.removeChild(this.el) | ||
} | ||
|
||
render() { | ||
return ReactDOM.createPortal( | ||
this.props.children, | ||
this.el | ||
) | ||
} | ||
} | ||
|
||
export default Modal | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import React from 'react' | ||
import PropTypes from 'prop-types' | ||
import { intlShape, injectIntl } from 'react-intl' | ||
|
||
import Logo from '../../../Logo' | ||
import SearchBar from '../../../SearchBar' | ||
|
||
import { ExtensionPoint } from 'render' | ||
|
||
const TopMenu = ({ logoUrl, logoTitle, intl, fixed, offsetTop }) => { | ||
const translate = id => intl.formatMessage({ id: `header.${id}` }) | ||
|
||
return ( | ||
<div | ||
className={`${ | ||
fixed ? 'fixed shadow-5' : '' | ||
} z-999 flex items-center w-100 flex-wrap pa4 pa5-ns bg-white tl`} | ||
style={{top: `${offsetTop}px`}} | ||
> | ||
<div className="flex w-100 w-auto-ns pa4-ns items-center"> | ||
<a className="link b f3 near-black tc tl-ns serious-black flex-auto" href="/"> | ||
<Logo | ||
url={logoUrl} | ||
title={logoTitle} | ||
/> | ||
</a> | ||
</div> | ||
<div className="flex-auto pr2 pa4"> | ||
<SearchBar | ||
placeholder={translate('search-placeholder')} | ||
emptyPlaceholder={translate('search-emptyPlaceholder')} | ||
/> | ||
</div> | ||
<div className="pr2 bg-black"> | ||
<ExtensionPoint id="minicart" /> | ||
<ExtensionPoint id="login" /> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
TopMenu.propTypes = { | ||
logoUrl: PropTypes.string, | ||
logoTitle: PropTypes.string, | ||
intl: intlShape.isRequired, | ||
fixed: PropTypes.bool, | ||
} | ||
|
||
TopMenu.defaultProps = { | ||
fixed: false, | ||
} | ||
|
||
export default injectIntl(TopMenu) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.vtex-header { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import React, { Component } from 'react' | ||
import PropTypes from 'prop-types' | ||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl' | ||
|
||
import Modal from './components/Modal' | ||
import TopMenu from './components/TopMenu' | ||
|
||
import { Alert } from 'vtex.styleguide' | ||
import { ExtensionPoint } from 'render' | ||
|
||
export const TOAST_TIMEOUT = 3000 | ||
|
||
class Header extends Component { | ||
state = { | ||
isAddToCart: false, | ||
hasError: false, | ||
error: null, | ||
showMenuPopup: false, | ||
} | ||
|
||
static propTypes = { | ||
name: PropTypes.string, | ||
logoUrl: PropTypes.string, | ||
logoTitle: PropTypes.string, | ||
intl: intlShape.isRequired, | ||
} | ||
|
||
componentDidMount() { | ||
this._timeouts = [] | ||
document.addEventListener('message:error', this.handleError) | ||
document.addEventListener('item:add', this.handleItemAdd) | ||
document.addEventListener('scroll', this.handleScroll) | ||
} | ||
|
||
componentWillUnmount() { | ||
if (this._timeouts.length !== 0) { | ||
this._timeouts.map(el => { | ||
clearTimeout(el) | ||
}) | ||
} | ||
|
||
document.removeEventListener('message:error', this.handleError) | ||
document.removeEventListener('item:add', this.handleItemAdd) | ||
document.removeEventListener('scroll', this.handleScroll) | ||
} | ||
|
||
handleError = e => { | ||
this.setState({ hasError: true, error: e }) | ||
const timeOut = window.setTimeout(() => { | ||
this.setState({ hasError: false }) | ||
}, TOAST_TIMEOUT) | ||
|
||
this._timeouts.push(timeOut) | ||
} | ||
|
||
handleItemAdd = () => { | ||
this.setState({ isAddToCart: !this.state.isAddToCart }) | ||
const timeOut = window.setTimeout(() => { | ||
this._timeoutId = undefined | ||
this.setState({ isAddToCart: !this.state.isAddToCart }) | ||
}, TOAST_TIMEOUT) | ||
|
||
this._timeouts.push(timeOut) | ||
} | ||
|
||
handleScroll = () => { | ||
if (!this._el) { | ||
return | ||
} | ||
|
||
const scroll = window.scrollY | ||
const { scrollHeight } = this._el | ||
|
||
if (scroll < scrollHeight && this.state.showMenuPopup) { | ||
this.setState({ | ||
showMenuPopup: false, | ||
}) | ||
} else if (scroll >= scrollHeight) { | ||
this.setState({ | ||
showMenuPopup: true, | ||
}) | ||
} | ||
} | ||
|
||
render() { | ||
const { account } = global.__RUNTIME__ | ||
const { name, logoUrl, logoTitle } = this.props | ||
const { isAddToCart, hasError, showMenuPopup, error } = this.state | ||
const offsetTop = (this._el && this._el.offsetTop) || 0 | ||
return ( | ||
<div | ||
className="vtex-header relative z-2 w-100 shadow-5" | ||
ref={e => { | ||
this._el = e | ||
}} | ||
> | ||
<div className="z-2 items-center w-100 top-0 bg-white tl"> | ||
<ExtensionPoint id="menu-link" /> | ||
</div> | ||
<TopMenu | ||
logoUrl={logoUrl} | ||
logoTitle={logoTitle} | ||
/> | ||
<ExtensionPoint id="category-menu" /> | ||
{showMenuPopup && ( | ||
<Modal> | ||
<TopMenu | ||
logoUrl={logoUrl} | ||
logoTitle={logoTitle} | ||
offsetTop={offsetTop} | ||
fixed | ||
/> | ||
</Modal> | ||
)} | ||
<div | ||
className="flex flex-column items-center fixed w-100" | ||
style={{ top: offsetTop + 120 }} | ||
> | ||
{isAddToCart && ( | ||
<div className="pa2 mw9"> | ||
<Alert type="success"> | ||
<FormattedMessage id="header.buy-success" /> | ||
</Alert> | ||
</div> | ||
)} | ||
|
||
{hasError && ( | ||
<div className="pa2 mw9"> | ||
<Alert type="error">{error.detail.message}</Alert> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
) | ||
} | ||
} | ||
|
||
export default injectIntl(Header) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters