Skip to content

Commit

Permalink
#921@trivial: Fixes issue with retrieving root font size in media que…
Browse files Browse the repository at this point in the history
…ries.
  • Loading branch information
capricorn86 committed May 18, 2023
1 parent 2813bd0 commit 523f379
Show file tree
Hide file tree
Showing 6 changed files with 424 additions and 174 deletions.
Expand Up @@ -15,6 +15,7 @@ import CSSStyleDeclarationElementMeasurementProperties from './config/CSSStyleDe
import CSSStyleDeclarationCSSParser from '../css-parser/CSSStyleDeclarationCSSParser';
import QuerySelector from '../../../query-selector/QuerySelector';
import CSSMeasurementConverter from '../measurement-converter/CSSMeasurementConverter';
import MediaQueryList from '../../../match-media/MediaQueryList';

const CSS_VARIABLE_REGEXP = /var\( *(--[^) ]+)\)/g;
const CSS_MEASUREMENT_REGEXP = /[0-9.]+(px|rem|em|vw|vh|%|vmin|vmax|cm|mm|in|pt|pc|Q)/g;
Expand Down Expand Up @@ -259,7 +260,7 @@ export default class CSSStyleDeclarationElementStyle {
return;
}

const defaultView = this.element.ownerDocument.defaultView;
const ownerWindow = this.element.ownerDocument.defaultView;

