Skip to content

Commit

Permalink
Merge pull request #114 from artsy/fix-custom-classnames
Browse files Browse the repository at this point in the history
[Feature] Allow classNames to be passed to <Media> container
  • Loading branch information
zephraph committed Feb 11, 2020
2 parents 13997c9 + 0b74737 commit f8a7859
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 4 deletions.
24 changes: 24 additions & 0 deletions README.md
Expand Up @@ -348,6 +348,8 @@ const ExampleAppMedia = createMedia({
interactions: {
hover: "(hover: hover)",
notHover: "(hover: none)",
landscape: "(orientation: landscape)",
portrait: "(orientation: portrait)",
},
})

Expand Down Expand Up @@ -395,6 +397,28 @@ export const HomePage = () => {
The examples given for each prop use breakpoint definitions as defined in the
above ‘Setup’ section.

If you would like to avoid the underlying div that is generated by `<Media>` and
instead use your own element, use the render-props form:

```tsx
export const HomePage = () => {
return (
<>
<Media at="sm">Hello mobile!</Media>
<Media greaterThan="xs">
{(mediaClassNames) => {
return (
<MySpecialComponent className={mediaClassNames}>
Hello desktop!
</MySpecialComponent>
)
}}
</Media>
</>
)
}
```

#### createMediaStyle

> Note: This is only used when SSR rendering
Expand Down
22 changes: 18 additions & 4 deletions src/Media.tsx
Expand Up @@ -11,10 +11,10 @@ import { intersection, propKey, createClassName } from "./Utils"
*
* @see {@link MediaProps.children}.
*/
export type RenderProp = ((
export type RenderProp = (
className: string,
renderChildren: boolean
) => React.ReactNode)
) => React.ReactNode

// TODO: All of these props should be mutually exclusive. Using a union should
// probably be made possible by https://github.com/Microsoft/TypeScript/pull/27408.
Expand Down Expand Up @@ -176,6 +176,11 @@ export interface MediaProps<B, I> extends MediaBreakpointProps<B> {
*
*/
children: React.ReactNode | RenderProp

/**
* Additional classNames to passed down and applied to Media container
*/
className?: string
}

export interface MediaContextProviderProps<M> {
Expand Down Expand Up @@ -363,13 +368,22 @@ export function createMedia<
validateProps(props)
}

static defaultProps = {
className: "",
}

render() {
const props = this.props
return (
<MediaContext.Consumer>
{({ onlyMatch } = {}) => {
let className: string | null
const { children, interaction, ...breakpointProps } = props
const {
children,
className: passedClassName,
interaction,
...breakpointProps
} = props
if (props.interaction) {
className = createClassName("interaction", props.interaction)
} else {
Expand Down Expand Up @@ -419,7 +433,7 @@ export function createMedia<
} else {
return (
<div
className={`fresnel-container ${className}`}
className={`fresnel-container ${className} ${passedClassName}`}
suppressHydrationWarning={!renderChildren}
>
{renderChildren ? props.children : null}
Expand Down
11 changes: 11 additions & 0 deletions src/__test__/Media.test.tsx
Expand Up @@ -134,6 +134,17 @@ describe("Media", () => {
expect(query).toHaveStyleRule("margin", "0")
expect(query).toHaveStyleRule("padding", "0")
})

it("applies additional classNames passed as props", () => {
const query = renderer
.create(
<Media lessThan="small" className="foo">
ohai
</Media>
)
.toJSON()
expect(query.props.className).toContain("foo")
})
})

describe("concerning breakpoints", () => {
Expand Down

0 comments on commit f8a7859

Please sign in to comment.