Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: [#1161] Implement DOMReact and DOMReactReadOnly interfaces #1402

Merged
merged 3 commits into from May 6, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/happy-dom/src/index.ts
Expand Up @@ -70,6 +70,7 @@ import DocumentFragment from './nodes/document-fragment/DocumentFragment.js';
import DocumentType from './nodes/document-type/DocumentType.js';
import Document from './nodes/document/Document.js';
import DOMRect from './nodes/element/DOMRect.js';
import DOMRectReadOnly from './nodes/element/DOMRectReadOnly.js';
import Element from './nodes/element/Element.js';
import HTMLCollection from './nodes/element/HTMLCollection.js';
import HTMLAnchorElement from './nodes/html-anchor-element/HTMLAnchorElement.js';
Expand Down Expand Up @@ -211,6 +212,7 @@ export {
DOMException,
DOMParser,
DOMRect,
DOMRectReadOnly,
DataTransfer,
DataTransferItem,
DataTransferItemList,
Expand Down
63 changes: 40 additions & 23 deletions packages/happy-dom/src/nodes/element/DOMRect.ts
@@ -1,30 +1,47 @@
import DOMRectReadOnly, { IDOMRectInit } from './DOMRectReadOnly.js';
import * as PropertySymbol from '../../PropertySymbol.js';

/* eslint-disable jsdoc/require-jsdoc */

/**
* Bounding rect object.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMRect
*/
export default class DOMRect {
public x = 0;
public y = 0;
public width = 0;
public height = 0;
public top = 0;
public right = 0;
public bottom = 0;
public left = 0;

/**
* Constructor.
*
* @param [x] X position.
* @param [y] Y position.
* @param [width] Width.
* @param [height] Height.
*/
constructor(x?, y?, width?, height?) {
this.x = x || 0;
this.y = y || 0;
this.width = width || 0;
this.height = height || 0;
export default class DOMRect extends DOMRectReadOnly {
public set x(value: number) {
this[PropertySymbol.x] = value;
}

public get x(): number {
return this[PropertySymbol.x];
}

public set y(value: number) {
this[PropertySymbol.y] = value;
}

public get y(): number {
return this[PropertySymbol.y];
}

public set width(value: number) {
this[PropertySymbol.width] = value;
}

public get width(): number {
return this[PropertySymbol.width];
}

public set height(value: number) {
this[PropertySymbol.height] = value;
}

public get height(): number {
return this[PropertySymbol.height];
}

public static fromRect(other: IDOMRectInit): DOMRect {
return new DOMRect(other.x, other.y, other.width, other.height);
}
}
86 changes: 86 additions & 0 deletions packages/happy-dom/src/nodes/element/DOMRectReadOnly.ts
@@ -0,0 +1,86 @@
import * as PropertySymbol from '../../PropertySymbol.js';

/* eslint-disable jsdoc/require-jsdoc */

/**
* Bounding rect readonly object.
*
* @see https://drafts.fxtf.org/geometry/#DOMRect
*/
export default class DOMRectReadOnly implements IDOMRectInit {
protected [PropertySymbol.x]: number = 0;
protected [PropertySymbol.y]: number = 0;
protected [PropertySymbol.width]: number = 0;
protected [PropertySymbol.height]: number = 0;

/**
* Constructor.
*
* @param [x] X position.
* @param [y] Y position.
* @param [width] Width.
* @param [height] Height.
*/
constructor(x?: number | null, y?: number | null, width?: number | null, height?: number | null) {
this[PropertySymbol.x] = x !== undefined && x !== null ? Number(x) : 0;
this[PropertySymbol.y] = y !== undefined && y !== null ? Number(y) : 0;
this[PropertySymbol.width] = width !== undefined && width !== null ? Number(width) : 0;
this[PropertySymbol.height] = height !== undefined && height !== null ? Number(height) : 0;
}

public get x(): number {
return this[PropertySymbol.x];
}

public get y(): number {
return this[PropertySymbol.y];
}

public get width(): number {
return this[PropertySymbol.width];
}

public get height(): number {
return this[PropertySymbol.height];
}

public get top(): number {
return Math.min(this[PropertySymbol.y], this[PropertySymbol.y] + this[PropertySymbol.height]);
}

public get right(): number {
return Math.max(this[PropertySymbol.x], this[PropertySymbol.x] + this[PropertySymbol.width]);
}

public get bottom(): number {
return Math.max(this[PropertySymbol.y], this[PropertySymbol.y] + this[PropertySymbol.height]);
}

public get left(): number {
return Math.min(this[PropertySymbol.x], this[PropertySymbol.x] + this[PropertySymbol.width]);
}

public toJSON(): object {
return {
x: this.x,
y: this.y,
width: this.width,
height: this.height,
top: this.top,
right: this.right,
bottom: this.bottom,
left: this.left
};
}

public static fromRect(other: IDOMRectInit): DOMRectReadOnly {
return new DOMRectReadOnly(other.x, other.y, other.width, other.height);
}
}

export interface IDOMRectInit {
readonly x: number;
readonly y: number;
readonly width: number;
readonly height: number;
}
2 changes: 2 additions & 0 deletions packages/happy-dom/src/window/BrowserWindow.ts
Expand Up @@ -97,6 +97,7 @@ import Plugin from '../navigator/Plugin.js';
import PluginArray from '../navigator/PluginArray.js';
import Fetch from '../fetch/Fetch.js';
import DOMRect from '../nodes/element/DOMRect.js';
import DOMRectReadOnly from '../nodes/element/DOMRectReadOnly.js';
import VMGlobalPropertyScript from './VMGlobalPropertyScript.js';
import VM from 'vm';
import { Buffer } from 'buffer';
Expand Down Expand Up @@ -372,6 +373,7 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
public readonly PluginArray = PluginArray;
public readonly FileList = FileList;
public readonly DOMRect = DOMRect;
public readonly DOMRectReadOnly = DOMRectReadOnly;
public readonly RadioNodeList = RadioNodeList;
public readonly ValidityState = ValidityState;
public readonly Headers = Headers;
Expand Down
156 changes: 156 additions & 0 deletions packages/happy-dom/test/nodes/element/DOMRect.test.ts
@@ -0,0 +1,156 @@
import { afterEach, describe, it, expect, vi } from 'vitest';
import DOMRect from '../../../src/nodes/element/DOMRect';

describe('DOMRect', () => {
afterEach(() => {
vi.restoreAllMocks();
});

describe('constructor()', () => {
it('Sets properties.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.x).toBe(1);
expect(rect.y).toBe(2);
expect(rect.width).toBe(3);
expect(rect.height).toBe(4);

const rect2 = new DOMRect(null, null, null, 4);
expect(rect2.x).toBe(0);
expect(rect2.y).toBe(0);
expect(rect2.width).toBe(0);
expect(rect2.height).toBe(4);

const rect3 = new DOMRect();
expect(rect3.x).toBe(0);
expect(rect3.y).toBe(0);
expect(rect3.width).toBe(0);
expect(rect3.height).toBe(0);

const rect4 = new DOMRect(
<number>(<unknown>'nan'),
<number>(<unknown>'nan'),
<number>(<unknown>'nan'),
<number>(<unknown>'nan')
);
expect(isNaN(rect4.x)).toBe(true);
expect(isNaN(rect4.y)).toBe(true);
expect(isNaN(rect4.width)).toBe(true);
expect(isNaN(rect4.height)).toBe(true);
});
});

describe('set x()', () => {
it('Sets rect x property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
rect.x = 2;
expect(rect.x).toBe(2);
});
});

describe('get x()', () => {
it('Returns rect x property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.x).toBe(1);
});
});

describe('set y()', () => {
it('Sets rect y property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
rect.y = 3;
expect(rect.y).toBe(3);
});
});

describe('get y()', () => {
it('Returns rect y property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.y).toBe(2);
});
});

describe('set width()', () => {
it('Sets rect y property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
rect.width = 4;
expect(rect.width).toBe(4);
});
});

