Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
65 changes: 64 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { compileQuery, matches, type Environment, type EvaluateResult, type SimplePerm } from "media-query-fns";

// TODO: remove `deviceWidth` & `deviceHeight` from it, and only derive those from `width`, `height`, and `dppx`
type MediaState = { [key in keyof Environment as key extends `${infer Key}Px` ? Key : key]?: Environment[key] };

const DEFAULT_ENV: Parameters<typeof matches>[1] = {
Expand Down Expand Up @@ -35,6 +36,58 @@ type Feature = keyof MediaState;

const now = Date.now();

/**
* Match the renaming done by media-query-fns
*
* {@link https://github.com/tbjgolden/media-query-fns/blob/7dae2618b9321f503cbd0a44a202a9190665e80e/lib/matches.ts#L200-L533}
*/
const MEDIA_FEATURE_TO_FEATURES = {
// Same but different casing
"any-hover": ["anyHover"],
"any-pointer": ["anyPointer"],
"color-gamut": ["colorGamut"],
"color-index": ["colorIndex"],
"display-mode": ["displayMode"],
"dynamic-range": ["dynamicRange"],
"environment-blending": ["environmentBlending"],
"forced-colors": ["forcedColors"],
grid: ["grid"],
"horizontal-viewport-segments": ["horizontalViewportSegments"],
hover: ["hover"],
"inverted-colors": ["invertedColors"],
"media-type": ["mediaType"],
"nav-controls": ["navControls"],
"overflow-block": ["overflowBlock"],
"overflow-inline": ["overflowInline"],
pointer: ["pointer"],
"prefers-color-scheme": ["prefersColorScheme"],
"prefers-contrast": ["prefersContrast"],
"prefers-reduced-data": ["prefersReducedData"],
"prefers-reduced-motion": ["prefersReducedMotion"],
"prefers-reduced-transparency": ["prefersReducedTransparency"],
scan: ["scan"],
scripting: ["scripting"],
update: ["update"],
"vertical-viewport-segments": ["verticalViewportSegments"],
"video-color-gamut": ["videoColorGamut"],
"video-dynamic-range": ["videoDynamicRange"],

// Numbers
monochrome: ["monochromeBits"],
color: ["colorBits"],
resolution: ["dppx"],

// Pixels
width: ["width"],
height: ["height"],
"device-height": ["deviceHeight"],
"device-width": ["deviceWidth"],

// Combinations
"aspect-ratio": ["width", "height"],
"device-aspect-ratio": ["deviceHeight", "deviceWidth"],
} as const satisfies Record<string, Feature[]>;

// Event was added in node 15, so until we drop the support for versions before it, we need to use this
class EventLegacy {
type: "change";
Expand Down Expand Up @@ -73,7 +126,17 @@ const EventCompat: typeof Event = typeof Event === "undefined" ? EventLegacy : E
const getFeaturesFromQuery = (query: EvaluateResult): Set<Feature> => {
const features = new Set<Feature>();
query.simplePerms.forEach((perm) => {
Object.keys(perm).forEach((feature) => features.add(feature as Feature));
Object.keys(perm).forEach((mediaFeature) => {
if (mediaFeature in MEDIA_FEATURE_TO_FEATURES) {
MEDIA_FEATURE_TO_FEATURES[mediaFeature as keyof typeof MEDIA_FEATURE_TO_FEATURES].forEach((feature) =>
features.add(feature),
);
}
// // For debut, we can comment out those:
// else {
// console.error("Unrecognized " + mediaFeature);
// }
});
});
return features;
};
Expand Down
14 changes: 1 addition & 13 deletions test/listeners.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,13 @@
const { test } = require("node:test");
const { strict: assert } = require("node:assert");
const { matchMedia, setMedia, cleanupListeners, MediaQueryListEvent, cleanup } = require("mock-match-media");
const { mock } = require("./utils.cjs");

test.afterEach(() => {
// cleanup listeners and state after each test
cleanup();
});

/**
* @type {() => [(event: MediaQueryListEvent) => void, MediaQueryListEvent[]]}
*/
const mock = () => {
const calls = [];
return [
(event) => {
calls.push(event);
},
calls,
];
};

test("`.addListener()`", () => {
const mql = matchMedia("(min-width: 500px)");

Expand Down
37 changes: 35 additions & 2 deletions test/matchers/aspect-ratio.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

const { test } = require("node:test");
const { strict: assert } = require("node:assert");
const { matchMedia, setMedia, cleanupMedia } = require("mock-match-media");
const { matchMedia, setMedia, cleanup } = require("mock-match-media");
const { mock } = require("../utils.cjs");

test.beforeEach(() => {
cleanupMedia();
cleanup();
});

test("unset", () => {
Expand Down Expand Up @@ -238,3 +239,35 @@ test("other syntax", () => {
});
assert.equal(matchMedia("(aspect-ratio: 3)").matches, true);
});

test("`.addEventListener()`", () => {
const mqlAR3 = matchMedia("(aspect-ratio > 3)");
const [cb, calls] = mock();

mqlAR3.addEventListener("change", cb);

assert.equal(matchMedia("(aspect-ratio > 3)").matches, false);

setMedia({
width: 7,
});

assert.equal(matchMedia("(aspect-ratio > 3)").matches, false);
assert.equal(calls.length, 0);

setMedia({
height: 2,
});

assert.equal(matchMedia("(aspect-ratio > 3)").matches, true);
assert.equal(calls.length, 1);
assert.equal(calls[0].matches, true);

setMedia({
height: 3,
});

assert.equal(matchMedia("(aspect-ratio > 3)").matches, false);
assert.equal(calls.length, 2);
assert.equal(calls[1].matches, false);
});
44 changes: 42 additions & 2 deletions test/matchers/device-aspect-ratio.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

const { test } = require("node:test");
const { strict: assert } = require("node:assert");
const { matchMedia, setMedia, cleanupMedia } = require("mock-match-media");
const { matchMedia, setMedia, cleanup } = require("mock-match-media");
const { mock } = require("../utils.cjs");

test.beforeEach(() => {
cleanupMedia();
cleanup();
});

test("unset", () => {
Expand Down Expand Up @@ -208,3 +209,42 @@ test("other syntax", () => {
assert.equal(matchMedia("(device-aspect-ratio: 16 / 16)").matches, true);
assert.equal(matchMedia("(device-aspect-ratio: 16 / 16)").matches, true);
});

test("`.addEventListener()`", () => {
const mqlAR3 = matchMedia("(device-aspect-ratio > 3)");
const [cb, calls] = mock();

mqlAR3.addEventListener("change", cb);

assert.equal(matchMedia("(device-aspect-ratio > 3)").matches, false);

setMedia({
width: 7,
});

assert.equal(matchMedia("(device-aspect-ratio > 3)").matches, false);
assert.equal(calls.length, 0);

setMedia({
height: 2,
});

assert.equal(matchMedia("(device-aspect-ratio > 3)").matches, true);
assert.equal(calls.length, 1);
assert.equal(calls[0].matches, true);

setMedia({
height: 3,
});

assert.equal(matchMedia("(device-aspect-ratio > 3)").matches, false);
assert.equal(calls.length, 2);
assert.equal(calls[1].matches, false);

setMedia({
dppx: 2,
});

assert.equal(matchMedia("(device-aspect-ratio > 3)").matches, false);
assert.equal(calls.length, 2);
});
34 changes: 34 additions & 0 deletions test/matchers/prefers-color-scheme.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
const { test } = require("node:test");
const { strict: assert } = require("node:assert");
const { matchMedia, setMedia, cleanupMedia } = require("mock-match-media");
const { mock } = require("../utils.cjs");

test.beforeEach(() => {
cleanupMedia();
});


test("unset", () => {
assert.equal(matchMedia("(prefers-color-scheme: light)").matches, false);
assert.equal(matchMedia("(prefers-color-scheme: dark)").matches, false);
Expand All @@ -30,3 +32,35 @@ test("dark", () => {
assert.equal(matchMedia("(prefers-color-scheme: light)").matches, false);
assert.equal(matchMedia("(prefers-color-scheme: dark)").matches, true);
});

test("`.addEventListener()`", () => {
const mqlLight = matchMedia("(prefers-color-scheme: light)");
const mqlDark = matchMedia("(prefers-color-scheme: dark)");
const [cbLight, callsLight] = mock();
const [cbDark, callsDark] = mock();

mqlLight.addEventListener("change", cbLight);
mqlDark.addEventListener("change", cbDark);

setMedia({
prefersColorScheme: "light",
});

assert.equal(matchMedia("(prefers-color-scheme: light)").matches, true);
assert.equal(matchMedia("(prefers-color-scheme: dark)").matches, false);
assert.equal(callsLight.length, 1);
assert.equal(callsLight[0].matches, true);
assert.equal(callsDark.length, 0);

setMedia({
prefersColorScheme: "dark",
});

assert.equal(matchMedia("(prefers-color-scheme: light)").matches, false);
assert.equal(matchMedia("(prefers-color-scheme: dark)").matches, true);
assert.equal(callsLight.length, 2);
assert.equal(callsLight[1].matches, false);
assert.equal(callsDark.length, 1);
assert.equal(callsDark[0].matches, true);
})

15 changes: 15 additions & 0 deletions test/utils.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @type {() => [(event: MediaQueryListEvent) => void, MediaQueryListEvent[]]}
*/
exports.mock = () => {
/**
* @type MediaQueryListEvent[]
*/
const calls = [];
return [
(event) => {
calls.push(event);
},
calls,
];
};