Skip to content

Commit

Permalink
Feat: Added XML Package for OpenSCD (#1536)
Browse files Browse the repository at this point in the history
* Chore: Added oscd-edit-completed Event

* Added import to initiator

* Added import to initiator

* Updated event to EditCompletedEvent

* Changed DocChangedEvent

* Fixed tests

* Feat: Added XML Package for OpenSCD

* Moved tests

* Fixed tsconfig

* Always emit the editCompletedEvent from the Editor

* Fixed tests

* Removed @open-wc from core eslint

* Removed unused files

* Fixed codacy issues

* Fixed codacy issue

* Added @open-wc/testing framework

* Added coverage folder to gitignore

* Removed puncutation from CONTRIBUTIN.md

* Fixed codacy issues on CONTRIBUTING.md

* Added sinon-chai

* Fixed tsconfig

* Delete packages/xml/CONTRIBUTING.md
  • Loading branch information
pascalwilbrink committed Jun 7, 2024
1 parent 34a58c0 commit ca60c2a
Show file tree
Hide file tree
Showing 103 changed files with 7,953 additions and 26,543 deletions.
31,496 changes: 5,392 additions & 26,104 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions packages/addons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
],
"dependencies": {
"lit": "^2.2.7",
"@openscd/core": "*",
"@openscd/components": "*"
"@openscd/core": "*"
},
"//": {
"clean": "rimraf build",
Expand Down
135 changes: 0 additions & 135 deletions packages/components/package.json

This file was deleted.

1 change: 1 addition & 0 deletions packages/core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"declaration": true,
"importHelpers": true,
"outDir": "dist",
Expand Down
3 changes: 2 additions & 1 deletion packages/openscd/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"lit-translate": "^1.2.1",
"marked": "^4.0.10",
"panzoom": "^9.4.2",
"@openscd/core": "*"
"@openscd/core": "*",
"@openscd/xml": "*"
},
"scripts": {
"clean": "rimraf build",
Expand Down
69 changes: 2 additions & 67 deletions packages/openscd/src/foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Select } from '@material/mwc-select';
import { TextField } from '@material/mwc-textfield';
import AceEditor from 'ace-custom-element';

import { getChildElementsByTagName } from '@openscd/xml';

import { WizardTextField } from './wizard-textfield.js';
import { WizardSelect } from './wizard-select.js';
import { WizardCheckbox } from './wizard-checkbox.js';
Expand Down Expand Up @@ -2325,32 +2327,6 @@ export function isEqual(a: Element, b: Element): boolean {
return true;
}

/** @returns a new [[`tag`]] element owned by [[`doc`]]. */
export function createElement(
doc: Document,
tag: string,
attrs: Record<string, string | null>
): Element {
const element = doc.createElementNS(doc.documentElement.namespaceURI, tag);
Object.entries(attrs)
.filter(([_, value]) => value !== null)
.forEach(([name, value]) => element.setAttribute(name, value!));
return element;
}

/** @returns a clone of `element` with attributes set to values from `attrs`. */
export function cloneElement(
element: Element,
attrs: Record<string, string | null>
): Element {
const newElement = <Element>element.cloneNode(false);
Object.entries(attrs).forEach(([name, value]) => {
if (value === null) newElement.removeAttribute(name);
else newElement.setAttribute(name, value);
});
return newElement;
}

