Repository: capricorn86/happy-dom
Pull request #323 (2021-12-21 00:48:12)

adding style property to SVGElement
To address https://github.com/capricorn86/happy-dom/issues/319

Similar implementation to `HTMLElement.ts`.


Patch:
diff --git a/packages/happy-dom/src/nodes/svg-element/ISVGElement.ts b/packages/happy-dom/src/nodes/svg-element/ISVGElement.ts
index 5e17871e..012344fb 100644
--- a/packages/happy-dom/src/nodes/svg-element/ISVGElement.ts
+++ b/packages/happy-dom/src/nodes/svg-element/ISVGElement.ts
@@ -1,3 +1,4 @@
+import CSSStyleDeclaration from '../../css/CSSStyleDeclaration';
 import IElement from '../element/IElement';
 import ISVGSVGElement from './ISVGSVGElement';
 
@@ -10,4 +11,5 @@ export default interface ISVGElement extends IElement {
 	readonly viewportElement: ISVGElement;
 	readonly ownerSVGElement: ISVGSVGElement;
 	readonly dataset: { [key: string]: string };
+	style: CSSStyleDeclaration;
 }
diff --git a/packages/happy-dom/src/nodes/svg-element/SVGElement.ts b/packages/happy-dom/src/nodes/svg-element/SVGElement.ts
index 2b274695..b75a44cf 100644
--- a/packages/happy-dom/src/nodes/svg-element/SVGElement.ts
+++ b/packages/happy-dom/src/nodes/svg-element/SVGElement.ts
@@ -1,6 +1,8 @@
+import CSSStyleDeclaration from '../../css/CSSStyleDeclaration';
 import Element from '../element/Element';
 import ISVGElement from './ISVGElement';
 import ISVGSVGElement from './ISVGSVGElement';
+import Attr from '../../attribute/Attr';
 
 /**
  * SVG Element.
@@ -9,6 +11,8 @@ import ISVGSVGElement from './ISVGSVGElement';
  * https://developer.mozilla.org/en-US/docs/Web/API/SVGElement.
  */
 export default class SVGElement extends Element implements ISVGElement {
+	private _style: CSSStyleDeclaration = null;
+
 	/**
 	 * Returns viewport.
 	 *
@@ -47,4 +51,48 @@ export default class SVGElement extends Element implements ISVGElement {
 		}
 		return dataset;
 	}
+
+	/**
+	 * Returns style.
+	 *
+	 * @returns Style.
+	 */
+	public get style(): CSSStyleDeclaration {
+		if (!this._style) {
+			this._style = new CSSStyleDeclaration(this._attributes);
+		}
+		return this._style;
+	}
+
+	/**
+	 * The setAttributeNode() method adds a new Attr node to the specified element.
+	 *
+	 * @override
+	 * @param attribute Attribute.
+	 * @returns Replaced attribute.
+	 */
+	public setAttributeNode(attribute: Attr): Attr {
+		const replacedAttribute = super.setAttributeNode(attribute);
+
+		if (attribute.name === 'style' && this._style) {
+			this._style.cssText = attribute.value;
+		}
+
+		return replacedAttribute;
+	}
+
+	/**
+	 * Removes an Attr node.
+	 *
+	 * @override
+	 * @param attribute Attribute.
+	 */
+	public removeAttributeNode(attribute: Attr): void {
+		super.removeAttributeNode(attribute);
+
+		if (attribute.name === 'style' && this._style) {
+			this._style.cssText = '';
+		}
+	}
+
 }


Test patch:
diff --git a/packages/happy-dom/test/nodes/document/Document.test.ts b/packages/happy-dom/test/nodes/document/Document.test.ts
index c88bddfe..028606f6 100644
--- a/packages/happy-dom/test/nodes/document/Document.test.ts
+++ b/packages/happy-dom/test/nodes/document/Document.test.ts
@@ -671,6 +671,12 @@ describe('Document', () => {
 			expect(div.namespaceURI).toBe(NamespaceURI.svg);
 			expect(div instanceof CustomElement).toBe(true);
 		});
+
+		it('Creates a custom element with namespace set to SVG and can set the style.', () => {
+			const svg = document.createElementNS(NamespaceURI.svg, 'svg');
+			svg.style.cssText = 'user-select:none;';
+			expect(svg.style.cssText).toBe('user-select:none;');
+		});
 	});
 
 	describe('createAttribute()', () => {
diff --git a/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts b/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts
index 852904f7..4adceafc 100644
--- a/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts
+++ b/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts
@@ -189,4 +189,33 @@ describe('SVGSVGElement', () => {
 			expect(element.createSVGTransform() instanceof SVGTransform).toBe(true);
 		});
 	});
+
+	describe('get style()', () => {
+		it('Returns styles.', () => {
+            element.setAttribute('style', 'border-radius: 2px; padding: 2px;');
+			expect(element.style.length).toEqual(2);
+			expect(element.style[0]).toEqual('border-radius');
+			expect(element.style[1]).toEqual('padding');
+			expect(element.style.borderRadius).toEqual('2px');
+			expect(element.style.padding).toEqual('2px');
+			expect(element.style.cssText).toEqual('border-radius: 2px; padding: 2px;');
+
+			element.setAttribute('style', 'border-radius: 4px; padding: 4px;');
+			expect(element.style.length).toEqual(2);
+			expect(element.style[0]).toEqual('border-radius');
+			expect(element.style[1]).toEqual('padding');
+			expect(element.style.borderRadius).toEqual('4px');
+			expect(element.style.padding).toEqual('4px');
+			expect(element.style.cssText).toEqual('border-radius: 4px; padding: 4px;');
+		});
+	});
+	describe('removeAttributeNode()', () => {
+		it('Removes property from CSSStyleDeclaration.', () => {
+			element.style.background = 'green';
+			element.style.color = 'black';
+			element.removeAttribute('style');
+			expect(element.style.length).toEqual(0);
+			expect(element.style.cssText).toEqual('');
+		});
+	});
 });