This repository has been archived by the owner on Aug 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Create a replication of the HsApp layout to wrap any story with. Replicate the HSApp nav. Add decorator * Add tests for HsApp component
- Loading branch information
Showing
13 changed files
with
407 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,125 @@ | ||
import styled from '../styled' | ||
import baseStyles from '../../../src/styles/resets/baseStyles.css.js' | ||
import { getColor } from '../../../src/styles/utilities/color' | ||
import Dropdown from '../Dropdown/DropdownV2' | ||
import Avatar from '../Avatar' | ||
|
||
export const config = { | ||
headerHeight: '54px', | ||
} | ||
|
||
export const HsAppUI = styled('div')` | ||
${baseStyles}; | ||
` | ||
|
||
export const AppLayoutUI = styled('div')` | ||
align-items: stretch; | ||
display: flex; | ||
justify-content: space-between; | ||
` | ||
export const AppContainerUI = styled('div')` | ||
align-items: stretch; | ||
box-sizing: border-box; | ||
display: flex; | ||
flex: 1; | ||
justify-content: space-between; | ||
min-height: calc((100vh) - (${config.headerHeight})); | ||
width: 100%; | ||
` | ||
|
||
export const HeaderUI = styled('div')` | ||
background: ${getColor('blue.700')}; | ||
padding: 7px 10px 7px 4px; | ||
position: relative; | ||
height: ${config.headerHeight}; | ||
color: ${getColor('blue.300')}; | ||
display: flex; | ||
align-items: center; | ||
` | ||
|
||
export const LogoUI = styled('span')` | ||
padding: 0 10px 0 16px; | ||
color: ${getColor('blue.300')}; | ||
&:hover { | ||
cursor: pointer; | ||
color: white; | ||
} | ||
` | ||
|
||
export const SecIconUI = styled('span')` | ||
padding: 0 18px; | ||
color: ${getColor('blue.300')}; | ||
&.less-padding { | ||
padding-right: 10px; | ||
} | ||
&:hover { | ||
cursor: pointer; | ||
color: white; | ||
} | ||
` | ||
|
||
export const SecondaryNavUI = styled('span')` | ||
margin-left: auto; | ||
display: flex; | ||
align-items: center; | ||
` | ||
|
||
export const NavUI = styled('span')` | ||
display: flex; | ||
flew-direction: row; | ||
` | ||
|
||
export const AvatarUI = styled(Avatar)` | ||
margin: 0 10px; | ||
` | ||
|
||
export const DropdownTriggerUI = styled('span')` | ||
display: inline-flex; | ||
align-items: center; | ||
height: 40px; | ||
padding: 0 10px 0 17px; | ||
color: ${getColor('blue.300')}; | ||
&:hover { | ||
color: white; | ||
} | ||
.c-Icon { | ||
top: 2px; | ||
margin-left: 2px; | ||
} | ||
` | ||
|
||
export const DropdownUI = styled(Dropdown)` | ||
.is-open ${DropdownTriggerUI} { | ||
color: white; | ||
} | ||
` | ||
|
||
export const SidenavUI = styled('div')` | ||
background: ${getColor('grey.300')}; | ||
width: 250px; | ||
border-right: 1px solid ${getColor('grey.500')}; | ||
height: 100%; | ||
` | ||
|
||
export const ContentUI = styled('div')` | ||
box-shadow: -1px 0 0 #d6dde3, 1px 0 0 #d6dde3, 0 1px 0 #d6dde3; | ||
box-sizing: border-box; | ||
flex: 1; | ||
flex-basis: auto; | ||
max-width: 100%; | ||
min-width: 0; | ||
position: relative; | ||
z-index: 2; | ||
background: #f9fafa; | ||
/* background: ${getColor('grey.400')}; */ | ||
padding: 20px; | ||
` | ||
|
||
export const InnerContentUI = styled('div')` | ||
background: #fff; | ||
padding: 20px; | ||
` |
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,67 @@ | ||
import * as React from 'react' | ||
|
||
import { | ||
HsAppUI, | ||
ContentUI, | ||
InnerContentUI, | ||
AppLayoutUI, | ||
AppContainerUI, | ||
} from './HsApp.css' | ||
import Nav from './Nav' | ||
import Sidenav from './Sidenav' | ||
|
||
export interface Props { | ||
children?: any | ||
withInnerWrapper?: boolean | ||
sidenavComponent?: any | ||
contentComponent?: any | ||
navComponent?: any | ||
} | ||
|
||
class HsApp extends React.PureComponent<Props> { | ||
static defaultProps = { | ||
withInnerWrapper: true, | ||
sidenavComponent: null, | ||
navComponent: null, | ||
contentComponent: null, | ||
} | ||
|
||
static Nav = Nav | ||
static Sidenav = Sidenav | ||
|
||
componentDidMount() { | ||
document.body.classList.add('with-hsapp-wrapper') | ||
} | ||
|
||
componentWillUnmount() { | ||
document.body.classList.remove('with-hsapp-wrapper') | ||
} | ||
|
||
renderChildren() { | ||
const { children, withInnerWrapper } = this.props | ||
if (withInnerWrapper) { | ||
return <InnerContentUI>{children}</InnerContentUI> | ||
} | ||
return children | ||
} | ||
|
||
render() { | ||
const { sidenavComponent, contentComponent, navComponent } = this.props | ||
return ( | ||
<HsAppUI className="c-HsApp"> | ||
{navComponent ? navComponent : <HsApp.Nav />} | ||
<AppLayoutUI> | ||
<AppContainerUI> | ||
{sidenavComponent ? sidenavComponent : <HsApp.Sidenav />} | ||
{contentComponent ? ( | ||
contentComponent | ||
) : ( | ||
<ContentUI>{this.renderChildren()}</ContentUI> | ||
)} | ||
</AppContainerUI> | ||
</AppLayoutUI> | ||
</HsAppUI> | ||
) | ||
} | ||
} | ||
export default HsApp |
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,9 @@ | ||
export const COMPONENT_KEY: { | ||
HsApp: string | ||
Nav: string | ||
Sidenav: string | ||
} = { | ||
HsApp: 'HsApp', | ||
Nav: 'PageNav', | ||
Sidenav: 'PageSidenav', | ||
} |
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,64 @@ | ||
import * as React from 'react' | ||
import { | ||
HeaderUI, | ||
LogoUI, | ||
NavUI, | ||
DropdownTriggerUI, | ||
DropdownUI, | ||
SecIconUI, | ||
SecondaryNavUI, | ||
AvatarUI, | ||
} from './HsApp.css' | ||
import Icon from '../Icon' | ||
import Text from '../Text' | ||
import { createSpec, faker } from '@helpscout/helix' | ||
|
||
const ItemSpec = createSpec({ | ||
id: faker.random.uuid(), | ||
label: faker.company.companyName(), | ||
value: faker.company.companyName(), | ||
}) | ||
|
||
export interface Props {} | ||
|
||
class Nav extends React.PureComponent<Props> { | ||
renderDropdowns() { | ||
return ['Mailboxes', 'Docs', 'Reports', 'Manage'].map(d => ( | ||
<DropdownUI | ||
key={d} | ||
items={ItemSpec.generate(8)} | ||
trigger={ | ||
<DropdownTriggerUI> | ||
<Text size="14">{d}</Text>{' '} | ||
<Icon name="caret-down" inline size="16" /> | ||
</DropdownTriggerUI> | ||
} | ||
/> | ||
)) | ||
} | ||
render() { | ||
return ( | ||
<HeaderUI> | ||
<LogoUI> | ||
<Icon name="hs-logo" size="20" /> | ||
</LogoUI> | ||
<NavUI>{this.renderDropdowns()}</NavUI> | ||
<SecondaryNavUI> | ||
<SecIconUI> | ||
<Icon name="bell" size="20" /> | ||
</SecIconUI> | ||
<SecIconUI className="less-padding"> | ||
<Icon name="buoy" size="20" withCaret /> | ||
</SecIconUI> | ||
<SecIconUI> | ||
<Icon name="search" size="20" /> | ||
</SecIconUI> | ||
</SecondaryNavUI> | ||
|
||
<AvatarUI shape="rounded" size="xs" /> | ||
</HeaderUI> | ||
) | ||
} | ||
} | ||
|
||
export default Nav |
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,22 @@ | ||
# HsApp | ||
|
||
This component wrap an any other component with the Helpscout App layout | ||
|
||
## Example | ||
|
||
```jsx | ||
<HsApp> | ||
<Page> | ||
<Page.Card>content</Page.Card> | ||
</Page> | ||
</HsApp> | ||
``` | ||
|
||
## Props | ||
|
||
| Prop | Type | Description | | ||
| ---------------- | ------ | -------------------------------------------------------------------------------------------------------------------- | | ||
| withInnerWrapper | `bool` | When active, the content will be wrapped with a white box, otherwise it will be the default HS App background color. | | ||
| sidenavComponent | `any` | Overwrite the sidebar with this component | | ||
| navComponent | `any` | Overwrite the top navigation with this component | | ||
| contentComponent | `any` | Overwrite the actual content with this component | |
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,12 @@ | ||
import * as React from 'react' | ||
import { SidenavUI } from './HsApp.css' | ||
|
||
export interface Props {} | ||
|
||
class Sidenav extends React.PureComponent<Props> { | ||
render() { | ||
return <SidenavUI /> | ||
} | ||
} | ||
|
||
export default Sidenav |
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,71 @@ | ||
import React from 'react' | ||
import { mount } from 'enzyme' | ||
import HsApp from '../HsApp' | ||
|
||
import { InnerContentUI } from '../HsApp.css' | ||
|
||
describe('ClassName', () => { | ||
test('Has default className', () => { | ||
const wrapper = mount(<HsApp />) | ||
expect(wrapper.getDOMNode().classList.contains('c-HsApp')).toBe(true) | ||
}) | ||
|
||
test('Adds/removes className on document.body when mounting/unmounting', () => { | ||
const wrapper = mount(<HsApp />) | ||
expect(document.body.classList.contains('with-hsapp-wrapper')).toBe(true) | ||
wrapper.unmount() | ||
expect(document.body.classList.contains('with-hsapp-wrapper')).toBe(false) | ||
}) | ||
}) | ||
|
||
describe('Rendering', () => { | ||
test('Renders inner wrapper by default', () => { | ||
const wrapper = mount(<HsApp />) | ||
expect(wrapper.find(InnerContentUI).length).toBe(1) | ||
}) | ||
|
||
test('Does not render inner wrapper', () => { | ||
const wrapper = mount(<HsApp withInnerWrapper={false} />) | ||
expect(wrapper.find(InnerContentUI).length).toBe(0) | ||
}) | ||
|
||
test('Renders custom nav', () => { | ||
const CustomNav = props => { | ||
return <span className="customNav" /> | ||
} | ||
const wrapper = mount(<HsApp navComponent={<CustomNav />} />) | ||
const HsAppNav = HsApp.Nav | ||
expect(wrapper.find(HsAppNav).length).toBe(0) | ||
expect(wrapper.find('.customNav').length).toBe(1) | ||
}) | ||
|
||
test('Renders custom sidenav', () => { | ||
const CustomSidenav = props => { | ||
return <span className="customSidenav" /> | ||
} | ||
const wrapper = mount(<HsApp sidenavComponent={<CustomSidenav />} />) | ||
const HsAppSidenav = HsApp.Sidenav | ||
expect(wrapper.find(HsAppSidenav).length).toBe(0) | ||
expect(wrapper.find('.customSidenav').length).toBe(1) | ||
}) | ||
|
||
test('Renders custom content', () => { | ||
const CustomContent = props => { | ||
return <span className="customContent" /> | ||
} | ||
const wrapper = mount(<HsApp contentComponent={<CustomContent />} />) | ||
expect(wrapper.find('.customContent').length).toBe(1) | ||
}) | ||
|
||
test('Renders children', () => { | ||
const ChildrenComponent = props => { | ||
return <span className="children" /> | ||
} | ||
const wrapper = mount( | ||
<HsApp> | ||
<ChildrenComponent /> | ||
</HsApp> | ||
) | ||
expect(wrapper.find('.children').length).toBe(1) | ||
}) | ||
}) |
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,8 @@ | ||
import { propConnect } from '../PropProvider' | ||
import HsApp from './HsApp' | ||
import { COMPONENT_KEY } from './HsApp.utils' | ||
|
||
HsApp.Nav = propConnect(COMPONENT_KEY.Nav)(HsApp.Nav) | ||
HsApp.Sidenav = propConnect(COMPONENT_KEY.Sidenav)(HsApp.Sidenav) | ||
|
||
export default propConnect(COMPONENT_KEY.HsApp)(HsApp) |
Oops, something went wrong.