describe('get width()', () => {
it('Returns rect y property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.width).toBe(3);
});
});

describe('set height()', () => {
it('Sets rect height property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
rect.height = 5;
expect(rect.height).toBe(5);
});
});

describe('get height()', () => {
it('Returns rect height property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.height).toBe(4);
});
});

describe('get top()', () => {
it('Returns rect top property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.top).toBe(2);
});
});

describe('get right()', () => {
it('Returns rect right property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.right).toBe(4);
});
});

describe('get bottom()', () => {
it('Returns rect bottom property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.bottom).toBe(6);
});
});

describe('get left()', () => {
it('Returns rect left property.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.left).toBe(1);
});
});

describe('fromRect()', () => {
it('Creates DOMRect instance', () => {
const rect = DOMRect.fromRect({ x: 1, y: 2, width: 3, height: 4 });
expect(rect instanceof DOMRect).toBe(true);
expect(rect.x).toBe(1);
expect(rect.y).toBe(2);
expect(rect.width).toBe(3);
expect(rect.height).toBe(4);
});
});

describe('toJSON()', () => {
it('Returns rect as JSON.', () => {
const rect = new DOMRect(1, 2, 3, 4);
expect(rect.toJSON()).toEqual({
x: 1,
y: 2,
width: 3,
height: 4,
top: 2,
right: 4,
bottom: 6,
left: 1
});
});
});
});