/** A directive rendering its argument `rendered` only if `rendered !== {}`. */
export const ifImplemented = directive(rendered => (part: Part) => {
if (Object.keys(rendered).length) part.setValue(rendered);
Expand Down Expand Up @@ -2438,18 +2414,6 @@ export function depth(t: Record<string, unknown>, mem = new WeakSet()): number {
}
}

export function getUniqueElementName(
parent: Element,
tagName: string,
iteration = 1
): string {
const newName = 'new' + tagName + iteration;
const child = parent.querySelector(`:scope > ${tagName}[name="${newName}"]`);

if (!child) return newName;
else return getUniqueElementName(parent, tagName, ++iteration);
}

export function findFCDAs(extRef: Element): Element[] {
if (extRef.tagName !== 'ExtRef' || extRef.closest('Private')) return [];

Expand Down Expand Up @@ -2519,16 +2483,6 @@ export function getVersion(element: Element): string {
return header[0].getAttribute('version') ?? '2003';
}

export function getChildElementsByTagName(
element: Element | null | undefined,
tag: string | null | undefined
): Element[] {
if (!element || !tag) return [];
return Array.from(element.children).filter(
element => element.tagName === tag
);
}

/** maximum value for `lnInst` attribute */
const maxLnInst = 99;
const lnInstRange = Array(maxLnInst)
Expand Down Expand Up @@ -2566,25 +2520,6 @@ export function newLnInstGenerator(
};
}

/**
* Format xml string in "pretty print" style and return as a string
* @param xml - xml document as a string
* @param tab - character to use as a tab
* @returns string with pretty print formatting
*/
export function formatXml(xml: string, tab?: string): string {
let formatted = '',
indent = '';

if (!tab) tab = '\t';
xml.split(/>\s*</).forEach(function (node) {
if (node.match(/^\/\w/)) indent = indent.substring(tab!.length);
formatted += indent + '<' + node + '>\r\n';
if (node.match(/^<?\w[^>]*[^/]$/)) indent += tab;
});
return formatted.substring(1, formatted.length - 3);
}

/**
* @param lnElements - The LN elements to be scanned for `inst`
* values already in use.
Expand Down
5 changes: 3 additions & 2 deletions packages/openscd/src/wizard-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import { IconButton } from '@material/mwc-icon-button';
import { List } from '@material/mwc-list';
import { Menu } from '@material/mwc-menu';

import { formatXml } from '@openscd/xml';

import 'ace-custom-element';
import './wizard-checkbox.js';
import './wizard-textfield.js';
import './wizard-select.js';

import{
import {
newActionEvent,
Delete,
Create,
Expand All @@ -45,7 +47,6 @@ import {
identity,
WizardInput,
WizardMenuActor,
formatXml,
} from './foundation.js';

function renderWizardInput(
Expand Down
80 changes: 0 additions & 80 deletions packages/openscd/test/unit/foundation.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { expect, fixture, html } from '@open-wc/testing';

import {
cloneElement,
depth,
find,
findControlBlocks,
findFCDAs,
getChildElementsByTagName,
getNameAttribute,
getReference,
getSclSchemaVersion,
getUniqueElementName,
identity,
ifImplemented,
newLnInstGenerator,
Expand Down Expand Up @@ -473,26 +470,6 @@ describe('foundation', () => {
});
});

describe('getUniqueElementName', () => {
let parent: Element;
beforeEach(() => {
const testDoc = new DOMParser().parseFromString(
'<Parent>' +
'<Child name="newChild1"/><Child name="newChild2"/>' +
'<Child2 name="newChild3"/><Child2 name="newChild21"/>' +
'</Parent>',
'application/xml'
);
parent = testDoc.querySelector<Element>('Parent')!;
});

it('returns unique name for Child', () =>
expect(getUniqueElementName(parent, 'Child')).to.equal('newChild3'));

it('returns unique name for Child2', () =>
expect(getUniqueElementName(parent, 'Child2')).to.equal('newChild22'));
});

describe('getNameAttribute', () => {
it('expect the correct value of the name attribute', () => {
const doElement = scl1.querySelector(
Expand Down Expand Up @@ -535,63 +512,6 @@ describe('foundation', () => {
});
});

describe('getChildElementsByTagName', () => {
let doc: Document;
beforeEach(async () => {
doc = await fetch('/test/testfiles/lnodewizard.scd')
.then(response => response.text())
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
});
it('returns a child Element array with a specific tag', () => {
const parent = doc.querySelector('Bay[name="COUPLING_BAY"]');
expect(getChildElementsByTagName(parent!, 'LNode').length).to.have.equal(
parent?.querySelectorAll(
':root > Substation > VoltageLevel > Bay[name="COUPLING_BAY"] > LNode'
).length
);
});
});

describe('cloneElement', () => {
let element: Element;
beforeEach(() => {
element = new DOMParser().parseFromString(
`<Element attr1="attrValue" ></Element>`,
'application/xml'
).documentElement;
});
it('does not copy child nodes', () => {
const newElement = cloneElement(element, {});
expect(newElement.childNodes.length).to.equal(0);
});
it('creates a newElement with specified attrs', () => {
const attr1 = 'newAttr1';
const attr2 = 'newAttr2';
const newElement = cloneElement(element, { attr1, attr2 });
expect(newElement.attributes.length).to.equal(2);
expect(newElement).to.have.attribute('attr2', 'newAttr2');
});
it('leaves attr untouched if not part of attrs', () => {
const attr2 = 'newAttr2';
const newElement = cloneElement(element, { attr2 });
expect(newElement.attributes.length).to.equal(2);
expect(newElement).to.have.attribute('attr1', 'attrValue');
});
it('updates existing attr if part of attrs', () => {
const attr1 = 'newAttr1';
const newElement = cloneElement(element, { attr1 });
expect(newElement.attributes.length).to.equal(1);
expect(newElement).to.have.attribute('attr1', 'newAttr1');
});
it('removes existing attr if set to null', () => {
const attr1 = null;
const attr2 = 'newAttr2';
const newElement = cloneElement(element, { attr1, attr2 });
expect(newElement.attributes.length).to.equal(1);
expect(newElement).to.not.have.attribute('attr1');
});
});

describe('depth', () => {
const circular = { a: { b: {} }, c: {} };
circular.a.b = circular;
Expand Down
Loading

0 comments on commit ca60c2a

Please sign in to comment.