diff --git a/package-lock.json b/package-lock.json
index 06453f057..33edd3906 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@helpscout/hsds-react",
- "version": "2.5.7",
+ "version": "2.5.8-page-beta-0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -10550,7 +10550,7 @@
"dependencies": {
"resolve": {
"version": "1.1.7",
- "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
"integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
"dev": true
}
@@ -12527,7 +12527,7 @@
"dependencies": {
"regexpu-core": {
"version": "1.0.0",
- "resolved": "http://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
"integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
"dev": true,
"requires": {
@@ -14013,7 +14013,7 @@
},
"events": {
"version": "1.1.1",
- "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
"dev": true
},
@@ -16598,7 +16598,7 @@
},
"is-accessor-descriptor": {
"version": "0.1.6",
- "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
"integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
"dev": true,
"requires": {
@@ -16657,7 +16657,7 @@
},
"is-data-descriptor": {
"version": "0.1.4",
- "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
"integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
"dev": true,
"requires": {
@@ -20848,7 +20848,7 @@
},
"globby": {
"version": "6.1.0",
- "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
"dev": true,
"requires": {
@@ -20861,7 +20861,7 @@
"dependencies": {
"pify": {
"version": "2.3.0",
- "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
}
@@ -22456,7 +22456,7 @@
},
"path-browserify": {
"version": "0.0.0",
- "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
"integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
"dev": true
},
@@ -24408,7 +24408,7 @@
},
"raw-loader": {
"version": "0.5.1",
- "resolved": "http://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
"integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=",
"dev": true
},
@@ -25793,11 +25793,6 @@
"integrity": "sha512-M2AelyJDVR/oLnToJLtuDJRBBWUGUvvGigj1411hXhAdyFWqMaqHp7TixW3FpiLuVaikIcR1QL+zqoJoZlOgpg==",
"dev": true
},
- "resizey": {
- "version": "0.0.14",
- "resolved": "https://registry.npmjs.org/resizey/-/resizey-0.0.14.tgz",
- "integrity": "sha512-VR26wbJFSoeBNewfD/7eaolJ05RxxiTuRwvRygt91u8NZCh2pqgviN/R8dO9QLL8ZUMSfzR8g5bNuzbnujc/rw=="
- },
"resolve": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz",
@@ -27492,7 +27487,7 @@
},
"sprintf-js": {
"version": "1.0.3",
- "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
},
@@ -27651,7 +27646,7 @@
},
"string-width": {
"version": "1.0.2",
- "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"requires": {
@@ -28041,7 +28036,7 @@
},
"temp": {
"version": "0.8.3",
- "resolved": "http://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
+ "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
"integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=",
"dev": true,
"requires": {
@@ -28390,7 +28385,7 @@
},
"tty-browserify": {
"version": "0.0.0",
- "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
"dev": true
},
@@ -29127,7 +29122,7 @@
},
"vm-browserify": {
"version": "0.0.4",
- "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
"integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
"dev": true,
"requires": {
diff --git a/package.json b/package.json
index f160a905e..6c4961508 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@helpscout/hsds-react",
- "version": "2.5.7",
+ "version": "2.5.8-page-beta-0",
"private": false,
"main": "dist/index.js",
"module": "dist/index.es.js",
diff --git a/src/components/Page/Card.tsx b/src/components/Page/Card.tsx
index 4dff4c0e1..687aaea87 100644
--- a/src/components/Page/Card.tsx
+++ b/src/components/Page/Card.tsx
@@ -8,22 +8,13 @@ import { COMPONENT_KEY } from './utils'
export interface Props {
children?: any
className?: string
- isResponsive: boolean
}
class Card extends React.PureComponent {
- static defaultProps = {
- isResponsive: false,
- }
-
render() {
- const { children, className, isResponsive, ...rest } = this.props
+ const { children, className, ...rest } = this.props
- const componentClassName = classNames(
- 'c-PageCard',
- isResponsive && 'is-responsive',
- className
- )
+ const componentClassName = classNames('c-PageCard', className)
return (
diff --git a/src/components/Page/Header.tsx b/src/components/Page/Header.tsx
index ea93b36b0..6cf730521 100644
--- a/src/components/Page/Header.tsx
+++ b/src/components/Page/Header.tsx
@@ -3,20 +3,44 @@ import getValidProps from '@helpscout/react-utils/dist/getValidProps'
import Text from '../Text'
import { classNames } from '../../utilities/classNames'
import { namespaceComponent } from '../../utilities/component'
-import { HeaderUI, TitleUI, SubTitleUI } from './styles/Header.css'
-import Heading from './Heading'
+import { HeaderUI, TitleUI, SubTitleUI, HeadingUI } from './styles/Header.css'
import { COMPONENT_KEY } from './utils'
export interface Props {
children?: any
className?: string
isResponsive: boolean
- title: string
+ render?: any
+ title?: string
subtitle?: string
withBorder: boolean
withBottomMargin: boolean
}
+const Title = props => {
+ const { headingLevel, isSecondary, className, children, ...rest } = props
+ const componentClassName = classNames('c-PageHeading', className)
+
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+const Subtitle = ({ children }) => (
+
+ {children}
+
+)
+
class Header extends React.PureComponent {
static defaultProps = {
isResponsive: false,
@@ -27,9 +51,9 @@ class Header extends React.PureComponent {
render() {
const {
- children,
className,
isResponsive,
+ render,
title,
subtitle,
withBorder,
@@ -45,23 +69,16 @@ class Header extends React.PureComponent {
className
)
- const titleMarkup = title && (
-
- {title}
-
- )
- const subtitleMarkup = subtitle && (
-
-
- {subtitle}
-
-
- )
-
return (
- {titleMarkup}
- {subtitleMarkup}
+ {render ? (
+ render({ Title, Subtitle })
+ ) : (
+
+
{title}
+ {subtitle && {subtitle} }
+
+ )}
)
}
diff --git a/src/components/Page/Heading.tsx b/src/components/Page/Heading.tsx
deleted file mode 100644
index 43a3f04df..000000000
--- a/src/components/Page/Heading.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import * as React from 'react'
-import getValidProps from '@helpscout/react-utils/dist/getValidProps'
-import { COMPONENT_KEY } from './utils'
-import { classNames } from '../../utilities/classNames'
-import { namespaceComponent } from '../../utilities/component'
-import { HeadingUI, SecondaryHeadingUI } from './styles/Heading.css'
-
-export interface Props {
- children?: any
- className?: string
- secondary?: boolean
-}
-
-class Heading extends React.PureComponent {
- static defaultProps = {
- secondary: false,
- }
-
- render() {
- const { children, secondary, className, ...rest } = this.props
-
- const componentClassName = classNames(
- 'c-PageHeader__titleHeading',
- secondary && 'c-PageHeader__titleHeading--secondary',
- className
- )
-
- return !secondary ? (
-
- {children}
-
- ) : (
-
- {children}
-
- )
- }
-}
-namespaceComponent(COMPONENT_KEY.Heading)(Heading)
-
-export default Heading
diff --git a/src/components/Page/Page.tsx b/src/components/Page/Page.tsx
index 5eadfe72a..a088e16ac 100644
--- a/src/components/Page/Page.tsx
+++ b/src/components/Page/Page.tsx
@@ -4,7 +4,7 @@ import Actions from './Actions'
import Card from './Card'
import Content from './Content'
import Header from './Header'
-import Heading from './Heading'
+import Section from './Section'
import PropProvider from '../PropProvider'
import { classNames } from '../../utilities/classNames'
import { namespaceComponent } from '../../utilities/component'
@@ -25,19 +25,22 @@ class Page extends React.PureComponent {
static Card = Card
static Content = Content
static Header = Header
- static Heading = Heading
+ static Section = Section
getPropProviderProps = () => {
const { isResponsive } = this.props
return {
Accordion: { isPage: true, isSeamless: true },
- [COMPONENT_KEY.Card]: {
+ [COMPONENT_KEY.Section]: {
isResponsive,
},
[COMPONENT_KEY.Header]: {
isResponsive,
},
+ [COMPONENT_KEY.Content]: {
+ isResponsive,
+ },
}
}
diff --git a/src/components/Page/Section.tsx b/src/components/Page/Section.tsx
new file mode 100644
index 000000000..03860df77
--- /dev/null
+++ b/src/components/Page/Section.tsx
@@ -0,0 +1,37 @@
+import * as React from 'react'
+import { COMPONENT_KEY } from './utils'
+import { classNames } from '../../utilities/classNames'
+import { namespaceComponent } from '../../utilities/component'
+import { SectionUI } from './styles/Section.css'
+
+export interface Props {
+ children?: any
+ className?: string
+ isResponsive?: boolean
+}
+
+class Section extends React.PureComponent {
+ static defaultProps = {
+ isResponsive: false,
+ }
+
+ render() {
+ const { children, className, isResponsive, ...rest } = this.props
+
+ const componentClassName = classNames(
+ 'c-PageSection',
+ isResponsive && 'is-responsive',
+ className
+ )
+
+ return (
+
+ {this.props.children}
+
+ )
+ }
+}
+
+namespaceComponent(COMPONENT_KEY.Section)(Section)
+
+export default Section
diff --git a/src/components/Page/__tests__/Card.test.js b/src/components/Page/__tests__/Card.test.js
index c9da9de5b..24d1e5245 100644
--- a/src/components/Page/__tests__/Card.test.js
+++ b/src/components/Page/__tests__/Card.test.js
@@ -34,17 +34,3 @@ describe('Content', () => {
expect(wrapper.find('div.ron').length).toBe(1)
})
})
-
-describe('Responsive', () => {
- test('Renders responsive styles, if specified', () => {
- const wrapper = mount( )
-
- expect(wrapper.getDOMNode().classList.contains('is-responsive')).toBe(true)
- })
-
- test('Does not render responsive styles, if specified', () => {
- const wrapper = mount( )
-
- expect(wrapper.getDOMNode().classList.contains('is-responsive')).toBe(false)
- })
-})
diff --git a/src/components/Page/__tests__/Header.test.js b/src/components/Page/__tests__/Header.test.js
index d503502c1..21f9c0603 100644
--- a/src/components/Page/__tests__/Header.test.js
+++ b/src/components/Page/__tests__/Header.test.js
@@ -25,36 +25,86 @@ describe('Content', () => {
})
})
-describe('Title', () => {
+describe('Title and Subtitle', () => {
test('Can render a title', () => {
- const wrapper = mount()
+ const wrapper = mount(
+ Channel 4 } />
+ )
- expect(wrapper.text()).toContain('Channel 4')
+ expect(wrapper.find('h1').text()).toContain('Channel 4')
})
-
test('Can render a subtitle', () => {
- const wrapper = mount()
+ const wrapper = mount(
+ Channel 4 }
+ />
+ )
+
+ expect(wrapper.find('span').text()).toContain('Channel 4')
+ })
+
+ test('Title is primary heading (h1) by default', () => {
+ const wrapper = mount(
+ Channel 4 } />
+ )
+
+ expect(wrapper.find('h1').length).toBe(1)
+ })
+
+ test('Title can render h2', () => {
+ const wrapper = mount(
+ Channel 4 }
+ />
+ )
+
+ expect(wrapper.find('h2').length).toBe(1)
+ })
+
+ test('Title can render smaller (secondary)', () => {
+ const wrapper = mount(
+ (
+
+ Channel 4
+
+ )}
+ />
+ )
+
+ expect(wrapper.find('h2').hasClass('is-h4')).toBe(true)
+ })
+
+ test('Can render the title with "title" prop', () => {
+ const wrapper = mount()
expect(wrapper.text()).toContain('Channel 4')
- expect(wrapper.text()).toContain('News team')
})
- test('Title is primary heading (h1)', () => {
+ test('Can render the subtitle with "subtitle" prop', () => {
const wrapper = mount()
- expect(wrapper.find('h1').length).toBe(1)
+ expect(wrapper.text()).toContain('Channel 4')
+ expect(wrapper.text()).toContain('News team')
})
})
describe('Border', () => {
test('Renders a border', () => {
- const wrapper = mount()
+ const wrapper = mount(
+ Channel 4 } />
+ )
expect(wrapper.getDOMNode().classList.contains('is-withBorder')).toBe(true)
})
test('Can not render a border, if specified', () => {
- const wrapper = mount()
+ const wrapper = mount(
+ Channel 4 }
+ withBorder={false}
+ />
+ )
expect(wrapper.getDOMNode().classList.contains('is-withBorder')).toBe(false)
})
@@ -76,7 +126,12 @@ describe('Responsive', () => {
describe('withBottomMargin', () => {
test('Renders withBottomMargin styles, if specified', () => {
- const wrapper = mount()
+ const wrapper = mount(
+ Channel 4 }
+ withBottomMargin
+ />
+ )
expect(wrapper.getDOMNode().classList.contains('is-withBottomMargin')).toBe(
true
@@ -84,7 +139,12 @@ describe('withBottomMargin', () => {
})
test('Does not render withBottomMargin styles, if specified', () => {
- const wrapper = mount()
+ const wrapper = mount(
+ Channel 4 }
+ withBottomMargin={false}
+ />
+ )
expect(wrapper.getDOMNode().classList.contains('is-withBottomMargin')).toBe(
false
diff --git a/src/components/Page/__tests__/Heading.test.js b/src/components/Page/__tests__/Heading.test.js
deleted file mode 100644
index 6124df1ad..000000000
--- a/src/components/Page/__tests__/Heading.test.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from 'react'
-import { mount } from 'enzyme'
-import Heading from '../Heading'
-
-describe('ClassName', () => {
- test('Has default className', () => {
- const wrapper = mount(Heisenberg shop )
-
- expect(
- wrapper.getDOMNode().classList.contains('c-PageHeader__titleHeading')
- ).toBe(true)
- })
-
- test('Has Secondary className', () => {
- const wrapper = mount(Beakers )
-
- expect(
- wrapper
- .getDOMNode()
- .classList.contains('c-PageHeader__titleHeading--secondary')
- ).toBe(true)
- })
-
- test('Applies custom className if specified', () => {
- const className = 'baby-blue'
- const wrapper = mount(
- Heisenberg shop
- )
-
- expect(wrapper.hasClass(className)).toBe(true)
- })
-})
-
-describe('Primary: h1', () => {
- test('Default is primary heading (h1)', () => {
- const wrapper = mount(Heisenberg Shop )
-
- expect(wrapper.find('h1').length).toBe(1)
- })
-})
-
-describe('Secondary: h2', () => {
- test('Secondary renders an h2', () => {
- const wrapper = mount(Beakers )
-
- expect(wrapper.find('h2').length).toBe(1)
- })
-})
diff --git a/src/components/Page/__tests__/Page.test.js b/src/components/Page/__tests__/Page.test.js
index db1b33b35..753373aad 100644
--- a/src/components/Page/__tests__/Page.test.js
+++ b/src/components/Page/__tests__/Page.test.js
@@ -19,9 +19,9 @@ describe('ClassName', () => {
describe('Content', () => {
test('Can render child content', () => {
- const wrapper = mount(Channel 4 )
+ const wrapper = mount(Channel 5 )
- expect(wrapper.html()).toContain('Channel 4')
+ expect(wrapper.html()).toContain('Channel 5')
})
test('Can render child component', () => {
@@ -52,7 +52,9 @@ describe('Responsive', () => {
const wrapper = mount(
-
+
+
+
@@ -60,7 +62,7 @@ describe('Responsive', () => {
expect(
wrapper
- .find(Page.Card)
+ .find(Page.Section)
.getDOMNode()
.classList.contains('is-responsive')
).toBe(true)
diff --git a/src/components/Page/__tests__/Section.test.js b/src/components/Page/__tests__/Section.test.js
new file mode 100644
index 000000000..9c87bd33c
--- /dev/null
+++ b/src/components/Page/__tests__/Section.test.js
@@ -0,0 +1,50 @@
+import React from 'react'
+import { mount } from 'enzyme'
+import Section from '../Section'
+
+describe('ClassName', () => {
+ test('Has default className', () => {
+ const wrapper = mount()
+
+ expect(wrapper.getDOMNode().classList.contains('c-PageSection')).toBe(true)
+ })
+
+ test('Applies custom className if specified', () => {
+ const className = 'channel-4'
+ const wrapper = mount()
+
+ expect(wrapper.getDOMNode().classList.contains(className)).toBe(true)
+ })
+})
+
+describe('Content', () => {
+ test('Can render child content', () => {
+ const wrapper = mount()
+
+ expect(wrapper.text()).toBe('Channel 4')
+ })
+
+ test('Can render child component', () => {
+ const wrapper = mount(
+
+ )
+
+ expect(wrapper.find('div.ron').length).toBe(1)
+ })
+})
+
+describe('Responsive', () => {
+ test('Renders responsive styles, if specified', () => {
+ const wrapper = mount()
+
+ expect(wrapper.getDOMNode().classList.contains('is-responsive')).toBe(true)
+ })
+
+ test('Does not render responsive styles, if specified', () => {
+ const wrapper = mount()
+
+ expect(wrapper.getDOMNode().classList.contains('is-responsive')).toBe(false)
+ })
+})
diff --git a/src/components/Page/docs/Card.md b/src/components/Page/docs/Card.md
index f4f33eeb0..d7a8b37a8 100644
--- a/src/components/Page/docs/Card.md
+++ b/src/components/Page/docs/Card.md
@@ -12,8 +12,7 @@ This component is a presentational wrapper used to render content within a [`Pag
## Props
-| Prop | Type | Description |
-| ------------ | --------- | ------------------------------------------------ |
-| children | `any` | Content to render. |
-| className | `string` | Custom class names to be added to the component. |
-| isResponsive | `boolean` | Enables responsive styles. Default `false`. |
+| Prop | Type | Description |
+| --------- | -------- | ------------------------------------------------ |
+| children | `any` | Content to render. |
+| className | `string` | Custom class names to be added to the component. |
diff --git a/src/components/Page/docs/Header.md b/src/components/Page/docs/Header.md
index 8efc6263a..9e832b366 100644
--- a/src/components/Page/docs/Header.md
+++ b/src/components/Page/docs/Header.md
@@ -1,28 +1,55 @@
# Header
-This component is a presentational wrapper used to a title/subtitle within a [`Page`](./Page.md).
+This component is a presentational wrapper used to display a title/subtitle within a [`Page`](./Page.md).
+
+The `render` prop gives us access to a `Title` and a `Subtitle` component.
+
+Aside from those 2, you are free to add whatever markup you need: icons, links, tooltips, etc
+
+`Title` takes two props: `headingLevel`, with values of `"h1"` or `"h2"` and `isSecondary` (boolean)
+
+* `headingLevel`: The html tag to use "h1" or "h2", has no effect on looks, just semantics
+* `isSecondary`: This determines if it should look slightly smaller when true. Use when you need a secondary heading in the same Card.
## Example
```jsx
-
+
+ (
+
+
Headers!
+ Oh so nice
+
+ )}
+ />
+
...
```
+For backwards compatibility you can use the header using the "title" and "subtitle" props:
+
+```jsx
+
+```
+
+However, this only renders big H1 tags, so avoid the use of it.
+
## Props
-| Prop | Type | Description |
-| ---------------- | --------- | --------------------------------------------------- |
-| className | `string` | Custom class names to be added to the component. |
-| isResponsive | `boolean` | Enables responsive styles. Default `false`. |
-| title | `string` | The [`Heading`](../../Heading) title to render. |
-| subtitle | `string` | The [`Text`](../../Text) subtitle to render. |
-| withBorder | `boolean` | Renders an [`Hr`](../../Hr) border. Default `true`. |
-| withBottomMargin | `boolean` | Renders bottom margin styles. Default `true`. |
+| Prop | Type | Description |
+| ---------------- | ---------- | --------------------------------------------------------------------------------------- |
+| className | `string` | Custom class names to be added to the component. |
+| isResponsive | `boolean` | Enables responsive styles. Default `false`. |
+| render | `function` | function with 2 arguments: `Title` and `Subtitle`, 2 React Components with styles ready |
+| withBorder | `boolean` | Renders a border under the header. Default `true`. |
+| withBottomMargin | `boolean` | Renders bottom margin styles. Default `true`. |
+| title | `string` | The [`Heading`](../../Heading) title to render. |
+| subtitle | `string` | The [`Text`](../../Text) subtitle to render. |
diff --git a/src/components/Page/docs/Heading.md b/src/components/Page/docs/Heading.md
deleted file mode 100644
index 426b65da6..000000000
--- a/src/components/Page/docs/Heading.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Heading
-
-This component leverages `` for use inside [`Page`](./Page.md) components.
-
-Pages usually have one [`Header`](./Header.md), whose contents are always a title and sometimes a subtitle, for the title `` uses this component in _primary_ mode, which means the heading will be an `` element.
-
-_Secondary_ mode is for additional headings within a page and will be rendered as `` elements.
-
-Styles (spacing and font sizes) are handled specifically for use within Pages.
-
-## Example
-
-```jsx
-
-
-
- Contact Details
- ...
- Address Details
- ...
-
-
-```
-
-## Props
-
-| Prop | Type | Description |
-| --------- | --------- | -------------------------------------------------- |
-| className | `string` | Custom class names to be added to the component. |
-| secondary | `boolean` | Signals a secondary (h2) heading. Default `false`. |
diff --git a/src/components/Page/docs/Page.md b/src/components/Page/docs/Page.md
index e5c654fb8..9abed08b2 100644
--- a/src/components/Page/docs/Page.md
+++ b/src/components/Page/docs/Page.md
@@ -7,9 +7,21 @@ This component is a presentational wrapper used to render a variety of content.
```jsx
-
+
+ (
+
+
My page
+ Very important stuff
+
+ )}
+ />
+
+ Here, I will explain the importance of this
+
+
- Save} />
+ Got it} />
```
diff --git a/src/components/Page/docs/Section.md b/src/components/Page/docs/Section.md
new file mode 100644
index 000000000..7fea5e6d0
--- /dev/null
+++ b/src/components/Page/docs/Section.md
@@ -0,0 +1,33 @@
+# Section
+
+Sections live inside `Card` components and their purpose is to divide content inside them.
+
+A clear indication of the need of more sections is when there are extra headings in the content.
+
+The use of sections, besides of being good for semantic purposes, allows the headings of each section to behave just like the headers in Responsive mode.
+
+`isResponsive` can be set on the `Page.Section`, but normally is better to set it on the parent `Page` component, so it doesn't have to be done individually on each section.
+
+## Example
+
+```jsx
+
+
+
+ Section 1
+ Content under section 1
+
+
+ Section 2
+ Content under section 2
+
+
+
+```
+
+## Props
+
+| Prop | Type | Description |
+| ------------ | --------- | ------------------------------------------------ |
+| className | `string` | Custom class names to be added to the component. |
+| isResponsive | `boolean` | Enables responsive styles. Default `false`. |
diff --git a/src/components/Page/index.ts b/src/components/Page/index.ts
index 073183c91..a98fc0abe 100644
--- a/src/components/Page/index.ts
+++ b/src/components/Page/index.ts
@@ -6,6 +6,6 @@ Page.Actions = propConnect(COMPONENT_KEY.Actions)(Page.Actions)
Page.Card = propConnect(COMPONENT_KEY.Card)(Page.Card)
Page.Content = propConnect(COMPONENT_KEY.Content)(Page.Content)
Page.Header = propConnect(COMPONENT_KEY.Header)(Page.Header)
-Page.Heading = propConnect(COMPONENT_KEY.Heading)(Page.Heading)
+Page.Section = propConnect(COMPONENT_KEY.Section)(Page.Section)
export default propConnect(COMPONENT_KEY.Page)(Page)
diff --git a/src/components/Page/styles/Card.css.ts b/src/components/Page/styles/Card.css.ts
index 920002335..62659dc8f 100644
--- a/src/components/Page/styles/Card.css.ts
+++ b/src/components/Page/styles/Card.css.ts
@@ -28,7 +28,7 @@ export const config = {
transition: 'all 300ms ease',
}
-export const CardUI = styled('section')`
+export const CardUI = styled('div')`
${baseStyles} background-color: white;
border-radius: ${config.borderRadius};
box-shadow: ${config.boxShadow};
diff --git a/src/components/Page/styles/Header.css.ts b/src/components/Page/styles/Header.css.ts
index f89d6a0de..d87049192 100644
--- a/src/components/Page/styles/Header.css.ts
+++ b/src/components/Page/styles/Header.css.ts
@@ -2,10 +2,14 @@ import baseStyles from '../../../styles/resets/baseStyles.css.js'
import { breakpoint } from '../../../styles/mixins/breakpoints.css.js'
import styled from '../../styled'
import PageConfig from './Page.config.css'
+import Heading from '../../Heading'
export const config = {
paddingBottom: '11px',
- marginBottom: '25px',
+ marginBottom: '27px',
+ marginRight: {
+ superWidescreen: '60px',
+ },
width: {
default: '100%',
superWidescreen: '250px',
@@ -14,11 +18,11 @@ export const config = {
export const HeaderUI = styled('header')`
${baseStyles} margin-bottom: 0;
- padding-bottom: ${config.paddingBottom};
width: ${config.width.default};
&.is-withBorder {
border-bottom: 1px solid #e3e8eb;
+ padding-bottom: ${config.paddingBottom};
}
&.is-withBottomMargin {
@@ -31,6 +35,7 @@ export const HeaderUI = styled('header')`
`
border-bottom: none;
width: ${config.width.superWidescreen};
+ margin-right: ${config.marginRight.superWidescreen}
`
)};
}
@@ -42,3 +47,8 @@ export const TitleUI = styled('div')`
export const SubTitleUI = styled('div')`
${baseStyles} margin-top: 5px;
`
+
+export const HeadingUI = styled(Heading)`
+ margin: 0;
+ padding: 0;
+`
diff --git a/src/components/Page/styles/Heading.css.ts b/src/components/Page/styles/Heading.css.ts
deleted file mode 100644
index 308f4345a..000000000
--- a/src/components/Page/styles/Heading.css.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import styled from '../../styled'
-import Heading from '../../Heading'
-
-export const config = {
- marginTop: '35px',
- marginBottom: '27px',
-}
-
-export const HeadingUI = styled(Heading)`
- margin: 0;
- padding: 0;
-`
-
-export const SecondaryHeadingUI = styled(Heading)`
- margin: ${config.marginTop} 0 ${config.marginBottom};
- padding: 0;
-`
diff --git a/src/components/Page/styles/Section.css.ts b/src/components/Page/styles/Section.css.ts
new file mode 100644
index 000000000..1072698a4
--- /dev/null
+++ b/src/components/Page/styles/Section.css.ts
@@ -0,0 +1,38 @@
+import styled from '../../styled'
+import baseStyles from '../../../styles/resets/baseStyles.css.js'
+import { breakpoint } from '../../../styles/mixins/breakpoints.css.js'
+import PageConfig from './Page.config.css'
+
+export const config = {
+ flexDirection: {
+ default: 'column',
+ superWidescreen: 'row',
+ },
+ flexItemsAlign: {
+ superWidescreen: 'flex-start',
+ },
+ marginBottom: '35px',
+}
+
+export const SectionUI = styled('section')`
+ ${baseStyles} display: flex;
+ flex-direction: ${config.flexDirection.default};
+ width: 100%;
+ margin-bottom: ${config.marginBottom};
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ &.is-responsive {
+ ${breakpoint(
+ PageConfig.breakpoint.superWidescreen,
+ `
+ flex-direction: ${config.flexDirection.superWidescreen};
+ align-items: ${config.flexItemsAlign.superWidescreen};
+ `
+ )};
+ }
+`
+
+export default SectionUI
diff --git a/src/components/Page/utils.ts b/src/components/Page/utils.ts
index eab60c604..e48c69e05 100644
--- a/src/components/Page/utils.ts
+++ b/src/components/Page/utils.ts
@@ -5,6 +5,7 @@ export const COMPONENT_KEY: {
Content: string
Header: string
Heading: string
+ Section: string
} = {
Page: 'Page',
Actions: 'PageActions',
@@ -12,4 +13,5 @@ export const COMPONENT_KEY: {
Content: 'PageContent',
Header: 'PageHeader',
Heading: 'PageHeading',
+ Section: 'PageSection',
}
diff --git a/stories/Page/Page.Card.stories.js b/stories/Page/Page.Card.stories.js
index e2dc8dc3e..b4bc0433e 100644
--- a/stories/Page/Page.Card.stories.js
+++ b/stories/Page/Page.Card.stories.js
@@ -1,15 +1,6 @@
import React from 'react'
-// import * as React from 'react'
import { storiesOf } from '@storybook/react'
-import {
- Button,
- ControlGroup,
- FormGroup,
- FormLabel,
- Input,
- Page,
- Switch,
-} from '../../src/index.js'
+import { Page } from '../../src/index.js'
import { App } from './decorators'
const stories = storiesOf('Page/Card', module).addDecorator(App)
@@ -17,124 +8,13 @@ const stories = storiesOf('Page/Card', module).addDecorator(App)
stories.add('default', () => (
-
-
-
-
-
-
+ Cards are just presentational wrappers, nothing special.
-
-))
-
-stories.add('Sections', () => (
-
-
-
-
-
-
-
-
-
-))
-
-stories.add('Responsive', () => (
-
-
-
+ There can be more than 1 per Page.
-
-
-
-
-
-
-
- Save
-
- }
- secondary={
-
- Discard Changes
-
- }
- serious={
-
- Something serious!
-
- }
- />
-
-))
-
-stories.add('Example', () => (
-
-
-
+ Sorry Cards, I didn't mean that. You are so pretty.
-
- Save}
- secondary={Discard Changes }
- />
))
-
-function ExampleContent() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- Default Settings
-
-
-
-
-
-
-
- .helpscoutdocs.com
-
-
-
-
-
-
-
-
-
- https://
-
-
-
-
-
-
-
-
- )
-}
diff --git a/stories/Page/Page.Example.stories.js b/stories/Page/Page.Example.stories.js
new file mode 100644
index 000000000..30e968d7e
--- /dev/null
+++ b/stories/Page/Page.Example.stories.js
@@ -0,0 +1,134 @@
+import React from 'react'
+import { storiesOf } from '@storybook/react'
+import {
+ Button,
+ ControlGroup,
+ FormGroup,
+ FormLabel,
+ Input,
+ Page,
+ Switch,
+} from '../../src/index.js'
+import { App } from './decorators'
+
+const stories = storiesOf('Page/Example', module).addDecorator(App)
+
+stories.add('Responsive', () => (
+
+
+
+ (
+
+
Edit your account
+ Welcome to the Dharma Initiative.
+
+ )}
+ />
+
+
+
+
+
+
+
+
+
+
+ (
+
+
+ Default Settings
+
+
+ )}
+ />
+
+
+
+
+
+
+ (
+
+
More settings this way
+
+ Heading looks the same, but it's an H2! There should only be one
+ h1 per page ;)
+
+
+ )}
+ />
+
+
+
+
+
+ Save
+
+ }
+ secondary={
+
+ Discard Changes
+
+ }
+ serious={
+
+ Something serious!
+
+ }
+ />
+
+))
+
+function ExampleContent() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .helpscoutdocs.com
+
+
+
+
+
+
+
+
+
+ https://
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/stories/Page/Page.Header.stories.js b/stories/Page/Page.Header.stories.js
new file mode 100644
index 000000000..cced5a534
--- /dev/null
+++ b/stories/Page/Page.Header.stories.js
@@ -0,0 +1,264 @@
+import React from 'react'
+import styled from '../../src/components/styled'
+import { storiesOf } from '@storybook/react'
+import { Page } from '../../src/index.js'
+import { App } from './decorators'
+
+const stories = storiesOf('Page/Header', module).addDecorator(App)
+
+const renderPropCode = `
+ (
+
+
Headers!
+ Oh so nice
+
+ )
+} />`
+
+const titleSubtitlePropCode = `
+ `
+
+const renderPropCodeH2 = `
+ (
+
+
Headers!
+ Oh so nice
+
+ )
+} />`
+
+const renderPropCodeSecondary = `
+ (
+
+
Headers!
+ Oh so nice
+
+ )
+} />`
+
+const P = styled('p')`
+ margin: 0 0 1.5em 0;
+ padding: 0;
+ font-size: 14px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+`
+
+stories.add('default', () => (
+
+
+
+ (
+
+
Headers!
+ Oh so nice
+
+ )}
+ />
+
+ Each Card can have one or more Headers.
+
+ The Header gives us one special prop:{' '}
+ render
+
+
+ render
takes a function
+ with 2 arguments: Title
{' '}
+ and Subtitle
+
+
+ Those 2 are React Components with all the pretty styles ready, use
+ like this:
+
+
+ {renderPropCode}
+
+
+ Inside render
aside
+ from those 2, you are free to add whatever markup you need: icons,
+ links, tooltips, etc
+
+
+ Title
has 2 props:
+
+
+
+
+
+ (
+
+
+ Second Header
+
+ This one has a subtitle too
+
+ )}
+ />
+ Something cool
+
+
+ (
+
+
+ Third Header, no subtitle here
+
+
+ )}
+ />
+ Something cool
+
+
+
+
+
+ For backwards compatibility you can use the header using the "title"
+ and "subtitle" props:
+
+
+ {titleSubtitlePropCode}
+
+ However, this only renders big H1 tags, so avoid the use of it.
+
+
+
+
+))
+
+stories.add('Subsequent Cards', () => (
+
+
+
+ (
+
+
Headers!
+ Oh so nice
+
+ )}
+ />
+
+ For accesibility compliance, each page should only have one{' '}
+ H1
heading
+
+ Normally the 1st Card would hold it
+
+
+
+
+ (
+
+
This is an h2
+ Not that you can tell
+
+ )}
+ />
+
+ So make sure that on the other Cards the prop{' '}
+ headingLevel
on the{' '}
+ Title
is set to "h2"
+
+
+ The heading would look the same, but screen readers would not get
+ confused what is the main purpose of this page
+
+
+
+
+))
+
+stories.add('Responsiveness', () => (
+
+
+
+ (
+
+
Responsive?
+ You bet
+
+ )}
+ />
+
+ Just set isResponsive
on
+ the Page
+
+
+
+ (
+
+
+ Second Header
+
+ This one has a subtitle too
+
+ )}
+ />
+ Something cool
+
+
+ (
+
+
+ Third Header, no subtitle here
+
+
+ )}
+ />
+ Something cool
+
+
+
+))
diff --git a/stories/Page/Page.Section.stories.js b/stories/Page/Page.Section.stories.js
new file mode 100644
index 000000000..94288b084
--- /dev/null
+++ b/stories/Page/Page.Section.stories.js
@@ -0,0 +1,27 @@
+import React from 'react'
+import { storiesOf } from '@storybook/react'
+import { Page } from '../../src/index.js'
+import { App } from './decorators'
+
+const stories = storiesOf('Page/Section', module).addDecorator(App)
+
+stories.add('default', () => (
+
+
+
+ Section 1
+ A Page.Section is where content should live.
+ Sections are wrappers too, not pretty, but smart
+ They handle responsiveness if enabled on the Page
+
+
+ Section 2
+ When to use?
+
+ If the content inside a Card has multiple Headings, it's a sure signal
+ that it has multiple sections
+
+
+
+
+))