for (const rule of options.cssRules) {
if (rule.type === CSSRuleTypeEnum.styleRule) {
Expand All @@ -286,9 +287,12 @@ export default class CSSStyleDeclarationElementStyle {
}
} else if (
rule.type === CSSRuleTypeEnum.mediaRule &&
// TODO: Gettings the root font causes a never ending loop as we need to the computed styles for the <html> element (root element) to get the font size. How to fix this?
this.element.tagName !== 'HTML' &&
defaultView.matchMedia((<CSSMediaRule>rule).conditionText).matches
// TODO: We need to send in a predfined root font size as it will otherwise be calculated using Window.getComputedStyle(), which will cause a never ending loop. Is there another solution?
new MediaQueryList({
ownerWindow,
media: (<CSSMediaRule>rule).conditionText,
rootFontSize: this.element.tagName === 'HTML' ? 16 : null
}).matches
) {
this.parseCSSRules({
elements: options.elements,
Expand Down
35 changes: 18 additions & 17 deletions packages/happy-dom/src/match-media/MediaQueryItem.ts
Expand Up @@ -12,33 +12,34 @@ export default class MediaQueryItem {
public not: boolean;
public rules: IMediaQueryRule[];
public ranges: IMediaQueryRange[];
private rootFontSize: number | null = null;
private rootFontSize: string | number | null = null;
private ownerWindow: IWindow;

/**
* Constructor.
*
* @param ownerWindow Window.
* @param [options] Options.
* @param options Options.
* @param options.ownerWindow Owner window.
* @param [options.rootFontSize] Root font size.
* @param [options.mediaTypes] Media types.
* @param [options.not] Not.
* @param [options.rules] Rules.
* @param [options.ranges] Ranges.
*/
constructor(
ownerWindow: IWindow,
options?: {
mediaTypes?: MediaQueryTypeEnum[];
not?: boolean;
rules?: IMediaQueryRule[];
ranges?: IMediaQueryRange[];
}
) {
this.ownerWindow = ownerWindow;
this.mediaTypes = (options && options.mediaTypes) || [];
this.not = (options && options.not) || false;
this.rules = (options && options.rules) || [];
this.ranges = (options && options.ranges) || [];
constructor(options: {
ownerWindow: IWindow;
rootFontSize?: string | number | null;
mediaTypes?: MediaQueryTypeEnum[];
not?: boolean;
rules?: IMediaQueryRule[];
ranges?: IMediaQueryRange[];
}) {
this.ownerWindow = options.ownerWindow;
this.rootFontSize = options.rootFontSize || null;
this.mediaTypes = options.mediaTypes || [];
this.not = options.not || false;
this.rules = options.rules || [];
this.ranges = options.ranges || [];
}

/**
Expand Down
31 changes: 24 additions & 7 deletions packages/happy-dom/src/match-media/MediaQueryList.ts
Expand Up @@ -17,17 +17,21 @@ export default class MediaQueryList extends EventTarget {
private _ownerWindow: IWindow;
private _items: IMediaQueryItem[] | null = null;
private _media: string;
private _rootFontSize: string | number | null = null;

/**
* Constructor.
*
* @param ownerWindow Window.
* @param media Media.
* @param options Options.
* @param options.ownerWindow Owner window.
* @param options.media Media.
* @param [options.rootFontSize] Root font size.
*/
constructor(ownerWindow: IWindow, media: string) {
constructor(options: { ownerWindow: IWindow; media: string; rootFontSize?: string | number }) {
super();
this._ownerWindow = ownerWindow;
this._media = media;
this._ownerWindow = options.ownerWindow;
this._media = options.media;
this._rootFontSize = options.rootFontSize || null;
}

/**
Expand All @@ -36,7 +40,14 @@ export default class MediaQueryList extends EventTarget {
* @returns Media.
*/
public get media(): string {
this._items = this._items || MediaQueryParser.parse(this._ownerWindow, this._media);
this._items =
this._items ||
MediaQueryParser.parse({
ownerWindow: this._ownerWindow,
mediaQuery: this._media,
rootFontSize: this._rootFontSize
});

return this._items.map((item) => item.toString()).join(', ');
}

Expand All @@ -46,7 +57,13 @@ export default class MediaQueryList extends EventTarget {
* @returns Matches.
*/
public get matches(): boolean {
this._items = this._items || MediaQueryParser.parse(this._ownerWindow, this._media);
this._items =
this._items ||
MediaQueryParser.parse({
ownerWindow: this._ownerWindow,
mediaQuery: this._media,
rootFontSize: this._rootFontSize
});

for (const item of this._items) {
if (!item.matches()) {
Expand Down
31 changes: 24 additions & 7 deletions packages/happy-dom/src/match-media/MediaQueryParser.ts
Expand Up @@ -37,19 +37,31 @@ export default class MediaQueryParser {
/**
* Parses a media query string.
*
* @param ownerWindow Window.
* @param mediaQuery Selector.
* @param options Options.
* @param options.ownerWindow Owner window.
* @param options.mediaQuery Media query string.
* @param [options.rootFontSize] Root font size.
* @returns Media query items.
*/
public static parse(ownerWindow: IWindow, mediaQuery: string): MediaQueryItem[] {
let currentMediaQueryItem: MediaQueryItem = new MediaQueryItem(ownerWindow);
public static parse(options: {
ownerWindow: IWindow;
mediaQuery: string;
rootFontSize?: string | number | null;
}): MediaQueryItem[] {
let currentMediaQueryItem: MediaQueryItem = new MediaQueryItem({
ownerWindow: options.ownerWindow,
rootFontSize: options.rootFontSize
});
const mediaQueryItems: MediaQueryItem[] = [currentMediaQueryItem];
const regexp = new RegExp(MEDIA_QUERY_REGEXP);
let match: RegExpExecArray | null = null;

while ((match = regexp.exec(mediaQuery.toLowerCase()))) {
while ((match = regexp.exec(options.mediaQuery.toLowerCase()))) {
if (match[4] === ',' || match[5] === 'or') {
currentMediaQueryItem = new MediaQueryItem(ownerWindow);
currentMediaQueryItem = new MediaQueryItem({
ownerWindow: options.ownerWindow,
rootFontSize: options.rootFontSize
});
mediaQueryItems.push(currentMediaQueryItem);
} else if (match[1] === 'all' || match[1] === 'screen' || match[1] === 'print') {
currentMediaQueryItem.mediaTypes.push(<MediaQueryTypeEnum>match[1]);
Expand Down Expand Up @@ -80,7 +92,12 @@ export default class MediaQueryParser {
const trimmedValue = value ? value.trim() : null;
if (!trimmedValue && !match[3]) {
return [
new MediaQueryItem(ownerWindow, { not: true, mediaTypes: [MediaQueryTypeEnum.all] })
new MediaQueryItem({
ownerWindow: options.ownerWindow,
rootFontSize: options.rootFontSize,
not: true,
mediaTypes: [MediaQueryTypeEnum.all]
})
];
}
currentMediaQueryItem.rules.push({
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/window/Window.ts
Expand Up @@ -669,7 +669,7 @@ export default class Window extends EventTarget implements IWindow {
* @returns A new MediaQueryList.
*/
public matchMedia(mediaQueryString: string): MediaQueryList {
return new MediaQueryList(this, mediaQueryString);
return new MediaQueryList({ ownerWindow: this, media: mediaQueryString });
}

/**
Expand Down

0 comments on commit 523f379

Please sign in to comment.