diff --git a/src/action-icon.ts b/src/action-icon.ts index aa5f5b8f7f..6b09d2ea11 100644 --- a/src/action-icon.ts +++ b/src/action-icon.ts @@ -31,6 +31,9 @@ export class ActionIcon extends LitElement { /** highlight pane with dotted outline */ @property({ type: Boolean }) highlighted = false; + /** disables CSS adoption to action buttons */ + @property({ type: Boolean }) + hideActions = false; async firstUpdated(): Promise { this.tabIndex = 0; @@ -75,16 +78,6 @@ export class ActionIcon extends LitElement { --mdc-icon-size: 64px; } - :host(:focus-within) ::slotted([slot='icon']), - :host(:focus-within) mwc-icon { - outline-style: solid; - outline-width: 4px; - transform: scale(0.8); - transition: all 250ms linear; - box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), - 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); - } - :host([secondary]) ::slotted([slot='icon']), :host([secondary]) mwc-icon { outline-color: var(--mdc-theme-secondary); @@ -96,6 +89,20 @@ export class ActionIcon extends LitElement { outline-width: 2px; } + :host(:focus-within) ::slotted([slot='icon']), + :host(:focus-within) mwc-icon { + outline-style: solid; + outline-width: 4px; + } + + :host(:focus-within:not([hideActions])) ::slotted([slot='icon']), + :host(:focus-within:not([hideActions])) mwc-icon { + transform: scale(0.8); + transition: all 250ms linear; + box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); + } + ::slotted([slot='icon']:hover), mwc-icon:hover { outline-style: dashed; @@ -178,6 +185,10 @@ export class ActionIcon extends LitElement { opacity 200ms linear; } + :host([secondary]) header { + background-color: var(--mdc-theme-secondary); + } + :host(:hover) header { position: absolute; opacity: 1; @@ -191,11 +202,18 @@ export class ActionIcon extends LitElement { :host(:focus-within) header { position: absolute; opacity: 1; - transform: translate(0, -80px); box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), opacity 250ms linear; } + + :host(:focus-within:not([hideActions])) header { + transform: translate(0, -80px); + } + + :host(:focus-within[hideActions]) header { + transform: translate(0, -40px); + } `; } diff --git a/src/editors/substation/bay-editor.ts b/src/editors/substation/bay-editor.ts index d6199b3e48..337040f42a 100644 --- a/src/editors/substation/bay-editor.ts +++ b/src/editors/substation/bay-editor.ts @@ -99,6 +99,20 @@ export class BayEditor extends LitElement { this.addMenu.anchor = this.addButton; } + private renderLNodes(): TemplateResult { + if (!this.showfunctions) return html``; + + const lNodes = getChildElementsByTagName(this.element, 'LNode'); + + return lNodes.length + ? html`
+ ${lNodes.map( + lNode => html`` + )} +
` + : html``; + } + renderFunctions(): TemplateResult { if (!this.showfunctions) return html``; @@ -177,7 +191,7 @@ export class BayEditor extends LitElement { >${this.renderAddButtons()} - ${this.renderIedContainer()} ${this.renderFunctions()} + ${this.renderIedContainer()}${this.renderLNodes()}${this.renderFunctions()}
+ ${lNodes.map( + lNode => html`` + )} +
` + : html``; + } + renderEqFunctions(): TemplateResult { if (!this.showfunctions) return html``; @@ -143,7 +156,7 @@ export class ConductingEquipmentEditor extends LitElement { render(): TemplateResult { if (this.showfunctions) return html`${this.renderContentPane()}${this.renderEqFunctions()}${this.renderContentPane()}${this.renderLNodes()}${this.renderEqFunctions()}`; return html` + ${lNodes.map( + lNode => html`` + )} + ` + : html``; + } + private renderEqSubFunctions(): TemplateResult { const eqSubFunctions = getChildElementsByTagName( this.element, @@ -45,7 +58,17 @@ export class EqFunctionEditor extends LitElement { icon="functions" secondary highlighted - >${this.renderEqSubFunctions()}${this.renderLNodes()}${this.renderEqSubFunctions()}`; } + + static styles = css` + .container.lnode { + display: grid; + grid-gap: 12px; + padding: 8px 12px 16px; + box-sizing: border-box; + grid-template-columns: repeat(auto-fit, minmax(64px, auto)); + } + `; } diff --git a/src/editors/substation/eq-sub-function-editor.ts b/src/editors/substation/eq-sub-function-editor.ts index 4981a12260..95f49bc8db 100644 --- a/src/editors/substation/eq-sub-function-editor.ts +++ b/src/editors/substation/eq-sub-function-editor.ts @@ -5,6 +5,7 @@ import { property, customElement, state, + css, } from 'lit-element'; import '../../action-pane.js'; @@ -38,9 +39,31 @@ export class EqSubFunctionEditor extends LitElement { )}`; } + private renderLNodes(): TemplateResult { + const lNodes = getChildElementsByTagName(this.element, 'LNode'); + + return lNodes.length + ? html`
+ ${lNodes.map( + lNode => html`` + )} +
` + : html``; + } + render(): TemplateResult { return html`${this.renderEqSubFunctions()}${this.renderLNodes()}${this.renderEqSubFunctions()}`; } + + static styles = css` + .container.lnode { + display: grid; + grid-gap: 12px; + padding: 8px 12px 16px; + box-sizing: border-box; + grid-template-columns: repeat(auto-fit, minmax(64px, auto)); + } + `; } diff --git a/src/editors/substation/foundation.ts b/src/editors/substation/foundation.ts index 8fa707c687..8ad144f719 100644 --- a/src/editors/substation/foundation.ts +++ b/src/editors/substation/foundation.ts @@ -297,6 +297,14 @@ export const styles = css` grid-template-columns: repeat(auto-fit, minmax(64px, auto)); } + .container.lnode { + display: grid; + grid-gap: 12px; + padding: 8px 12px 16px; + box-sizing: border-box; + grid-template-columns: repeat(auto-fit, minmax(64px, auto)); + } + powertransformer-editor[showfunctions] { margin: 4px 8px 16px; } diff --git a/src/editors/substation/function-editor.ts b/src/editors/substation/function-editor.ts index d83fb8be83..bb646d20ab 100644 --- a/src/editors/substation/function-editor.ts +++ b/src/editors/substation/function-editor.ts @@ -5,6 +5,7 @@ import { property, customElement, state, + css, } from 'lit-element'; import '../../action-pane.js'; @@ -26,6 +27,18 @@ export class FunctionEditor extends LitElement { return `${name}${desc ? ` - ${desc}` : ''}${type ? ` (${type})` : ''}`; } + private renderLNodes(): TemplateResult { + const lNodes = getChildElementsByTagName(this.element, 'LNode'); + + return lNodes.length + ? html`
+ ${lNodes.map( + lNode => html`` + )} +
` + : html``; + } + private renderSubFunctions(): TemplateResult { const subfunctions = getChildElementsByTagName(this.element, 'SubFunction'); return html` ${subfunctions.map( @@ -42,7 +55,17 @@ export class FunctionEditor extends LitElement { icon="functions" secondary highlighted - >${this.renderSubFunctions()}${this.renderLNodes()}${this.renderSubFunctions()}`; } + + static styles = css` + .container.lnode { + display: grid; + grid-gap: 12px; + padding: 8px 12px 16px; + box-sizing: border-box; + grid-template-columns: repeat(auto-fit, minmax(64px, auto)); + } + `; } diff --git a/src/editors/substation/l-node-editor.ts b/src/editors/substation/l-node-editor.ts new file mode 100644 index 0000000000..01dcd8b248 --- /dev/null +++ b/src/editors/substation/l-node-editor.ts @@ -0,0 +1,89 @@ +import { + html, + LitElement, + TemplateResult, + property, + customElement, + state, +} from 'lit-element'; + +import '../../action-icon.js'; +import { identity } from '../../foundation.js'; +import { + automationLogicalNode, + controlLogicalNode, + functionalLogicalNode, + furtherPowerSystemEquipmentLogicalNode, + generalLogicalNode, + interfacingLogicalNode, + measurementLogicalNode, + nonElectricalLogicalNode, + powerTransformerLogicalNode, + protectionLogicalNode, + protectionRelatedLogicalNode, + qualityLogicalNode, + supervisionLogicalNode, + switchgearLogicalNode, + systemLogicalNode, + transformerLogicalNode, +} from '../../icons/lnode.js'; + +export function getLNodeIcon(lNode: Element): TemplateResult { + const lnClassGroup = lNode.getAttribute('lnClass')?.charAt(0) ?? ''; + return lnClassIcons[lnClassGroup] ?? systemLogicalNode; +} + +const lnClassIcons: Partial> = { + L: systemLogicalNode, + A: automationLogicalNode, + C: controlLogicalNode, + F: functionalLogicalNode, + G: generalLogicalNode, + I: interfacingLogicalNode, + K: nonElectricalLogicalNode, + M: measurementLogicalNode, + P: protectionLogicalNode, + Q: qualityLogicalNode, + R: protectionRelatedLogicalNode, + S: supervisionLogicalNode, + T: transformerLogicalNode, + X: switchgearLogicalNode, + Y: powerTransformerLogicalNode, + Z: furtherPowerSystemEquipmentLogicalNode, +}; + +/** Pane rendering `LNode` element with its children */ +@customElement('l-node-editor') +export class LNodeEditor extends LitElement { + /** The edited `LNode` element */ + @property({ attribute: false }) + element!: Element; + @state() + private get header(): string { + const prefix = this.element.getAttribute('prefix') ?? ''; + const lnClass = this.element.getAttribute('lnClass'); + const lnInst = this.element.getAttribute('lnInst'); + + const header = this.missingIedReference + ? `${prefix} ${lnClass} ${lnInst}` + : identity(this.element); + + return typeof header === 'string' ? header : ''; + } + @state() + private get missingIedReference(): boolean { + return this.element.getAttribute('iedName') === 'None' ?? false; + } + + render(): TemplateResult { + return html`${getLNodeIcon(this.element)}`; + } +} diff --git a/src/editors/substation/powertransformer-editor.ts b/src/editors/substation/powertransformer-editor.ts index 6f6b97700a..81db57c037 100644 --- a/src/editors/substation/powertransformer-editor.ts +++ b/src/editors/substation/powertransformer-editor.ts @@ -21,7 +21,7 @@ import { newActionEvent, newWizardEvent, } from '../../foundation.js'; -import { startMove } from './foundation.js'; +import { startMove, styles } from './foundation.js'; import { SubstationEditor } from './substation-editor.js'; import { BayEditor } from './bay-editor.js'; import { VoltageLevelEditor } from './voltage-level-editor.js'; @@ -65,6 +65,18 @@ export class PowerTransformerEditor extends LitElement { ); } + private renderLNodes(): TemplateResult { + const lNodes = getChildElementsByTagName(this.element, 'LNode'); + + return lNodes.length + ? html`
+ ${lNodes.map( + lNode => html`` + )} +
` + : html``; + } + renderEqFunctions(): TemplateResult { if (!this.showfunctions) return html``; @@ -159,7 +171,7 @@ export class PowerTransformerEditor extends LitElement { render(): TemplateResult { if (this.showfunctions) return html`${this.renderContentPane()}${this.renderEqFunctions()}${this.renderContentPane()}${this.renderLNodes()}${this.renderEqFunctions()} `; return html` + ${lNodes.map( + lNode => html`` + )} + ` + : html``; + } + private renderSubFunctions(): TemplateResult { const subfunctions = getChildElementsByTagName(this.element, 'SubFunction'); return html` ${subfunctions.map( @@ -38,7 +50,17 @@ export class SubFunctionEditor extends LitElement { render(): TemplateResult { return html`${this.renderSubFunctions()}${this.renderLNodes()}${this.renderSubFunctions()}`; } + + static styles = css` + .container.lnode { + display: grid; + grid-gap: 12px; + padding: 8px 12px 16px; + box-sizing: border-box; + grid-template-columns: repeat(auto-fit, minmax(64px, auto)); + } + `; } diff --git a/src/editors/substation/substation-editor.ts b/src/editors/substation/substation-editor.ts index bc7036dbd6..854afb55c3 100644 --- a/src/editors/substation/substation-editor.ts +++ b/src/editors/substation/substation-editor.ts @@ -105,6 +105,20 @@ export class SubstationEditor extends LitElement { this.addMenu.anchor = this.addButton; } + private renderLNodes(): TemplateResult { + if (!this.showfunctions) return html``; + + const lNodes = getChildElementsByTagName(this.element, 'LNode'); + + return lNodes.length + ? html`
+ ${lNodes.map( + lNode => html`` + )} +
` + : html``; + } + renderFunctions(): TemplateResult { if (!this.showfunctions) return html``; @@ -207,7 +221,7 @@ export class SubstationEditor extends LitElement { >${this.renderAddButtons()} - ${this.renderIedContainer()}${this.renderFunctions()} + ${this.renderIedContainer()}${this.renderLNodes()}${this.renderFunctions()} ${this.renderPowerTransformerContainer()} ${Array.from(this.element.querySelectorAll(selectors.VoltageLevel)).map( voltageLevel => diff --git a/src/editors/substation/voltage-level-editor.ts b/src/editors/substation/voltage-level-editor.ts index 4907134cd2..805b7dd575 100644 --- a/src/editors/substation/voltage-level-editor.ts +++ b/src/editors/substation/voltage-level-editor.ts @@ -115,6 +115,20 @@ export class VoltageLevelEditor extends LitElement { this.addMenu.anchor = this.addButton; } + private renderLNodes(): TemplateResult { + if (!this.showfunctions) return html``; + + const lNodes = getChildElementsByTagName(this.element, 'LNode'); + + return lNodes.length + ? html`
+ ${lNodes.map( + lNode => html`` + )} +
` + : html``; + } + renderFunctions(): TemplateResult { if (!this.showfunctions) return html``; @@ -218,7 +232,7 @@ export class VoltageLevelEditor extends LitElement { >${this.renderAddButtons()} - ${this.renderIedContainer()}${this.renderFunctions()} + ${this.renderIedContainer()}${this.renderLNodes()}${this.renderFunctions()} ${this.renderPowerTransformerContainer()}
${Array.from(this.element?.querySelectorAll(selectors.Bay) ?? []).map( diff --git a/src/icons/lnode.ts b/src/icons/lnode.ts new file mode 100644 index 0000000000..2897f681c5 --- /dev/null +++ b/src/icons/lnode.ts @@ -0,0 +1,65 @@ +import { svg } from 'lit-element'; + +export const systemLogicalNode = svg` + +`; + +export const automationLogicalNode = svg` + +`; + +export const controlLogicalNode = svg` + +`; + +export const functionalLogicalNode = svg` + +`; + +export const generalLogicalNode = svg` + +`; + +export const interfacingLogicalNode = svg` + +`; + +export const nonElectricalLogicalNode = svg` + +`; + +export const measurementLogicalNode = svg` + +`; + +export const protectionLogicalNode = svg` + +`; + +export const qualityLogicalNode = svg` + +`; + +export const protectionRelatedLogicalNode = svg` + +`; + +export const supervisionLogicalNode = svg` + +`; + +export const transformerLogicalNode = svg` + +`; + +export const switchgearLogicalNode = svg` + +`; + +export const powerTransformerLogicalNode = svg` + +`; + +export const furtherPowerSystemEquipmentLogicalNode = svg` + +`; diff --git a/test/testfiles/zeroline/functions.scd b/test/testfiles/zeroline/functions.scd index 5e7054da2b..780f698e3e 100644 --- a/test/testfiles/zeroline/functions.scd +++ b/test/testfiles/zeroline/functions.scd @@ -7,10 +7,15 @@ + + + - + + + 110.0 @@ -27,35 +32,485 @@ - + + + + + - + + + - + - - - + + + - + - - + + + + + + - + - - + + + + - - - - + + + + - + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + + + sbo-with-enhanced-security + + + + + + + + status-only + + + + + + + sbo-with-enhanced-security + + + + + + + + + + status-only + + + + + + + + + status-only + + + + + + + + + direct-with-normal-security + + + + + + + sbo-with-normal-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + + + status-only + + + + + + + status-only + + + + + + + status-only + + + + + + + + + + + status-only + + + + + + + direct-with-enhanced-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sbo-with-enhanced-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + status-only + direct-with-normal-security + sbo-with-normal-security + direct-with-enhanced-security + sbo-with-enhanced-security + + + on + blocked + test + test/blocked + off + + + Ok + Warning + Alarm + + + not-supported + bay-control + station-control + remote-control + automatic-bay + automatic-station + automatic-remote + maintenance + process + + diff --git a/test/unit/editors/substation/__snapshots__/bay-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/bay-editor.test.snap.js index c9cf9629b7..658c44e587 100644 --- a/test/unit/editors/substation/__snapshots__/bay-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/bay-editor.test.snap.js @@ -311,3 +311,103 @@ snapshots["bay-editor with function filter deactivated looks like the latest sna `; /* end snapshot bay-editor with function filter deactivated looks like the latest snapshot */ +snapshots["bay-editor with existing LNode children looks like the latest snapshot"] = +` + + + + + + + + + + + + + + + + + + + + + + + + + + + LNode + + + + + PowerTransformer + + + + + ConductingEquipment + + + + +
+ + + + + + +
+
+
+
+`; +/* end snapshot bay-editor with existing LNode children looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/__snapshots__/conducting-equipment-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/conducting-equipment-editor.test.snap.js index 8e145f8b9f..15e3303353 100644 --- a/test/unit/editors/substation/__snapshots__/conducting-equipment-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/conducting-equipment-editor.test.snap.js @@ -154,3 +154,69 @@ snapshots["conducting-equipment-editor rendered as action pane with EqFunction c `; /* end snapshot conducting-equipment-editor rendered as action pane with EqFunction children looks like the latest snapshot */ +snapshots["conducting-equipment-editor rendered as action pane with LNode children looks like the latest snapshot"] = +` + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+
+`; +/* end snapshot conducting-equipment-editor rendered as action pane with LNode children looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/__snapshots__/eq-function-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/eq-function-editor.test.snap.js index da906462c9..4c5a188197 100644 --- a/test/unit/editors/substation/__snapshots__/eq-function-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/eq-function-editor.test.snap.js @@ -29,3 +29,23 @@ snapshots["web component rendering EqFunction element with missing desc and type `; /* end snapshot web component rendering EqFunction element with missing desc and type attribute looks like the latest snapshot */ +snapshots["web component rendering EqFunction element with existing LNode children looks like the latest snapshot"] = +` +
+ + + + + + +
+
+`; +/* end snapshot web component rendering EqFunction element with existing LNode children looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/__snapshots__/eq-sub-function-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/eq-sub-function-editor.test.snap.js index 40b9949528..f3df8882db 100644 --- a/test/unit/editors/substation/__snapshots__/eq-sub-function-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/eq-sub-function-editor.test.snap.js @@ -25,3 +25,22 @@ snapshots["web component rendering EqSubFunction element with missing desc and t `; /* end snapshot web component rendering EqSubFunction element with missing desc and type attribute looks like the latest snapshot */ +snapshots["web component rendering EqSubFunction element with existing LNode children looks like the latest snapshot"] = +` +
+ + + + + + +
+
+`; +/* end snapshot web component rendering EqSubFunction element with existing LNode children looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/__snapshots__/function-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/function-editor.test.snap.js index f1eea4690c..819e80c9b6 100644 --- a/test/unit/editors/substation/__snapshots__/function-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/function-editor.test.snap.js @@ -29,3 +29,23 @@ snapshots["web component rendering Function element with missing desc and type a `; /* end snapshot web component rendering Function element with missing desc and type attribute looks like the latest snapshot */ +snapshots["web component rendering Function element with existing LNode children looks like the latest snapshot"] = +` +
+ + + + + + +
+
+`; +/* end snapshot web component rendering Function element with existing LNode children looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/__snapshots__/l-node-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/l-node-editor.test.snap.js new file mode 100644 index 0000000000..3a0f0fc09c --- /dev/null +++ b/test/unit/editors/substation/__snapshots__/l-node-editor.test.snap.js @@ -0,0 +1,29 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["web component rendering LNode element as reference to a LN/LN0 within IED looks like the latest snapshot"] = +` + + + +`; +/* end snapshot web component rendering LNode element as reference to a LN/LN0 within IED looks like the latest snapshot */ + +snapshots["web component rendering LNode element as instance of a LNodeType only looks like the latest snapshot"] = +` + + + +`; +/* end snapshot web component rendering LNode element as instance of a LNodeType only looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/__snapshots__/powertransformer-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/powertransformer-editor.test.snap.js index bb06c2da07..1dd9a4775b 100644 --- a/test/unit/editors/substation/__snapshots__/powertransformer-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/powertransformer-editor.test.snap.js @@ -155,3 +155,65 @@ snapshots["powertransformer-editor rendered as action pane with EqFunction child `; /* end snapshot powertransformer-editor rendered as action pane with EqFunction childrend looks like the latest snapshot */ +snapshots["powertransformer-editor rendered as action pane with LNode childrend looks like the latest snapshot"] = +` + + + + + + + + + + + + + + + + + + +
+ + +
+
+`; +/* end snapshot powertransformer-editor rendered as action pane with LNode childrend looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/__snapshots__/sub-function-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/sub-function-editor.test.snap.js index 0dce3e0a8c..35f8d30ade 100644 --- a/test/unit/editors/substation/__snapshots__/sub-function-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/sub-function-editor.test.snap.js @@ -25,3 +25,22 @@ snapshots["web component rendering SubFunction element with missing desc and typ `; /* end snapshot web component rendering SubFunction element with missing desc and type attribute looks like the latest snapshot */ +snapshots["web component rendering SubFunction element with existing LNode children looks like the latest snapshot"] = +` +
+ + + + + + +
+
+`; +/* end snapshot web component rendering SubFunction element with existing LNode children looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/__snapshots__/substation-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/substation-editor.test.snap.js index 45962bfda5..d34ce2f4d2 100644 --- a/test/unit/editors/substation/__snapshots__/substation-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/substation-editor.test.snap.js @@ -275,6 +275,14 @@ snapshots["substation-editor with function filter deactivated looks like the lat +
+ + + + + + +
@@ -283,6 +291,8 @@ snapshots["substation-editor with function filter deactivated looks like the lat
+ + `; /* end snapshot substation-editor with function filter deactivated looks like the latest snapshot */ diff --git a/test/unit/editors/substation/__snapshots__/voltage-level-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/voltage-level-editor.test.snap.js index c87b96a48f..645f509712 100644 --- a/test/unit/editors/substation/__snapshots__/voltage-level-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/voltage-level-editor.test.snap.js @@ -291,8 +291,111 @@ snapshots["voltage-level-editor with function filter deactivated looks like the
+ +
`; /* end snapshot voltage-level-editor with function filter deactivated looks like the latest snapshot */ +snapshots["voltage-level-editor with function filter deactivated and existing LNode children looks like the latest snapshot"] = +` + + + + + + + + + + + + + + + + + + + + + + + + + + + LNode + + + + + PowerTransformer + + + + + Bay + + + + +
+ + + + + + +
+
+
+
+`; +/* end snapshot voltage-level-editor with function filter deactivated and existing LNode children looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/bay-editor.test.ts b/test/unit/editors/substation/bay-editor.test.ts index b7fe0e49d5..8dcd53997c 100644 --- a/test/unit/editors/substation/bay-editor.test.ts +++ b/test/unit/editors/substation/bay-editor.test.ts @@ -47,4 +47,20 @@ describe('bay-editor', () => { await expect(element).shadowDom.to.equalSnapshot(); }); }); + + describe('with existing LNode children', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/zeroline/functions.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element.element = doc.querySelector('Bay[name="Q01"]')!; + element.showfunctions = true; + await element.requestUpdate(); + }); + + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); }); diff --git a/test/unit/editors/substation/conducting-equipment-editor.test.ts b/test/unit/editors/substation/conducting-equipment-editor.test.ts index b0546d5e2a..7190b345d6 100644 --- a/test/unit/editors/substation/conducting-equipment-editor.test.ts +++ b/test/unit/editors/substation/conducting-equipment-editor.test.ts @@ -114,6 +114,22 @@ describe('conducting-equipment-editor', () => { await expect(element).shadowDom.to.equalSnapshot()); }); + describe('with LNode children', () => { + beforeEach(async () => { + const doc = await fetch('/test/testfiles/zeroline/functions.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element.element = doc.querySelector( + 'Bay[name="COUPLING_BAY"] > ConductingEquipment[name="QC9"]' + )!; + await element.requestUpdate(); + }); + + it('looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); + }); + it('renders empty string in case ConductingEquipment name attribute is missing', async () => { const condEq = validSCL.querySelector('ConductingEquipment'); condEq?.removeAttribute('name'); diff --git a/test/unit/editors/substation/eq-function-editor.test.ts b/test/unit/editors/substation/eq-function-editor.test.ts index 22b53d46b6..e7ab98e5fc 100644 --- a/test/unit/editors/substation/eq-function-editor.test.ts +++ b/test/unit/editors/substation/eq-function-editor.test.ts @@ -42,4 +42,21 @@ describe('web component rendering EqFunction element', () => { it('looks like the latest snapshot', async () => await expect(element).shadowDom.to.equalSnapshot()); }); + + describe('with existing LNode children', () => { + beforeEach(async () => { + element = ( + await fixture( + html`` + ) + ); + }); + + it('looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); + }); }); diff --git a/test/unit/editors/substation/eq-sub-function-editor.test.ts b/test/unit/editors/substation/eq-sub-function-editor.test.ts index 1f52d40589..dc6959833d 100644 --- a/test/unit/editors/substation/eq-sub-function-editor.test.ts +++ b/test/unit/editors/substation/eq-sub-function-editor.test.ts @@ -42,4 +42,21 @@ describe('web component rendering EqSubFunction element', () => { it('looks like the latest snapshot', async () => await expect(element).shadowDom.to.equalSnapshot()); }); + + describe('with existing LNode children', () => { + beforeEach(async () => { + element = ( + await fixture( + html`` + ) + ); + }); + + it('looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); + }); }); diff --git a/test/unit/editors/substation/function-editor.test.ts b/test/unit/editors/substation/function-editor.test.ts index 5d307545cb..fdbf0151e3 100644 --- a/test/unit/editors/substation/function-editor.test.ts +++ b/test/unit/editors/substation/function-editor.test.ts @@ -44,4 +44,22 @@ describe('web component rendering Function element', () => { await expect(element).shadowDom.to.equalSnapshot(); }); }); + + describe('with existing LNode children', () => { + beforeEach(async () => { + element = ( + await fixture( + html`` + ) + ); + }); + + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); }); diff --git a/test/unit/editors/substation/l-node-editor.test.ts b/test/unit/editors/substation/l-node-editor.test.ts new file mode 100644 index 0000000000..8951ebf29f --- /dev/null +++ b/test/unit/editors/substation/l-node-editor.test.ts @@ -0,0 +1,244 @@ +import { fixture, html, expect } from '@open-wc/testing'; + +import '../../../../src/editors/substation/l-node-editor.js'; +import { LNodeEditor } from '../../../../src/editors/substation/l-node-editor.js'; + +describe('web component rendering LNode element', () => { + let element: LNodeEditor; + let doc: XMLDocument; + + beforeEach(async () => { + doc = await fetch('/test/testfiles/zeroline/functions.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + }); + + describe('is an action-icon type component', () => { + let lNode: Element; + beforeEach(async () => { + lNode = new DOMParser().parseFromString( + '', + 'application/xml' + ).documentElement; + + element = ( + await fixture(html``) + ); + }); + + it('having a default icon for invalid lnClass groups', () => { + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H11V15H15V17H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Lxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Lxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H11V15H15V17H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Axxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Axxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M11,7H13A2,2 0 0,1 15,9V17H13V13H11V17H9V9A2,2 0 0,1 11,7M11,9V11H13V9H11M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z' + ); + }); + + it('with specific icon for Cxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Cxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M11,7H13A2,2 0 0,1 15,9V10H13V9H11V15H13V14H15V15A2,2 0 0,1 13,17H11A2,2 0 0,1 9,15V9A2,2 0 0,1 11,7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Fxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Fxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H15V9H11V11H14V13H11V17H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Gxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Gxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M11,7H15V9H11V15H13V11H15V15A2,2 0 0,1 13,17H11A2,2 0 0,1 9,15V9A2,2 0 0,1 11,7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Ixxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Ixxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M14,7V9H13V15H14V17H10V15H11V9H10V7H14M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Kxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Kxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H11V10.33L13,7H15L12,12L15,17H13L11,13.67V17H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Mxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Mxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H15A2,2 0 0,1 17,9V17H15V9H13V16H11V9H9V17H7V9A2,2 0 0,1 9,7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Pxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Pxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H13A2,2 0 0,1 15,9V11A2,2 0 0,1 13,13H11V17H9V7M11,9V11H13V9H11M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Qxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Qxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M11,7H13A2,2 0 0,1 15,9V15A2,2 0 0,1 13,17V19H11V17A2,2 0 0,1 9,15V9A2,2 0 0,1 11,7M11,9V15H13V9H11Z' + ); + }); + + it('with specific icon for Rxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Rxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H13A2,2 0 0,1 15,9V11C15,11.84 14.5,12.55 13.76,12.85L15,17H13L11.8,13H11V17H9V7M11,9V11H13V9H11M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12C4,16.41 7.58,20 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Sxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Sxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M11,7H15V9H11V11H13A2,2 0 0,1 15,13V15A2,2 0 0,1 13,17H9V15H13V13H11A2,2 0 0,1 9,11V9A2,2 0 0,1 11,7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Txxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Txxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H15V9H13V17H11V9H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Xxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Xxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H11L12,9.5L13,7H15L13,12L15,17H13L12,14.5L11,17H9L11,12L9,7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Yxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Yxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H11L12,10L13,7H15L13,13V17H11V13L9,7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + + it('with specific icon for Zxxx lnClass attribute', async () => { + lNode.setAttribute('lnClass', 'Zxxx'); + element.element = lNode; + await element.requestUpdate(); + + expect(element.shadowRoot?.querySelector('path')).to.have.attribute( + 'd', + 'M9,7H15V9L11,15H15V17H9V15L13,9H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' + ); + }); + }); + + describe('as reference to a LN/LN0 within IED ', () => { + beforeEach(async () => { + element = ( + await fixture( + html`` + ) + ); + }); + + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); + + describe('as instance of a LNodeType only', () => { + beforeEach(async () => { + element = ( + await fixture( + html`` + ) + ); + }); + + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); +}); diff --git a/test/unit/editors/substation/powertransformer-editor.test.ts b/test/unit/editors/substation/powertransformer-editor.test.ts index 75fa0f4751..d04181dd1e 100644 --- a/test/unit/editors/substation/powertransformer-editor.test.ts +++ b/test/unit/editors/substation/powertransformer-editor.test.ts @@ -105,6 +105,20 @@ describe('powertransformer-editor', () => { await expect(element).shadowDom.to.equalSnapshot()); }); + describe('with LNode childrend', () => { + beforeEach(async () => { + const doc = await fetch('/test/testfiles/zeroline/functions.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element.element = doc.querySelector('PowerTransformer[name="myPtr1"]')!; + await element.requestUpdate(); + }); + + it('looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); + }); + it('triggers edit wizard for Linking LNode element on action button click', async () => { (( element.shadowRoot?.querySelector( diff --git a/test/unit/editors/substation/sub-function-editor.test.ts b/test/unit/editors/substation/sub-function-editor.test.ts index d588950646..2594c7e5cd 100644 --- a/test/unit/editors/substation/sub-function-editor.test.ts +++ b/test/unit/editors/substation/sub-function-editor.test.ts @@ -44,4 +44,22 @@ describe('web component rendering SubFunction element', () => { await expect(element).shadowDom.to.equalSnapshot(); }); }); + + describe('with existing LNode children', () => { + beforeEach(async () => { + element = ( + await fixture( + html`` + ) + ); + }); + + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); }); diff --git a/test/unit/editors/substation/voltage-level-editor.test.ts b/test/unit/editors/substation/voltage-level-editor.test.ts index 3f3cd778b5..e348cbb6de 100644 --- a/test/unit/editors/substation/voltage-level-editor.test.ts +++ b/test/unit/editors/substation/voltage-level-editor.test.ts @@ -49,4 +49,20 @@ describe('voltage-level-editor', () => { await expect(element).shadowDom.to.equalSnapshot(); }); }); + + describe('with function filter deactivated and existing LNode children', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/zeroline/functions.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element.element = doc.querySelector('VoltageLevel[name="J1"]')!; + element.showfunctions = true; + await element.requestUpdate(); + }); + + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); });