Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ Keys of media query objects are camel-cased and numeric values automatically get
See the [json2mq docs](https://github.com/akiran/json2mq/blob/master/README.md#usage) for more
examples of queries you can construct using objects.

An optional `targetWindow` prop can be specified if you want the `query` to be
evaluated against a different window object than the one the code is running in.
This can be useful for example if you are rendering part of your component tree
to an iframe or [a popup window](https://hackernoon.com/using-a-react-16-portal-to-do-something-cool-2a2d627b0202).

If you're curious about how react-media differs from
[react-responsive](https://github.com/contra/react-responsive), please see
[this comment](https://github.com/ReactTraining/react-media/issues/70#issuecomment-347774260).
Expand Down
16 changes: 12 additions & 4 deletions modules/Media.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class Media extends React.Component {
PropTypes.arrayOf(PropTypes.object.isRequired)
]).isRequired,
render: PropTypes.func,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func])
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
targetWindow: PropTypes.object
};

static defaultProps = {
Expand All @@ -28,13 +29,20 @@ class Media extends React.Component {
updateMatches = () => this.setState({ matches: this.mediaQueryList.matches });

componentWillMount() {
if (typeof window !== "object") return;

let { query } = this.props;
const targetWindow = this.props.targetWindow || window;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still breaks server-side rendering because componentWillMount fires on the server but window is not defined.


if (typeof targetWindow !== "object") return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the line that checks to make sure we're actually in a browser. It should stay:

if (typeof window !== 'object') return;

and then get the targetWindow prop afterwards.


if (!targetWindow.matchMedia) {
throw new Error(
'You passed a `targetWindow` prop to `Media` that does not have a `matchMedia` function.'
);
}

if (typeof query !== "string") query = json2mq(query);

this.mediaQueryList = window.matchMedia(query);
this.mediaQueryList = targetWindow.matchMedia(query);
this.mediaQueryList.addListener(this.updateMatches);
this.updateMatches();
}
Expand Down
38 changes: 38 additions & 0 deletions modules/__tests__/Media-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,44 @@ describe("A <Media>", () => {
});
});

describe("when a custom targetWindow prop is passed", () => {
beforeEach(() => {
window.matchMedia = createMockMediaMatcher(true);
});

it("renders its child", () => {
const testWindow = {
matchMedia: createMockMediaMatcher(false)
};

const element = (
<Media query="" targetWindow={testWindow}>
{matches => (matches ? <div>hello</div> : <div>goodbye</div>)}
</Media>
);

ReactDOM.render(element, node, () => {
expect(node.firstChild.innerHTML).toMatch(/goodbye/);
});
});

describe("when a non-window prop is passed for targetWindow", () => {
it("errors with a useful message", () => {
const notAWindow = {};

const element = (
<Media query="" targetWindow={notAWindow}>
{matches => (matches ? <div>hello</div> : <div>goodbye</div>)}
</Media>
);

expect(() => {
ReactDOM.render(element, node, () => {});
}).toThrow("does not have a `matchMedia` function");
});
})
});

describe("rendered on the server", () => {
beforeEach(() => {
window.matchMedia = createMockMediaMatcher(true);
Expand Down