Skip to content

Commit

Permalink
Add responsive component, make banner responsive
Browse files Browse the repository at this point in the history
  • Loading branch information
zephraph committed Jun 9, 2018
1 parent f67bcd0 commit 3b1b1d7
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 14 deletions.
3 changes: 2 additions & 1 deletion .stylelintrc
Expand Up @@ -7,6 +7,7 @@
"block-opening-brace-space-after": null,
"block-closing-brace-space-before": null,
"declaration-block-no-redundant-longhand-properties": null,
"color-hex-length": null
"color-hex-length": null,
"block-no-empty": null
}
}
85 changes: 85 additions & 0 deletions src/Styleguide/Elements/Responsive.tsx
@@ -0,0 +1,85 @@
import React from "react"

const ResponsiveContext = React.createContext({})

interface Breakpoints {
[breakpoint: string]: string
}

interface ResponsiveProviderProps {
breakpoints: Breakpoints
}

interface BreakpointState {
[breakpoint: string]: boolean
}

interface ResponsiveProviderState {
breakpoints: BreakpointState
mediaMatchers: MediaQueryList[]
mqHandler: null | MediaQueryListListener
}

export class ResponsiveProvider extends React.Component<
ResponsiveProviderProps,
ResponsiveProviderState
> {
constructor(props) {
super(props)
this.state = {
breakpoints: {
...Object.keys(props.breakpoints)
.map(breakpoint => ({
[breakpoint]: false,
}))
.reduce((acc, curr) => ({ ...acc, ...curr }), {}),
},
mediaMatchers: [],
mqHandler: null,
}
}

componentDidMount() {
this.setupObservers(this.props.breakpoints)
}

componentWillUnmount() {
if (this.state.mqHandler) {
this.state.mediaMatchers.forEach(mediaQuery =>
mediaQuery.removeListener(this.state.mqHandler)
)
}
}

setupObservers = breakpoints => {
const breakpointKeys = Object.keys(breakpoints)
const mediaMatchers = breakpointKeys.map(breakpoint =>
window.matchMedia(breakpoints[breakpoint])
)
const mqHandler = () => {
for (let i = 0; i < mediaMatchers.length; ++i) {
this.setState({
breakpoints: { [breakpointKeys[i]]: mediaMatchers[i].matches },
})
}
}
this.setState({ mediaMatchers, mqHandler })
mediaMatchers.forEach(mediaQuery => {
mediaQuery.addListener(mqHandler)
})
mqHandler()
}

render() {
return (
<ResponsiveContext.Provider value={this.state.breakpoints}>
{this.props.children}
</ResponsiveContext.Provider>
)
}
}

export const Responsive: React.ComponentType<
React.ConsumerProps<BreakpointState>
> =
ResponsiveContext.Consumer
49 changes: 37 additions & 12 deletions src/Styleguide/Pages/Artwork/Banner.tsx
Expand Up @@ -2,6 +2,7 @@ import React from "react"
import { Avatar } from "../../Elements/Avatar"
import { Sans, Serif } from "@artsy/palette"
import { Flex } from "../../Elements/Flex"
import { Responsive } from "../../Elements/Responsive"

export interface BannerProps {
src: string
Expand All @@ -13,18 +14,42 @@ export interface BannerProps {
export class Banner extends React.Component<BannerProps> {
render() {
return (
<Flex flexDirection="row">
<Avatar size="110px" src={this.props.src} />
<Flex flexDirection="column" justifyContent="center" ml={4}>
<Sans weight="medium" size="2">
{this.props.badge}
</Sans>
<Serif size="4t">{this.props.headline}</Serif>
<Serif size="4t" color="black60">
{this.props.subHeadline}
</Serif>
</Flex>
</Flex>
<Responsive>
{({ xs }) => {
if (xs) return <SmallBanner {...this.props} />
else return <LargeBanner {...this.props} />
}}
</Responsive>
)
}
}

export const LargeBanner = props => (
<Flex flexDirection="row">
<Avatar size="110px" src={props.src} />
<Flex flexDirection="column" justifyContent="center" ml={4}>
<Sans weight="medium" size="2">
{props.badge}
</Sans>
<Serif size="4t">{props.headline}</Serif>
<Serif size="4t" color="black60">
{props.subHeadline}
</Serif>
</Flex>
</Flex>
)

export const SmallBanner = props => (
<Flex flexDirection="row">
<Flex flexDirection="column" justifyContent="center" ml={4}>
<Sans weight="medium" size="2">
{props.badge}
</Sans>
<Serif size="4t">{props.headline}</Serif>
<Serif size="4t" color="black60">
{props.subHeadline}
</Serif>
</Flex>
<Avatar size="110px" src={props.src} />
</Flex>
)
17 changes: 16 additions & 1 deletion src/__stories__/storiesOf.js
@@ -1,11 +1,26 @@
import React from "react"
import { storiesOf as _storiesOf } from "@storybook/react"
import { Theme, injectGlobalCSS } from "@artsy/palette"
import { ResponsiveProvider } from "../Styleguide/Elements/Responsive"

injectGlobalCSS()

const breakpoints = {
xl: "(min-width: 1192px)",
lg: "(min-width: 1024px) and (max-width: 1191px)",
md: "(min-width: 900px) and (max-width: 1023px)",
sm: "(min-width: 768px) and (max-width: 889px)",
xs: "(max-width: 767px)",
}

export function storiesOf(desc, mod) {
return _storiesOf(desc, mod).addDecorator(storyFn => {
return <Theme>{storyFn()}</Theme>
return (
<Theme>
<ResponsiveProvider breakpoints={breakpoints}>
{storyFn()}
</ResponsiveProvider>
</Theme>
)
})
}

0 comments on commit 3b1b1d7

Please sign in to comment.