diff --git a/src/editors/substation/line-editor.ts b/src/editors/substation/line-editor.ts
new file mode 100644
index 0000000000..459c200e6e
--- /dev/null
+++ b/src/editors/substation/line-editor.ts
@@ -0,0 +1,103 @@
+import {
+ customElement,
+ html,
+ LitElement,
+ TemplateResult,
+ property,
+ state,
+} from 'lit-element';
+
+import './conducting-equipment-editor.js';
+import './function-editor.js';
+import './general-equipment-editor.js';
+import './l-node-editor.js';
+import { getChildElementsByTagName } from '../../foundation.js';
+
+@customElement('line-editor')
+export class LineEditor extends LitElement {
+ /** The document being edited as provided to editor by [[`Zeroline`]]. */
+ @property({ attribute: false })
+ doc!: XMLDocument;
+ /** SCL element Line */
+ @property({ attribute: false })
+ element!: Element;
+ /** Whether `Function` and `LNode` are rendered */
+ @property({ type: Boolean })
+ showfunctions = false;
+
+ @state()
+ get header(): string {
+ const name = this.element.getAttribute('name') ?? '';
+ const desc = this.element.getAttribute('desc');
+
+ return `${name} ${desc ? `—${desc}` : ''}`;
+ }
+
+ private renderConductingEquipments(): TemplateResult {
+ const ConductingEquipments = getChildElementsByTagName(
+ this.element,
+ 'ConductingEquipment'
+ );
+ return html` ${ConductingEquipments.map(
+ ConductingEquipment =>
+ html` `
+ )}`;
+ }
+
+ private renderGeneralEquipments(): TemplateResult {
+ const GeneralEquipments = getChildElementsByTagName(
+ this.element,
+ 'GeneralEquipment'
+ );
+ return html` ${GeneralEquipments.map(
+ GeneralEquipment =>
+ html` `
+ )}`;
+ }
+
+ private renderFunctions(): TemplateResult {
+ if (!this.showfunctions) return html``;
+
+ const Functions = getChildElementsByTagName(this.element, 'Function');
+ return html` ${Functions.map(
+ Function =>
+ html` `
+ )}`;
+ }
+
+ private renderLNodes(): TemplateResult {
+ if (!this.showfunctions) return html``;
+
+ const lNodes = getChildElementsByTagName(this.element, 'LNode');
+ return lNodes.length
+ ? html`
+ ${lNodes.map(
+ lNode =>
+ html` `
+ )}
+
`
+ : html``;
+ }
+
+ render(): TemplateResult {
+ return html` ${this.renderConductingEquipments()}${this.renderGeneralEquipments()}${this.renderFunctions()}${this.renderLNodes()}
+ `;
+ }
+}
diff --git a/src/editors/substation/zeroline-pane.ts b/src/editors/substation/zeroline-pane.ts
index 918f0077b9..146a860ea6 100644
--- a/src/editors/substation/zeroline-pane.ts
+++ b/src/editors/substation/zeroline-pane.ts
@@ -14,6 +14,7 @@ import '@material/mwc-icon-button-toggle';
import { IconButton } from '@material/mwc-icon-button';
import { IconButtonToggle } from '@material/mwc-icon-button-toggle';
+import './line-editor.js';
import './substation-editor.js';
import './ied-editor.js';
import { communicationMappingWizard } from '../../wizards/commmap-wizards.js';
@@ -113,11 +114,31 @@ export class ZerolinePane extends LitElement {
return ieds.length
? html`
- ${ieds.map(ied => html` `)}
+ ${ieds.map(
+ ied =>
+ html` `
+ )}
`
: html``;
}
+ renderLines(): TemplateResult {
+ return this.doc?.querySelector(':root > Line')
+ ? html`
+ ${Array.from(this.doc.querySelectorAll('Line') ?? [])
+ .filter(isPublic)
+ .map(
+ line =>
+ html` `
+ )}
+ `
+ : html``;
+ }
+
render(): TemplateResult {
return html`
@@ -198,7 +219,7 @@ export class ZerolinePane extends LitElement {
${translate('substation.missing')}
- `}`;
+ `}${this.renderLines()}`;
}
static styles = css`
diff --git a/test/testfiles/editors/substation/Line.scd b/test/testfiles/editors/substation/Line.scd
new file mode 100644
index 0000000000..3cbdb667bf
--- /dev/null
+++ b/test/testfiles/editors/substation/Line.scd
@@ -0,0 +1,235 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ status-only
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sbo-with-enhanced-security
+
+
+ 30000
+
+
+ 600
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1000
+
+
+ direct-with-enhanced-security
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sbo-with-enhanced-security
+
+
+ 30000
+
+
+ 600
+
+
+
+
+
+
+
+
+
+
+
+ IEC 61850-8-1:2003
+
+
+
+
+
+
+
+
+ IEC 61850-8-1:2003
+
+
+
+
+
+
+
+
+
+
+
+
+
+ IEC 61850-8-1:2003
+
+
+
+
+
+
+
+
+ IEC 61850-8-1:2003
+
+
+
+
+
+
+
+ IEC 61850-8-1:2003
+
+
+
+
+
+
+
+
+ IEC 61850-8-1:2003
+
+
+
+
+
+
+ status-only
+
+
+ pulse
+ persistent
+ persistent-feedback
+
+
+ Ok
+ Warning
+ Alarm
+
+
+ status-only
+ direct-with-normal-security
+ sbo-with-normal-security
+ direct-with-enhanced-security
+ sbo-with-enhanced-security
+
+
+ on
+ blocked
+ test
+ test/blocked
+ off
+
+
+ not-supported
+ bay-control
+ station-control
+ remote-control
+ automatic-bay
+ automatic-station
+ automatic-remote
+ maintenance
+ process
+
+
+
+
+
+
+
+
+ 110.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 380.0
+
+
+
+
+
diff --git a/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js
new file mode 100644
index 0000000000..ae6dbfe89d
--- /dev/null
+++ b/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js
@@ -0,0 +1,56 @@
+/* @web/test-runner snapshot v1 */
+export const snapshots = {};
+
+snapshots["web component rendering Line element rendering LNode and Function children looks like the latest snapshot"] =
+`
+
+
+
+
+
+
+
+
+
+`;
+/* end snapshot web component rendering Line element rendering LNode and Function children looks like the latest snapshot */
+
+snapshots["web component rendering Line element rendering ConductingEquipment looks like the latest snapshot"] =
+`
+
+
+
+
+
+
+
+
+
+`;
+/* end snapshot web component rendering Line element rendering ConductingEquipment looks like the latest snapshot */
+
+snapshots["web component rendering Line element rendering GeneralEquipment looks like the latest snapshot"] =
+`
+
+
+
+
+
+
+
+
+
+
+
+`;
+/* end snapshot web component rendering Line element rendering GeneralEquipment looks like the latest snapshot */
+
diff --git a/test/unit/editors/substation/line-editor.test.ts b/test/unit/editors/substation/line-editor.test.ts
new file mode 100644
index 0000000000..31e6ef9e88
--- /dev/null
+++ b/test/unit/editors/substation/line-editor.test.ts
@@ -0,0 +1,68 @@
+import { fixture, html, expect } from '@open-wc/testing';
+
+import '../../../../src/editors/substation/line-editor.js';
+import { LineEditor } from '../../../../src/editors/substation/line-editor.js';
+
+describe('web component rendering Line element', () => {
+ let element: LineEditor;
+ let doc: XMLDocument;
+
+ describe('rendering LNode and Function children', () => {
+ beforeEach(async () => {
+ doc = await fetch('/test/testfiles/editors/substation/Line.scd')
+ .then(response => response.text())
+ .then(str => new DOMParser().parseFromString(str, 'application/xml'));
+ element = (
+ await fixture(
+ html` `
+ )
+ );
+ element.showfunctions = true;
+ await element.updateComplete;
+ });
+ it('looks like the latest snapshot', async () => {
+ await expect(element).shadowDom.to.equalSnapshot();
+ });
+ });
+
+ describe('rendering ConductingEquipment', () => {
+ beforeEach(async () => {
+ doc = await fetch('/test/testfiles/editors/substation/Line.scd')
+ .then(response => response.text())
+ .then(str => new DOMParser().parseFromString(str, 'application/xml'));
+ element = (
+ await fixture(
+ html` `
+ )
+ );
+ element.showfunctions = true;
+ await element.updateComplete;
+ });
+ it('looks like the latest snapshot', async () => {
+ await expect(element).shadowDom.to.equalSnapshot();
+ });
+ });
+ describe('rendering GeneralEquipment', () => {
+ beforeEach(async () => {
+ doc = await fetch('/test/testfiles/editors/substation/Line.scd')
+ .then(response => response.text())
+ .then(str => new DOMParser().parseFromString(str, 'application/xml'));
+ element = (
+ await fixture(
+ html` `
+ )
+ );
+ element.showfunctions = true;
+ await element.updateComplete;
+ });
+ it('looks like the latest snapshot', async () => {
+ await expect(element).shadowDom.to.equalSnapshot();
+ });
+ });
+});