diff --git a/src/editors/ied/da-container.ts b/src/editors/ied/da-container.ts
new file mode 100644
index 000000000..092952205
--- /dev/null
+++ b/src/editors/ied/da-container.ts
@@ -0,0 +1,116 @@
+import {
+ css,
+ customElement,
+ html,
+ LitElement,
+ property,
+ query,
+ TemplateResult,
+} from 'lit-element';
+import { nothing } from 'lit-html';
+import { translate } from 'lit-translate';
+
+import '@material/mwc-icon-button-toggle';
+import { IconButtonToggle } from '@material/mwc-icon-button-toggle';
+
+import '../../action-pane.js';
+import { getNameAttribute } from '../../foundation.js';
+
+/** [[`IED`]] plugin subeditor for editing `(B)DA` element. */
+@customElement('da-container')
+export class DAContainer extends LitElement {
+ /**
+ * The DA itself.
+ */
+ @property({ attribute: false })
+ element!: Element;
+
+ /**
+ * The optional DAI of this (B)DA.
+ */
+ @property({ attribute: false })
+ instanceElement!: Element;
+
+ @query('#toggleButton') toggleButton: IconButtonToggle | undefined;
+
+ private header(): TemplateResult {
+ const name = getNameAttribute(this.element);
+ const bType = this.element!.getAttribute('bType') ?? nothing;
+
+ if (this.instanceElement) {
+ return html`${name} — ${bType}`;
+ } else {
+ return html`${name} — ${bType}`;
+ }
+ }
+
+ /**
+ * Rendering an optional value of this (B)DA container.
+ * If there is a DAI, it get's priority on top of (B)DA values.
+ * @returns TemplateResult containing the value of the instance, element or nothing.
+ */
+ private renderValue(): TemplateResult {
+ if (this.instanceElement) {
+ return html`${this.getValueElement(this.instanceElement)?.textContent}`
+ }
+
+ return html`${this.getValueElement(this.element)?.textContent}`;
+ }
+
+ /**
+ * Get the 'Val' element of another element.
+ * @param element - The element to search for an 'Val' element.
+ * @returns the 'Val' element, or null if not found.
+ */
+ private getValueElement(element: Element): Element | null {
+ return element.querySelector('Val') ?? null;
+ }
+
+ /**
+ * Get the nested (B)DA element(s) if available.
+ * @returns The nested (B)DA element(s) of this (B)DA container.
+ */
+ private getBDAElements(): Element[] {
+ const type = this.element!.getAttribute('type') ?? undefined;
+ const doType = this.element!.closest('SCL')!.querySelector(`:root > DataTypeTemplates > DAType[id="${type}"]`);
+ if (doType != null) {
+ return Array.from(doType!.querySelectorAll(':scope > BDA'))
+ }
+ return [];
+ }
+
+ render(): TemplateResult {
+ const bType = this.element!.getAttribute('bType');
+
+ return html`
+ ${bType == 'Struct' ? html`
+ this.requestUpdate()}
+ >
+ ` : nothing}
+ ${this.renderValue()}
+ ${this.toggleButton?.on && bType == 'Struct' ? this.getBDAElements().map(element =>
+ html`
+ `) : nothing}
+
+ `;
+ }
+
+ static styles = css`
+ h6 {
+ color: var(--mdc-theme-on-surface);
+ font-family: 'Roboto', sans-serif;
+ font-weight: 500;
+ font-size: 0.8em;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ margin: 0px;
+ padding-left: 0.3em;
+ }
+ `;
+}
\ No newline at end of file
diff --git a/src/editors/ied/do-container.ts b/src/editors/ied/do-container.ts
index ef49c9e42..de1660819 100644
--- a/src/editors/ied/do-container.ts
+++ b/src/editors/ied/do-container.ts
@@ -1,15 +1,20 @@
import {
- css,
customElement,
html,
LitElement,
property,
+ query,
TemplateResult,
} from 'lit-element';
import { nothing } from 'lit-html';
+import '@material/mwc-icon-button-toggle';
+import { IconButtonToggle } from '@material/mwc-icon-button-toggle';
+
import '../../action-pane.js';
+import './da-container.js';
import { getDescriptionAttribute, getNameAttribute } from '../../foundation.js';
+import { translate } from 'lit-translate';
/** [[`IED`]] plugin subeditor for editing `DO` element. */
@customElement('do-container')
@@ -25,6 +30,8 @@ export class DOContainer extends LitElement {
*/
@property({ attribute: false })
instanceElement!: Element;
+
+ @query('#toggleButton') toggleButton: IconButtonToggle | undefined;
private header(): TemplateResult {
const name = getNameAttribute(this.element);
@@ -51,25 +58,68 @@ export class DOContainer extends LitElement {
}
/**
- * Get the instance element (SDI) of a SDO element (if available)
- * @param sdo - The SDO object to search with.
+ * Get the nested (B)DA element(s).
+ * @returns The nested (B)DA element(s) of this DO container.
+ */
+ private getDAElements(): Element[] {
+ const type = this.element.getAttribute('type') ?? undefined;
+ const doType = this.element.closest('SCL')!.querySelector(`:root > DataTypeTemplates > DOType[id="${type}"]`);
+ if (doType != null) {
+ return Array.from(doType!.querySelectorAll(':scope > DA'))
+ }
+ return [];
+ }
+
+ /**
+ * Get the instance element (SDI) of a (S)DO element (if available)
+ * @param dO - The (S)DO object to search with.
* @returns The optional SDI element.
*/
- private getInstanceElement(sdo: Element): Element | null {
- const sdoName = getNameAttribute(sdo);
+ private getInstanceDOElement(dO: Element): Element | null {
+ const sdoName = getNameAttribute(dO);
if (this.instanceElement) {
return this.instanceElement.querySelector(`:scope > SDI[name="${sdoName}"]`)
}
return null;
}
+ /**
+ * Get the instance element (DAI) of a DA element (if available)
+ * @param da - The (B)DA object to search with.
+ * @returns The optional DAI element.
+ */
+ private getInstanceDAElement(da: Element): Element | null {
+ const daName = getNameAttribute(da);
+ if (this.instanceElement) {
+ return this.instanceElement.querySelector(`:scope > DAI[name="${daName}"]`)
+ }
+ return null;
+ }
+
render(): TemplateResult {
+ const daElements = this.getDAElements();
+ const doElements = this.getDOElements();
+
return html`
- ${this.getDOElements().map(dO =>
+ ${daElements.length > 0 || doElements.length > 0 ?
+ html`
+ this.requestUpdate()}
+ >
+ ` : nothing}
+ ${this.toggleButton?.on ? daElements.map(da =>
+ html`
+ `) : nothing}
+ ${this.toggleButton?.on ? doElements.map(dO =>
html`
- `)}
+ .instanceElement=${this.getInstanceDOElement(dO)}>
+ `) : nothing}
`;
}
diff --git a/src/editors/ied/ln-container.ts b/src/editors/ied/ln-container.ts
index 34854e8fd..67e5c1832 100644
--- a/src/editors/ied/ln-container.ts
+++ b/src/editors/ied/ln-container.ts
@@ -4,6 +4,7 @@ import {
html,
LitElement,
property,
+ query,
TemplateResult,
} from 'lit-element';
import { nothing } from 'lit-html';
@@ -11,12 +12,16 @@ import { nothing } from 'lit-html';
import '../../action-pane.js';
import './do-container.js';
import { getInstanceAttribute, getNameAttribute } from '../../foundation.js';
+import { translate } from 'lit-translate';
+import { IconButtonToggle } from '@material/mwc-icon-button-toggle';
/** [[`IED`]] plugin subeditor for editing `LN` and `LN0` element. */
@customElement('ln-container')
export class LNContainer extends LitElement {
@property({ attribute: false })
element!: Element;
+
+ @query('#toggleButton') toggleButton!: IconButtonToggle | undefined;
private header(): TemplateResult {
const prefix = this.element.getAttribute('prefix');
@@ -43,7 +48,7 @@ export class LNContainer extends LitElement {
/**
* Get the instance element (DOI) of a DO element (if available)
- * @param dO - The DOI object to use.
+ * @param dO - The DO object to use.
* @returns The optional DOI object.
*/
private getInstanceElement(dO: Element): Element | null {
@@ -52,12 +57,22 @@ export class LNContainer extends LitElement {
}
render(): TemplateResult {
+ const doElements = this.getDOElements();
+
return html`
- ${this.getDOElements().map(dO => html` 0 ? html`
+ this.requestUpdate()}
+ >
+ ` : nothing}
+ ${this.toggleButton?.on ? this.getDOElements().map(dO => html`
- `)}
+ `) : nothing}
`;
}
diff --git a/src/translations/de.ts b/src/translations/de.ts
index 34ab4b1d2..94413bc37 100644
--- a/src/translations/de.ts
+++ b/src/translations/de.ts
@@ -162,6 +162,7 @@ export const de: Translations = {
searchHelper: 'IED auswählen',
searchHelperDesc: '({{description}})',
missing: 'Kein IED vorhanden',
+ toggleChildElements: "???"
},
voltagelevel: {
name: 'Spannungsebene',
diff --git a/src/translations/en.ts b/src/translations/en.ts
index 0e4cf58e4..857c60754 100644
--- a/src/translations/en.ts
+++ b/src/translations/en.ts
@@ -158,7 +158,8 @@ export const en = {
iededitor: {
searchHelper: "Select IED",
searchHelperDesc: "({{description}})",
- missing: 'No IED'
+ missing: 'No IED',
+ toggleChildElements: "Toggle child elements"
},
voltagelevel: {
name: 'Voltage level',
diff --git a/test/unit/editors/ied/__snapshots__/da-container.test.snap.js b/test/unit/editors/ied/__snapshots__/da-container.test.snap.js
new file mode 100644
index 000000000..c69d5ce88
--- /dev/null
+++ b/test/unit/editors/ied/__snapshots__/da-container.test.snap.js
@@ -0,0 +1,61 @@
+/* @web/test-runner snapshot v1 */
+export const snapshots = {};
+
+snapshots["da-container looks like the latest snapshot with a DA element"] =
+`
+
+
+
+`;
+/* end snapshot da-container looks like the latest snapshot with a DA element */
+
+snapshots["da-container looks like the latest snapshot with a DA element and child elements are toggled."] =
+`
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+/* end snapshot da-container looks like the latest snapshot with a DA element and child elements are toggled. */
+
+snapshots["da-container looks like the latest snapshot with a DA element containing and a DAI."] =
+`
+
+ status-only
+
+
+`;
+/* end snapshot da-container looks like the latest snapshot with a DA element containing and a DAI. */
+
diff --git a/test/unit/editors/ied/__snapshots__/do-container.test.snap.js b/test/unit/editors/ied/__snapshots__/do-container.test.snap.js
index 54435eba5..ded699c08 100644
--- a/test/unit/editors/ied/__snapshots__/do-container.test.snap.js
+++ b/test/unit/editors/ied/__snapshots__/do-container.test.snap.js
@@ -1,23 +1,121 @@
/* @web/test-runner snapshot v1 */
export const snapshots = {};
+snapshots["do-container looks like the latest snapshot with a DO element."] =
+`
+
+
+
+
+
+`;
+/* end snapshot do-container looks like the latest snapshot with a DO element. */
+
+
+
+snapshots["do-container looks like the latest snapshot with a DO element and child elements are toggled."] =
+`
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+/* end snapshot do-container looks like the latest snapshot with a DO element and child elements are toggled. */
+
snapshots["do-container looks like the latest snapshot with a SDO element."] =
`
-
-
+
+
+
+
`;
/* end snapshot do-container looks like the latest snapshot with a SDO element. */
-snapshots["do-container looks like the latest snapshot with a DO element."] =
+snapshots["do-container looks like the latest snapshot with a SDO element and child elements are toggled."] =
`
+
+
+
+
+
+
+
+
+
+
`;
-/* end snapshot do-container looks like the latest snapshot with a DO element. */
+/* end snapshot do-container looks like the latest snapshot with a SDO element and child elements are toggled. */
diff --git a/test/unit/editors/ied/__snapshots__/ln-container.test.snap.js b/test/unit/editors/ied/__snapshots__/ln-container.test.snap.js
index 9aa4546e4..875757d8b 100644
--- a/test/unit/editors/ied/__snapshots__/ln-container.test.snap.js
+++ b/test/unit/editors/ied/__snapshots__/ln-container.test.snap.js
@@ -3,6 +3,35 @@ export const snapshots = {};
snapshots["ln-container looks like the latest snapshot with a LN0 element."] =
`
+
+
+
+
+
+`;
+/* end snapshot ln-container looks like the latest snapshot with a LN0 element. */
+
+snapshots["ln-container looks like the latest snapshot with a LN0 element and child elements are toggled."] =
+`
+
+
+
+
@@ -17,10 +46,22 @@ snapshots["ln-container looks like the latest snapshot with a LN0 element."] =
`;
-/* end snapshot ln-container looks like the latest snapshot with a LN0 element. */
+/* end snapshot ln-container looks like the latest snapshot with a LN0 element and child elements are toggled. */
-snapshots["ln-container looks like the latest snapshot with a LN element."] =
+snapshots["ln-container looks like the latest snapshot with a LN element and child elements are toggled."] =
`
+
+
+
+
@@ -37,5 +78,22 @@ snapshots["ln-container looks like the latest snapshot with a LN element."] =
`;
+/* end snapshot ln-container looks like the latest snapshot with a LN element and child elements are toggled. */
+
+snapshots["ln-container looks like the latest snapshot with a LN element."] =
+`
+
+
+
+
+
+`;
/* end snapshot ln-container looks like the latest snapshot with a LN element. */
diff --git a/test/unit/editors/ied/da-container.test.ts b/test/unit/editors/ied/da-container.test.ts
new file mode 100644
index 000000000..36e13c9cf
--- /dev/null
+++ b/test/unit/editors/ied/da-container.test.ts
@@ -0,0 +1,77 @@
+import { html, fixture, expect } from '@open-wc/testing';
+
+import '../../../../src/editors/ied/da-container.js';
+import { DAContainer } from '../../../../src/editors/ied/da-container.js';
+
+describe('da-container', () => {
+ let element: DAContainer;
+ let validSCL: XMLDocument;
+
+ beforeEach(async () => {
+ validSCL = await fetch('/test/testfiles/valid2007B4withIEDModifications.scd')
+ .then(response => response.text())
+ .then(str => new DOMParser().parseFromString(str, 'application/xml'));
+ });
+
+ it('looks like the latest snapshot with a DA element', async () => {
+ element = await fixture(html` DOType[id="Dummy.XCBR1.Pos"] > DA[name="ctlModel"]')}
+ >`);
+ expect(element).shadowDom.to.equalSnapshot();
+ });
+
+ it('looks like the latest snapshot with a DA element and child elements are toggled.', async () => {
+ element = await fixture(html` DOType[id="Dummy.LPHD1.Sim"] > DA[name="SBOw"]')}
+ >`);
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element).shadowDom.to.equalSnapshot();
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element.shadowRoot!.querySelectorAll('do-container').length).to.eql(0);
+ });
+
+ it('looks like the latest snapshot with a DA element containing and a DAI.', async () => {
+ element = await fixture(html` DOType[id="Dummy.XCBR1.Pos"] > DA[name="ctlModel"]')}
+ .instanceElement=${validSCL.querySelector(
+ ':root > IED[name="IED2"] > AccessPoint[name="P1"] > Server > LDevice[inst="CircuitBreaker_CB1"] > LN[lnType="Dummy.XCBR1"] > DOI[name="Pos"]> DAI[name="ctlModel"]')}
+ >`);
+ expect(element).shadowDom.to.equalSnapshot();
+ });
+
+ describe('has a getBDAElements function ', () => {
+ it('which returns BDA elements if available', async () => {
+ element = await fixture(html` DOType[id="Dummy.LPHD1.Sim"] > DA[name="SBOw"]')}
+ >`);
+
+ const bdaElements = element['getBDAElements']();
+ expect(bdaElements.length).to.eql(6);
+ expect(bdaElements[4].getAttribute('name')).to.eql('Test');
+ });
+
+ it('which returns no BDA elements if they are not available', async () => {
+ element = await fixture(html` DOType[id="Dummy.LPHD1.Sim"] > DA[name="SBO"]')}
+ >`);
+
+ const bdaElements = element['getBDAElements']();
+ expect(bdaElements).to.be.empty;
+ });
+ });
+});
diff --git a/test/unit/editors/ied/do-container.test.ts b/test/unit/editors/ied/do-container.test.ts
index e477aa514..b4c3b4980 100644
--- a/test/unit/editors/ied/do-container.test.ts
+++ b/test/unit/editors/ied/do-container.test.ts
@@ -21,6 +21,27 @@ describe('do-container', () => {
expect(element).shadowDom.to.equalSnapshot();
});
+ it('looks like the latest snapshot with a DO element and child elements are toggled.', async () => {
+ element = await fixture(html` LNodeType[id="Dummy.LLN0"] > DO[name="Mod"]')}
+ >`);
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element).shadowDom.to.equalSnapshot();
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element.shadowRoot!.querySelectorAll('do-container').length).to.eql(0);
+ });
+
it('looks like the latest snapshot with a SDO element.', async () => {
element = await fixture(html` {
expect(element).shadowDom.to.equalSnapshot();
});
+ it('looks like the latest snapshot with a SDO element and child elements are toggled.', async () => {
+ element = await fixture(html` DOType[id="Dummy.LLN0.ExtendedMod"] > SDO[name="someSdo"]')}
+ >`);
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element).shadowDom.to.equalSnapshot();
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element.shadowRoot!.querySelectorAll('do-container').length).to.eql(0);
+ });
+
describe('has a getDOElements function ', () => {
it('which return the (S)DO containers underneath a given DO.', async () => {
element = await fixture(html` {
expect(element).shadowDom.to.equalSnapshot();
});
+ it('looks like the latest snapshot with a LN0 element and child elements are toggled.', async () => {
+ element = await fixture(html` AccessPoint[name="P1"] > Server > LDevice[inst="CircuitBreaker_CB1"] > LN0[lnClass="LLN0"]')}
+ >`);
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element).shadowDom.to.equalSnapshot();
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element.shadowRoot!.querySelectorAll('do-container').length).to.eql(0);
+ });
+
it('looks like the latest snapshot with a LN element.', async () => {
element = await fixture(html` {
expect(element).shadowDom.to.equalSnapshot();
});
+ it('looks like the latest snapshot with a LN element and child elements are toggled.', async () => {
+ element = await fixture(html` AccessPoint[name="P1"] > Server > LDevice[inst="CircuitBreaker_CB1"] > LN[lnClass="XCBR"]')}
+ >`);
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element).shadowDom.to.equalSnapshot();
+
+ ((
+ element.shadowRoot!.querySelector('mwc-icon-button-toggle')
+ )).click();
+ await element.requestUpdate();
+ await element.updateComplete;
+ expect(element.shadowRoot!.querySelectorAll('do-container').length).to.eql(0);
+ });
+
describe('has a getDOElements function ', () => {
it('which return the DO containers underneath a given LN.', async () => {
element = await fixture(html`