Skip to content

Commit

Permalink
Merge pull request #1402 from domakas/domrect-implementation
Browse files Browse the repository at this point in the history
fix: [#1161] Implement DOMReact and DOMReactReadOnly interfaces
  • Loading branch information
capricorn86 committed May 6, 2024
2 parents a289408 + c302656 commit 2a8030f
Show file tree
Hide file tree
Showing 6 changed files with 410 additions and 23 deletions.
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
});
});
});
});

0 comments on commit 2a8030f

Please sign in to comment.