diff --git a/__snapshots__/Substation Plugin.md b/__snapshots__/Substation Plugin.md index 51a045d98..9616e063f 100644 --- a/__snapshots__/Substation Plugin.md +++ b/__snapshots__/Substation Plugin.md @@ -5,10 +5,9 @@ #### `looks like the latest snapshot` ```html + +

- - [substation.missing] -
@@ -26,7 +26,7 @@
@@ -34,7 +34,7 @@ diff --git a/__snapshots__/bay-editor.md b/__snapshots__/bay-editor.md index 4a46b8745..77bcf3c35 100644 --- a/__snapshots__/bay-editor.md +++ b/__snapshots__/bay-editor.md @@ -33,17 +33,46 @@

-
- - - - - - - - - - +
+
+ + + + + + + + + + +
+
+ + +``` + +## `with readonly property` + +#### `looks like the latest snapshot` + +```html +
+

+ COUPLING_BAY — Bay +

+
+
+ + + + + + + + + + +
diff --git a/__snapshots__/conducting-equipment-editor wizarding integration.md b/__snapshots__/conducting-equipment-editor wizarding integration.md index ad7e1f76d..afa295bc5 100644 --- a/__snapshots__/conducting-equipment-editor wizarding integration.md +++ b/__snapshots__/conducting-equipment-editor wizarding integration.md @@ -5,15 +5,15 @@ ```html
@@ -42,7 +42,7 @@
@@ -50,7 +50,7 @@ diff --git a/__snapshots__/conducting-equipment-editor.md b/__snapshots__/conducting-equipment-editor.md index e959738dc..0b75dd8c7 100644 --- a/__snapshots__/conducting-equipment-editor.md +++ b/__snapshots__/conducting-equipment-editor.md @@ -38,3 +38,19 @@ ``` +## `with readonly property` + +#### `looks like the latest snapshot` + +```html +
+
+

+ QA1 +

+ +``` + diff --git a/__snapshots__/open-scd.md b/__snapshots__/open-scd.md index 9cd35d1a2..e8ad2c20d 100644 --- a/__snapshots__/open-scd.md +++ b/__snapshots__/open-scd.md @@ -982,6 +982,10 @@ + + + +
@@ -26,7 +26,7 @@
@@ -34,7 +34,7 @@ diff --git a/__snapshots__/substation-editor.md b/__snapshots__/substation-editor.md index 915b44c92..193e27afa 100644 --- a/__snapshots__/substation-editor.md +++ b/__snapshots__/substation-editor.md @@ -33,3 +33,20 @@ ``` +## `with readonly property` + +#### `looks like the latest snapshot` + +```html +
+

+ AA1 — Substation +

+ + + + +
+ +``` + diff --git a/__snapshots__/voltage-level-editor wizarding integration.md b/__snapshots__/voltage-level-editor wizarding integration.md index 9aea6ff40..1aea3e20b 100644 --- a/__snapshots__/voltage-level-editor wizarding integration.md +++ b/__snapshots__/voltage-level-editor wizarding integration.md @@ -5,36 +5,36 @@ ```html
@@ -66,7 +66,7 @@ diff --git a/__snapshots__/voltage-level-editor.md b/__snapshots__/voltage-level-editor.md index 4c6ea782b..991571476 100644 --- a/__snapshots__/voltage-level-editor.md +++ b/__snapshots__/voltage-level-editor.md @@ -44,3 +44,23 @@ ``` +## `with readonly property` + +#### `looks like the latest snapshot` + +```html +
+

+ E1 — Voltage Level + (110.0 kV) +

+
+ + + + +
+
+ +``` + diff --git a/__snapshots__/zeroline-pane.md b/__snapshots__/zeroline-pane.md new file mode 100644 index 000000000..8badf995d --- /dev/null +++ b/__snapshots__/zeroline-pane.md @@ -0,0 +1,86 @@ +# `zeroline-pane` + +#### `per default looks like the latest snapshot` + +```html +

+ + + + + +

+
+ + + + +
+ +``` + +#### `readonly looks like the latest snapshot` + +```html +

+ + + + + +

+
+ + + + +
+ +``` + +#### `showieds looks like the latest snapshot` + +```html +

+ + + + + +

+
+ + + + +
+ +``` + diff --git a/src/Setting.ts b/src/Setting.ts index 74c3de4fe..cb48ac5ca 100644 --- a/src/Setting.ts +++ b/src/Setting.ts @@ -13,11 +13,13 @@ export type Settings = { language: Language; theme: 'light' | 'dark'; mode: 'safe' | 'pro'; + showieds: 'on' | 'off'; }; export const defaults: Settings = { language: 'en', theme: 'light', mode: 'safe', + showieds: 'off', }; /** Mixin that saves [[`Settings`]] to `localStorage`, reflecting them in the @@ -33,6 +35,7 @@ export function Setting(Base: TBase) { language: this.getSetting('language'), theme: this.getSetting('theme'), mode: this.getSetting('mode'), + showieds: this.getSetting('showieds'), }; } @@ -44,6 +47,8 @@ export function Setting(Base: TBase) { darkThemeUI!: Switch; @query('#mode') modeUI!: Switch; + @query('#showieds') + showiedsUI!: Switch; private getSetting(setting: T): Settings[T] { return ( @@ -69,6 +74,7 @@ export function Setting(Base: TBase) { this.setSetting('language', this.languageUI.value); this.setSetting('theme', this.darkThemeUI.checked ? 'dark' : 'light'); this.setSetting('mode', this.modeUI.checked ? 'pro' : 'safe'); + this.setSetting('showieds', this.showiedsUI.checked ? 'on' : 'off'); this.requestUpdate('settings'); } } @@ -121,6 +127,12 @@ export function Setting(Base: TBase) { ?checked=${this.settings.mode === 'pro'} > + + + ${translate('cancel')} diff --git a/src/editors/Substation.ts b/src/editors/Substation.ts index 09d58916c..2d0d71b70 100644 --- a/src/editors/Substation.ts +++ b/src/editors/Substation.ts @@ -1,13 +1,10 @@ import { LitElement, html, TemplateResult, property, css } from 'lit-element'; -import { translate, get } from 'lit-translate'; +import { get } from 'lit-translate'; import { newWizardEvent } from '../foundation.js'; import { wizards } from '../wizards/wizard-library.js'; -import { selectors, styles } from './substation/foundation.js'; - -import './substation/substation-editor.js'; -import { SubstationEditor } from './substation/substation-editor.js'; +import '../zeroline-pane.js'; /** An editor [[`plugin`]] for editing the `Substation` section. */ export default class SubstationPlugin extends LitElement { @@ -22,29 +19,20 @@ export default class SubstationPlugin extends LitElement { } render(): TemplateResult { - if (!this.doc?.querySelector(selectors.Substation)) - return html`

- ${translate('substation.missing')} - this.openCreateSubstationWizard()} - > -

`; - return html` - ${Array.from(this.doc.querySelectorAll(selectors.Substation) ?? []).map( - substation => - html`` - )} - `; + return html` + ${!this.doc?.querySelector(':root > Substation') + ? html`

+ this.openCreateSubstationWizard()} + > +

` + : html``}`; } static styles = css` - ${styles} - mwc-fab { position: fixed; bottom: 32px; diff --git a/src/editors/substation/bay-editor.ts b/src/editors/substation/bay-editor.ts deleted file mode 100644 index fd61562b6..000000000 --- a/src/editors/substation/bay-editor.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { - css, - customElement, - html, - LitElement, - property, - TemplateResult, -} from 'lit-element'; -import { translate } from 'lit-translate'; - -import { newActionEvent, newWizardEvent } from '../../foundation.js'; - -import { startMove, styles, cloneElement } from './foundation.js'; -import './conducting-equipment-editor.js'; -import { VoltageLevelEditor } from './voltage-level-editor.js'; -import { wizards } from '../../wizards/wizard-library.js'; - -/** [[`SubstationEditor`]] subeditor for a `Bay` element. */ -@customElement('bay-editor') -export class BayEditor extends LitElement { - @property({ attribute: false }) - element!: Element; - - @property({ type: String }) - get name(): string { - return this.element.getAttribute('name') ?? ''; - } - @property({ type: String }) - get desc(): string | null { - return this.element.getAttribute('desc') ?? null; - } - - openEditWizard(): void { - const wizard = wizards['Bay'].edit(this.element); - if (wizard) this.dispatchEvent(newWizardEvent(wizard)); - } - - openConductingEquipmentWizard(): void { - const wizard = wizards['ConductingEquipment'].create(this.element); - if (wizard) this.dispatchEvent(newWizardEvent(wizard)); - } - - /** Opens a [[`WizardDialog`]] for editing `LNode` connections. */ - openLNodeWizard(): void { - const wizard = wizards['LNode'].edit(this.element); - if (wizard) this.dispatchEvent(newWizardEvent(wizard)); - } - - remove(): void { - if (this.element) - this.dispatchEvent( - newActionEvent({ - old: { - parent: this.element.parentElement!, - element: this.element, - reference: this.element.nextSibling, - }, - }) - ); - } - - renderHeader(): TemplateResult { - return html`

- ${this.name} ${this.desc === null ? '' : html`—`} ${this.desc} - - this.openConductingEquipmentWizard()} - > - - -

`; - } - - render(): TemplateResult { - return html`
- ${this.renderHeader()} -
- ${Array.from( - this.element?.querySelectorAll( - ':root > Substation > VoltageLevel > Bay > ConductingEquipment' - ) ?? [] - ).map( - voltageLevel => - html`` - )} -
-
`; - } - - static styles = css` - ${styles} - - section { - margin: 0px; - } - - #ceContainer { - display: grid; - grid-gap: 12px; - padding: 12px; - box-sizing: border-box; - grid-template-columns: repeat(auto-fit, minmax(64px, auto)); - } - `; -} diff --git a/src/editors/substation/conducting-equipment-types.ts b/src/editors/substation/conducting-equipment-types.ts deleted file mode 100644 index 0a3c3670a..000000000 --- a/src/editors/substation/conducting-equipment-types.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TemplateResult } from 'lit-html'; - -import { - disconnectorIcon, - circuitBreakerIcon, - currentTransformerIcon, - earthSwitchIcon, - generalConductingEquipmentIcon, - voltageTransformerIcon, -} from '../../icons.js'; - -function typeStr(condEq: Element): string { - return condEq.getAttribute('type') === 'DIS' && - condEq.querySelector('Terminal')?.getAttribute('cNodeName') === 'grounded' - ? 'ERS' - : condEq.getAttribute('type') ?? ''; -} - -const typeIcons: Partial> = { - CBR: circuitBreakerIcon, - DIS: disconnectorIcon, - CTR: currentTransformerIcon, - VTR: voltageTransformerIcon, - ERS: earthSwitchIcon, -}; - -export function typeIcon(condEq: Element): TemplateResult { - return typeIcons[typeStr(condEq)] ?? generalConductingEquipmentIcon; -} diff --git a/src/translations/de.ts b/src/translations/de.ts index 399a93f60..a33e9bf94 100644 --- a/src/translations/de.ts +++ b/src/translations/de.ts @@ -33,6 +33,7 @@ export const de: Translations = { languages: { de: 'Deutsch', en: 'Englisch (English)' }, dark: 'Dunkles Design', mode: 'Profimodus', + showieds: 'Zeige IEDs im Substation-Editor', }, menu: { title: 'Menü', @@ -44,6 +45,10 @@ export const de: Translations = { readError: '{{ name }} Lesefehler', readAbort: '{{ name }} Leseabbruch', }, + zeroline: { + iedsloading: 'IEDs werden geladen...', + showieds: 'IEDs anzeigen/ausblenden', + }, editing: { created: '{{ name }} hinzugefügt', deleted: '{{ name }} entfernt', diff --git a/src/translations/en.ts b/src/translations/en.ts index 4505dcd8a..ce03e3367 100644 --- a/src/translations/en.ts +++ b/src/translations/en.ts @@ -31,6 +31,7 @@ export const en = { languages: { de: 'German (Deutsch)', en: 'English' }, dark: 'Dark theme', mode: 'Pro mode', + showieds: 'Show IEDs in substation editor', }, menu: { title: 'Menu', @@ -42,6 +43,10 @@ export const en = { readError: 'Error reading {{ name }}', readAbort: 'Aborted reading {{ name }}', }, + zeroline: { + iedsloading: 'Loading IEDs...', + showieds: 'Display/hide IEDs', + }, editing: { created: 'Added {{ name }}', deleted: 'Removed {{ name }}', diff --git a/src/wizards/bay.ts b/src/wizards/bay.ts index 94a0628c9..19cbe8a2c 100644 --- a/src/wizards/bay.ts +++ b/src/wizards/bay.ts @@ -1,6 +1,5 @@ import { html, TemplateResult } from 'lit-html'; import { get, translate } from 'lit-translate'; -import { updateNamingAction } from '../editors/substation/foundation.js'; import { createElement, @@ -12,6 +11,8 @@ import { WizardInput, } from '../foundation.js'; +import { updateNamingAction } from '../zeroline/foundation.js'; + function render(name: string | null, desc: string | null): TemplateResult[] { return [ html` { const name = getValue(inputs.find(i => i.label === 'name')!); const desc = getValue(inputs.find(i => i.label === 'desc')!); diff --git a/src/wizards/substation.ts b/src/wizards/substation.ts index 69108616d..a5055dcce 100644 --- a/src/wizards/substation.ts +++ b/src/wizards/substation.ts @@ -40,7 +40,7 @@ function render( ]; } -function createAction(parent: Element): WizardActor { +export function createAction(parent: Element): WizardActor { return (inputs: WizardInput[], wizard: Element): EditorAction[] => { const name = getValue(inputs.find(i => i.label === 'name')!); const desc = getValue(inputs.find(i => i.label === 'desc')!); @@ -69,6 +69,8 @@ function createAction(parent: Element): WizardActor { } export function substationCreateWizard(parent: Element): Wizard { + const guessable = parent.querySelector('Substation') === null; + return [ { title: get('substation.wizard.title.add'), @@ -78,7 +80,7 @@ export function substationCreateWizard(parent: Element): Wizard { label: get('add'), action: createAction(parent), }, - content: render('', '', true), + content: render('', '', guessable), }, ]; } diff --git a/src/zeroline-pane.ts b/src/zeroline-pane.ts new file mode 100644 index 000000000..a2be0e3a0 --- /dev/null +++ b/src/zeroline-pane.ts @@ -0,0 +1,116 @@ +import { + LitElement, + html, + TemplateResult, + property, + customElement, + css, +} from 'lit-element'; +import { until } from 'lit-html/directives/until'; +import { translate } from 'lit-translate'; + +import { isPublic, newWizardEvent } from './foundation.js'; +import { getAttachedIeds, styles } from './zeroline/foundation.js'; + +import './zeroline/substation-editor.js'; +import './zeroline/ied-editor.js'; +import { Settings } from './Setting.js'; +import { wizards } from './wizards/wizard-library.js'; + +function shouldShowIEDs(): boolean { + return localStorage.getItem('showieds') === 'on'; +} + +function setShowIEDs(value: Settings['showieds']) { + localStorage.setItem('showieds', value); +} + +/** [[`Zeroline`]] pane for displaying `Substation` and/or `IED` sections. */ +@customElement('zeroline-pane') +export class ZerolinePane extends LitElement { + /** The document being edited as provided to editor by [[`Zeroline`]]. */ + @property({ attribute: false }) + doc!: XMLDocument; + @property({ type: Boolean }) + readonly = false; + + @property({ attribute: false }) + getAttachedIeds?: (element: Element) => Promise = async () => []; + + /** Opens a [[`WizardDialog`]] for creating a new `Substation` element. */ + openCreateSubstationWizard(): void { + const wizard = wizards['Substation'].create(this.doc.documentElement); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + + toggleShowIEDs(): void { + console.warn(shouldShowIEDs()); + if (shouldShowIEDs()) setShowIEDs('off'); + else setShowIEDs('on'); + console.log(shouldShowIEDs()); + this.requestUpdate(); + } + + async renderIedContainer(): Promise { + this.getAttachedIeds = shouldShowIEDs() + ? getAttachedIeds(this.doc) + : async () => []; + const ieds = await this.getAttachedIeds?.(this.doc.documentElement); + + await new Promise(requestAnimationFrame); + return ieds.length + ? html`
+ ${ieds.map(ied => html``)} +
` + : html``; + } + + render(): TemplateResult { + return html` +

+ ${html` + this.openCreateSubstationWizard()} + > + `} + + +

+ ${until(this.renderIedContainer(), html`loading ieds...`)} + ${ + this.doc?.querySelector(':root > Substation') + ? html`
+ ${Array.from(this.doc.querySelectorAll('Substation') ?? []) + .filter(isPublic) + .map( + substation => + html`` + )} +
` + : html`

+ ${translate('substation.missing')} +

` + }`; + } + + static styles = css` + ${styles} + `; +} diff --git a/src/zeroline/bay-editor.ts b/src/zeroline/bay-editor.ts new file mode 100644 index 000000000..01b358662 --- /dev/null +++ b/src/zeroline/bay-editor.ts @@ -0,0 +1,166 @@ +import { + css, + customElement, + html, + LitElement, + property, + TemplateResult, +} from 'lit-element'; +import { until } from 'lit-html/directives/until'; +import { translate } from 'lit-translate'; + +import { startMove, styles, cloneElement } from './foundation.js'; +import { newActionEvent, newWizardEvent } from '../foundation.js'; + +import { wizards } from '../wizards/wizard-library.js'; + +import { VoltageLevelEditor } from './voltage-level-editor.js'; +import './conducting-equipment-editor.js'; + +/** [[`SubstationEditor`]] subeditor for a `Bay` element. */ +@customElement('bay-editor') +export class BayEditor extends LitElement { + @property({ attribute: false }) + element!: Element; + @property({ type: Boolean }) + readonly = false; + + @property({ type: String }) + get name(): string { + return this.element.getAttribute('name') ?? ''; + } + @property({ type: String }) + get desc(): string | null { + return this.element.getAttribute('desc') ?? null; + } + + @property({ attribute: false }) + getAttachedIeds?: (element: Element) => Promise = async () => { + return []; + }; + + openEditWizard(): void { + const wizard = wizards['Bay'].edit(this.element); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + + openConductingEquipmentWizard(): void { + const wizard = wizards['ConductingEquipment'].create(this.element); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + + /** Opens a [[`WizardDialog`]] for editing `LNode` connections. */ + openLNodeWizard(): void { + const wizard = wizards['LNode'].edit(this.element); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + + remove(): void { + if (this.element) + this.dispatchEvent( + newActionEvent({ + old: { + parent: this.element.parentElement!, + element: this.element, + reference: this.element.nextSibling, + }, + }) + ); + } + + async renderIedContainer(): Promise { + const ieds = await this.getAttachedIeds?.(this.element); + return ieds?.length + ? html`
+ ${ieds.map(ied => html``)} +
` + : html``; + } + + renderHeader(): TemplateResult { + return html`

+ ${this.name} ${this.desc === null ? '' : html`—`} ${this.desc} + ${this.readonly + ? html`` + : html` + this.openConductingEquipmentWizard()} + > + + `} +

`; + } + + render(): TemplateResult { + return html`
+ ${this.renderHeader()} +
+ ${until( + this.renderIedContainer(), + html`${translate('zeroline.iedsloading')}` + )} +
+ ${Array.from( + this.element?.querySelectorAll( + ':root > Substation > VoltageLevel > Bay > ConductingEquipment' + ) ?? [] + ).map( + voltageLevel => + html`` + )} +
+
+
`; + } + + static styles = css` + ${styles} + + section { + margin: 0px; + } + + #ceContainer { + display: grid; + grid-gap: 12px; + padding: 12px; + box-sizing: border-box; + grid-template-columns: repeat(auto-fit, minmax(64px, auto)); + } + `; +} diff --git a/src/editors/substation/conducting-equipment-editor.ts b/src/zeroline/conducting-equipment-editor.ts similarity index 67% rename from src/editors/substation/conducting-equipment-editor.ts rename to src/zeroline/conducting-equipment-editor.ts index 7a218e52a..2f51d3100 100644 --- a/src/editors/substation/conducting-equipment-editor.ts +++ b/src/zeroline/conducting-equipment-editor.ts @@ -7,14 +7,39 @@ import { TemplateResult, } from 'lit-element'; -import { newActionEvent, newWizardEvent } from '../../foundation.js'; - import { startMove } from './foundation.js'; +import { newActionEvent, newWizardEvent } from '../foundation.js'; + +import { + circuitBreakerIcon, + currentTransformerIcon, + disconnectorIcon, + earthSwitchIcon, + generalConductingEquipmentIcon, + voltageTransformerIcon, +} from '../icons.js'; + import { BayEditor } from './bay-editor.js'; +import { wizards } from '../wizards/wizard-library.js'; + +function typeStr(condEq: Element): string { + return condEq.getAttribute('type') === 'DIS' && + condEq.querySelector('Terminal')?.getAttribute('cNodeName') === 'grounded' + ? 'ERS' + : condEq.getAttribute('type') ?? ''; +} -import { typeIcon } from './conducting-equipment-types.js'; +const typeIcons: Partial> = { + CBR: circuitBreakerIcon, + DIS: disconnectorIcon, + CTR: currentTransformerIcon, + VTR: voltageTransformerIcon, + ERS: earthSwitchIcon, +}; -import { wizards } from '../../wizards/wizard-library.js'; +export function typeIcon(condEq: Element): TemplateResult { + return typeIcons[typeStr(condEq)] ?? generalConductingEquipmentIcon; +} /** [[`SubstationEditor`]] subeditor for a `ConductingEquipment` element. */ @customElement('conducting-equipment-editor') @@ -22,14 +47,13 @@ export class ConductingEquipmentEditor extends LitElement { @property({ type: Element }) element!: Element; + @property({ type: Boolean }) + readonly = false; + @property({ type: String }) get name(): string { return this.element.getAttribute('name') ?? ''; } - @property({ type: String }) - get desc(): string { - return this.element.getAttribute('desc') ?? ''; - } openEditWizard(): void { const wizard = wizards['ConductingEquipment'].edit(this.element); @@ -59,31 +83,33 @@ export class ConductingEquipmentEditor extends LitElement { return html`
${typeIcon(this.element)} - - - - + ${this.readonly + ? html`` + : html` + + + `}

${this.name}

`; diff --git a/src/editors/substation/foundation.ts b/src/zeroline/foundation.ts similarity index 69% rename from src/editors/substation/foundation.ts rename to src/zeroline/foundation.ts index bcc697e06..39918bc1e 100644 --- a/src/editors/substation/foundation.ts +++ b/src/zeroline/foundation.ts @@ -6,28 +6,105 @@ import { newActionEvent, WizardActor, WizardInput, -} from '../../foundation.js'; -import { VoltageLevelEditor } from './voltage-level-editor.js'; + isPublic, +} from '../foundation.js'; + import { BayEditor } from './bay-editor.js'; +import { VoltageLevelEditor } from './voltage-level-editor.js'; -export type ElementEditor = Element & { - element: Element; -}; +function containsReference(element: Element, iedName: string): boolean { + return Array.from(element.getElementsByTagName('LNode')) + .filter(isPublic) + .some(lnode => lnode.getAttribute('iedName') === iedName); +} -interface UpdateOptions { - element: Element; +function isReferencedItself(element: Element, iedName: string): boolean { + return (Array.from(element.children)).some( + child => + child.tagName === 'LNode' && child.getAttribute('iedName') === iedName + ); +} + +function hasReferencedChildren(element: Element, iedName: string): boolean { + const threshold = element.tagName === 'Bay' ? 0 : 1; + return ( + (Array.from(element.children)).filter(child => + containsReference(child, iedName) + ).length > threshold + ); +} + +function hasOurs(element: Element, iedName: string): boolean { + return Array.from(element.getElementsByTagName('LNode')) + .filter(isPublic) + .some(lnode => lnode.getAttribute('iedName') === iedName); +} + +function getOurs(element: Element, iedName: string): Element[] { + return Array.from(element.getElementsByTagName('LNode')) + .filter(isPublic) + .filter(lnode => lnode.getAttribute('iedName') === iedName); +} + +function hasTheirs(element: Element, iedName: string): boolean { + const ours = getOurs(element, iedName); + const scl = element.closest('SCL')!; + + return Array.from(scl.getElementsByTagName('LNode')) + .filter(isPublic) + .filter(lnode => lnode.getAttribute('iedName') === iedName) + .some(lnode => !ours.includes(lnode)); } -interface CreateOptions { - parent: Element; + +export async function attachedIeds( + element: Element, + remainingIeds: Set +): Promise { + await new Promise(requestAnimationFrame); + + const attachedIeds: Element[] = []; + for (const ied of remainingIeds) { + const iedName = ied.getAttribute('name')!; + + if (element.tagName === 'SCL') { + if (!hasOurs(element, iedName) || hasReferencedChildren(element, iedName)) + attachedIeds.push(ied); + + continue; + } + + if (hasTheirs(element, iedName)) continue; + if ( + hasReferencedChildren(element, iedName) || + isReferencedItself(element, iedName) + ) + attachedIeds.push(ied); + } + + for (const ied of attachedIeds) { + remainingIeds.delete(ied); + } + + return attachedIeds; } -export type WizardOptions = UpdateOptions | CreateOptions; -export function isCreateOptions( - options: WizardOptions -): options is CreateOptions { - return (options).parent !== undefined; +export function getAttachedIeds( + doc: XMLDocument +): (element: Element) => Promise { + return async (element: Element) => { + const ieds = new Set( + Array.from(doc.querySelectorAll('IED')).filter(isPublic) + ); + await new Promise(requestAnimationFrame); + + return await attachedIeds(element, ieds); + }; } +export type ElementEditor = Element & { + element: Element; +}; + export function updateNamingAction(element: Element): WizardActor { return (inputs: WizardInput[]): EditorAction[] => { const name = getValue(inputs.find(i => i.label === 'name')!)!; @@ -241,4 +318,12 @@ export const styles = css` text-decoration: none; border-bottom: none; } + + #iedcontainer { + 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/zeroline/ied-editor.ts b/src/zeroline/ied-editor.ts new file mode 100644 index 000000000..cb51cf791 --- /dev/null +++ b/src/zeroline/ied-editor.ts @@ -0,0 +1,135 @@ +import { + css, + customElement, + html, + LitElement, + property, + TemplateResult, +} from 'lit-element'; +import { newActionEvent } from '../foundation.js'; + +/** [[`SubstationEditor`]] subeditor for a `ConductingEquipment` element. */ +@customElement('ied-editor') +export class IedEditor extends LitElement { + @property({ type: Element }) + element!: Element; + + @property({ type: String }) + get name(): string { + return this.element.getAttribute('name') ?? ''; + } + + render(): TemplateResult { + return html` +
+ developer_board +
+

${this.name}

+ `; + } + + static styles = css` + #container { + color: var(--mdc-theme-on-surface); + width: 50px; + height: 50px; + margin: auto; + position: relative; + transition: all 200ms linear; + } + + #container:focus { + outline: none; + } + + .icon { + color: var(--mdc-theme-on-surface); + --mdc-icon-size: 50px; + transition: transform 150ms linear, box-shadow 200ms linear; + outline-color: var(--mdc-theme-primary); + outline-style: solid; + outline-width: 0px; + } + + #container > .icon { + color: var(--mdc-theme-on-surface); + width: 50px; + height: 50px; + transition: transform 150ms linear, box-shadow 200ms linear; + outline-color: var(--mdc-theme-primary); + outline-style: solid; + outline-width: 0px; + } + + #container:focus > .icon { + 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); + } + + #container:hover > .icom { + outline: 2px dashed var(--mdc-theme-primary); + transition: transform 200ms linear, box-shadow 250ms linear; + } + + #container:focus-within > .icon { + outline: 2px solid var(--mdc-theme-primary); + background: var(--mdc-theme-on-primary); + transform: scale(0.8); + transition: transform 200ms linear, box-shadow 250ms linear; + } + + .menu-item { + color: var(--mdc-theme-on-surface); + transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1), + opacity 200ms linear; + position: absolute; + top: 8px; + left: 8px; + pointer-events: none; + z-index: 1; + opacity: 0; + } + + #container:focus-within > .menu-item { + transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), + opacity 250ms linear; + pointer-events: auto; + opacity: 1; + } + + #container:focus-within > .menu-item.up { + transform: translate(0px, -52px); + } + + #container:focus-within > .menu-item.down { + transform: translate(0px, 52px); + } + + #container:focus-within > .menu-item.right { + transform: translate(52px, 0px); + } + + #container:focus-within > .menu-item.left { + transform: translate(-52px, 0px); + } + + h4 { + color: var(--mdc-theme-on-surface); + font-family: 'Roboto', sans-serif; + font-weight: 300; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + margin: 0px; + opacity: 1; + transition: opacity 200ms linear; + text-align: center; + direction: rtl; + } + + :host(.moving) #container, + :host(.moving) h4 { + opacity: 0.3; + } + `; +} diff --git a/src/editors/substation/substation-editor.ts b/src/zeroline/substation-editor.ts similarity index 51% rename from src/editors/substation/substation-editor.ts rename to src/zeroline/substation-editor.ts index 012347a7c..eb8ffbe62 100644 --- a/src/editors/substation/substation-editor.ts +++ b/src/zeroline/substation-editor.ts @@ -6,20 +6,23 @@ import { property, TemplateResult, } from 'lit-element'; +import { until } from 'lit-html/directives/until'; import { translate } from 'lit-translate'; - -import { newActionEvent, newWizardEvent } from '../../foundation.js'; +import { newActionEvent, newWizardEvent } from '../foundation.js'; +import { wizards } from '../wizards/wizard-library.js'; import { selectors, styles } from './foundation.js'; import './voltage-level-editor.js'; -import { wizards } from '../../wizards/wizard-library.js'; + /** [[`Substation`]] plugin subeditor for editing `Substation` sections. */ @customElement('substation-editor') export class SubstationEditor extends LitElement { /** The edited `Element`, a common property of all Substation subeditors. */ @property({ attribute: false }) element!: Element; + @property({ type: Boolean }) + readonly = false; /** [[element | `element.name`]] */ @property({ type: String }) @@ -32,6 +35,11 @@ export class SubstationEditor extends LitElement { return this.element.getAttribute('desc'); } + @property({ attribute: false }) + getAttachedIeds?: (element: Element) => Promise = async () => { + return []; + }; + /** Opens a [[`WizardDialog`]] for editing [[`element`]]. */ openEditWizard(): void { const wizard = wizards['Substation'].edit(this.element); @@ -63,48 +71,68 @@ export class SubstationEditor extends LitElement { ); } + async renderIedContainer(): Promise { + const ieds = await this.getAttachedIeds?.(this.element); + return ieds?.length + ? html`
+ ${ieds.map(ied => html``)} +
` + : html``; + } + renderHeader(): TemplateResult { return html` -

- ${this.name} ${this.desc === null ? '' : html`—`} ${this.desc} - - this.openVoltageLevelWizard()} - > - - -

- `; +

+ ${this.name} ${this.desc === null ? '' : html`—`} ${this.desc} + ${ + this.readonly + ? html`` + : html` + this.openVoltageLevelWizard()} + > + + ` + } + +

+ `; } render(): TemplateResult { return html`
${this.renderHeader()} + ${until( + this.renderIedContainer(), + html`${translate('zeroline.iedsloading')}` + )} ${Array.from(this.element.querySelectorAll(selectors.VoltageLevel)).map( voltageLevel => html`` )}
diff --git a/src/editors/substation/voltage-level-editor.ts b/src/zeroline/voltage-level-editor.ts similarity index 52% rename from src/editors/substation/voltage-level-editor.ts rename to src/zeroline/voltage-level-editor.ts index c67f106d7..a7b7a6637 100644 --- a/src/editors/substation/voltage-level-editor.ts +++ b/src/zeroline/voltage-level-editor.ts @@ -8,18 +8,20 @@ import { } from 'lit-element'; import { translate } from 'lit-translate'; -import { newActionEvent, newWizardEvent } from '../../foundation.js'; - import { selectors, startMove, styles, cloneElement } from './foundation.js'; import './bay-editor.js'; import { SubstationEditor } from './substation-editor.js'; -import { wizards } from '../../wizards/wizard-library.js'; +import { wizards } from '../wizards/wizard-library.js'; +import { newActionEvent, newWizardEvent } from '../foundation.js'; +import { until } from 'lit-html/directives/until'; /** [[`Substation`]] subeditor for a `VoltageLevel` element. */ @customElement('voltage-level-editor') export class VoltageLevelEditor extends LitElement { @property() element!: Element; + @property({ type: Boolean }) + readonly = false; @property() get name(): string { @@ -39,6 +41,11 @@ export class VoltageLevelEditor extends LitElement { return v ? v + u : null; } + @property({ attribute: false }) + getAttachedIeds?: (element: Element) => Promise = async () => { + return []; + }; + openEditWizard(): void { const wizard = wizards['VoltageLevel'].edit(this.element); if (wizard) this.dispatchEvent(newWizardEvent(wizard)); @@ -68,58 +75,77 @@ export class VoltageLevelEditor extends LitElement { ); } + async renderIedContainer(): Promise { + const ieds = await this.getAttachedIeds?.(this.element); + return ieds?.length + ? html`
+ ${ieds.map(ied => html``)} +
` + : html``; + } + renderHeader(): TemplateResult { return html`

${this.name} ${this.desc === null ? '' : html`—`} ${this.desc} ${this.voltage === null ? '' : html`(${this.voltage})`} - - this.openBayWizard()} - > - - + ${this.readonly + ? html`` + : html` + this.openBayWizard()} + > + + `}

`; } render(): TemplateResult { return html`
${this.renderHeader()} + ${until( + this.renderIedContainer(), + html`${translate('zeroline.iedsloading')}` + )}
${Array.from(this.element?.querySelectorAll(selectors.Bay) ?? []).map( - bay => html`` + bay => html`` )}
`; diff --git a/test/integration/editors/substation/Substation.test.ts b/test/integration/editors/Substation.test.ts similarity index 84% rename from test/integration/editors/substation/Substation.test.ts rename to test/integration/editors/Substation.test.ts index f969c4e1f..0324a7a9b 100644 --- a/test/integration/editors/substation/Substation.test.ts +++ b/test/integration/editors/Substation.test.ts @@ -1,9 +1,9 @@ import { html, fixture, expect } from '@open-wc/testing'; -import '../../../mock-wizard.js'; -import { Editing } from '../../../../src/Editing.js'; -import Substation from '../../../../src/editors/Substation.js'; -import { Wizarding, WizardingElement } from '../../../../src/Wizarding.js'; +import '../../mock-wizard.js'; +import { Editing } from '../../../src/Editing.js'; +import Substation from '../../../src/editors/Substation.js'; +import { Wizarding, WizardingElement } from '../../../src/Wizarding.js'; describe('Substation Plugin', () => { customElements.define('substation-plugin', Wizarding(Editing(Substation))); @@ -29,8 +29,8 @@ describe('Substation Plugin', () => { html`` ); }); - it('constains a substation-editor rendering the substation section', () => { - expect(element.shadowRoot?.querySelector('substation-editor')).to.exist; + it('constains a zeroline-pane rendering the substation sections', () => { + expect(element.shadowRoot?.querySelector('zeroline-pane')).to.exist; }); }); diff --git a/test/integration/editors/substation/guess-wizarding-editing.test.ts b/test/integration/editors/substation/guess-wizarding-editing.test.ts index 201a1f1f7..b893c0e88 100644 --- a/test/integration/editors/substation/guess-wizarding-editing.test.ts +++ b/test/integration/editors/substation/guess-wizarding-editing.test.ts @@ -1,13 +1,14 @@ import { expect, fixture, html } from '@open-wc/testing'; import '../../../mock-wizard-editor.js'; +import '../../../mock-wizard.js'; import '@material/mwc-list/mwc-check-list-item'; import '@material/mwc-list/mwc-list'; -import '../../../mock-wizard.js'; + +import { EditingElement } from '../../../../src/Editing.js'; import { MockWizard } from '../../../mock-wizard.js'; import { guessVoltageLevel } from '../../../../src/editors/substation/guess-wizard.js'; -import { EditingElement } from '../../../../src/Editing.js'; import { WizardingElement } from '../../../../src/Wizarding.js'; describe('guess-wizard-integration', () => { diff --git a/test/integration/editors/substation/bay-editor-wizarding-editing.test.ts b/test/integration/zeroline/bay-editor-wizarding-editing.test.ts similarity index 97% rename from test/integration/editors/substation/bay-editor-wizarding-editing.test.ts rename to test/integration/zeroline/bay-editor-wizarding-editing.test.ts index 312b81ca9..96b84d16c 100644 --- a/test/integration/editors/substation/bay-editor-wizarding-editing.test.ts +++ b/test/integration/zeroline/bay-editor-wizarding-editing.test.ts @@ -1,12 +1,12 @@ import { fixture, html, expect } from '@open-wc/testing'; -import '../../../mock-wizard-editor.js'; -import { BayEditor } from '../../../../src/editors/substation/bay-editor.js'; -import { EditingElement } from '../../../../src/Editing.js'; -import { WizardingElement } from '../../../../src/Wizarding.js'; +import '../../mock-wizard-editor.js'; -import { WizardTextField } from '../../../../src/wizard-textfield.js'; +import { BayEditor } from '../../../src/zeroline/bay-editor.js'; +import { EditingElement } from '../../../src/Editing.js'; import { Select } from '@material/mwc-select'; +import { WizardingElement } from '../../../src/Wizarding.js'; +import { WizardTextField } from '../../../src/wizard-textfield.js'; describe('bay-editor wizarding editing integration', () => { describe('edit wizard', () => { diff --git a/test/integration/editors/substation/bay-editor-wizarding.test.ts b/test/integration/zeroline/bay-editor-wizarding.test.ts similarity index 92% rename from test/integration/editors/substation/bay-editor-wizarding.test.ts rename to test/integration/zeroline/bay-editor-wizarding.test.ts index ddf3c8d64..cd8279f10 100644 --- a/test/integration/editors/substation/bay-editor-wizarding.test.ts +++ b/test/integration/zeroline/bay-editor-wizarding.test.ts @@ -1,10 +1,10 @@ import { fixture, html, expect } from '@open-wc/testing'; import fc from 'fast-check'; -import '../../../mock-wizard.js'; -import { WizardingElement } from '../../../../src/Wizarding.js'; +import { regExp, regexString } from '../../foundation.js'; +import '../../mock-wizard.js'; -import { regexString, regExp } from '../../../foundation.js'; +import { WizardingElement } from '../../../src/Wizarding.js'; describe('bay-editor wizarding integration', () => { let doc: XMLDocument; diff --git a/test/integration/editors/substation/conducting-equipment-editor-wizarding-editing.test.ts b/test/integration/zeroline/conducting-equipment-editor-wizarding-editing.test.ts similarity index 95% rename from test/integration/editors/substation/conducting-equipment-editor-wizarding-editing.test.ts rename to test/integration/zeroline/conducting-equipment-editor-wizarding-editing.test.ts index cc776f3c6..930181cfa 100644 --- a/test/integration/editors/substation/conducting-equipment-editor-wizarding-editing.test.ts +++ b/test/integration/zeroline/conducting-equipment-editor-wizarding-editing.test.ts @@ -1,10 +1,10 @@ import { fixture, html, expect } from '@open-wc/testing'; -import '../../../mock-wizard-editor.js'; -import { ConductingEquipmentEditor } from '../../../../src/editors/substation/conducting-equipment-editor.js'; -import { EditingElement } from '../../../../src/Editing.js'; -import { WizardingElement } from '../../../../src/Wizarding.js'; -import { WizardTextField } from '../../../../src/wizard-textfield.js'; +import '../../mock-wizard-editor.js'; +import { EditingElement } from '../../../src/Editing.js'; +import { WizardingElement } from '../../../src/Wizarding.js'; +import { WizardTextField } from '../../../src/wizard-textfield.js'; +import { ConductingEquipmentEditor } from '../../../src/zeroline/conducting-equipment-editor.js'; describe('conducting-equipment-editor wizarding editing integration', () => { describe('edit wizard', () => { diff --git a/test/integration/editors/substation/conducting-equipment-editor-wizarding.test.ts b/test/integration/zeroline/conducting-equipment-editor-wizarding.test.ts similarity index 93% rename from test/integration/editors/substation/conducting-equipment-editor-wizarding.test.ts rename to test/integration/zeroline/conducting-equipment-editor-wizarding.test.ts index 464a4d0e9..69b052be1 100644 --- a/test/integration/editors/substation/conducting-equipment-editor-wizarding.test.ts +++ b/test/integration/zeroline/conducting-equipment-editor-wizarding.test.ts @@ -1,10 +1,10 @@ import { fixture, html, expect } from '@open-wc/testing'; import fc from 'fast-check'; -import '../../../mock-wizard.js'; -import { WizardingElement } from '../../../../src/Wizarding.js'; +import { regexString, regExp } from '../../foundation.js'; +import '../../mock-wizard.js'; -import { regexString, regExp } from '../../../foundation.js'; +import { WizardingElement } from '../../../src/Wizarding.js'; describe('conducting-equipment-editor wizarding integration', () => { let doc: XMLDocument; diff --git a/test/integration/editors/substation/substation-editor-wizarding-editing.test.ts b/test/integration/zeroline/substation-editor-wizarding-editing.test.ts similarity index 95% rename from test/integration/editors/substation/substation-editor-wizarding-editing.test.ts rename to test/integration/zeroline/substation-editor-wizarding-editing.test.ts index 95daeceb5..fded7ce2e 100644 --- a/test/integration/editors/substation/substation-editor-wizarding-editing.test.ts +++ b/test/integration/zeroline/substation-editor-wizarding-editing.test.ts @@ -1,11 +1,10 @@ import { fixture, html, expect } from '@open-wc/testing'; -import '../../../mock-wizard-editor.js'; -import { EditingElement } from '../../../../src/Editing.js'; -import { WizardingElement } from '../../../../src/Wizarding.js'; - -import { SubstationEditor } from '../../../../src/editors/substation/substation-editor.js'; -import { WizardTextField } from '../../../../src/wizard-textfield.js'; +import '../../mock-wizard-editor.js'; +import { EditingElement } from '../../../src/Editing.js'; +import { SubstationEditor } from '../../../src/zeroline/substation-editor.js'; +import { WizardingElement } from '../../../src/Wizarding.js'; +import { WizardTextField } from '../../../src/wizard-textfield.js'; describe('substation-editor wizarding editing integration', () => { describe('edit wizard', () => { diff --git a/test/integration/editors/substation/substation-editor-wizarding.test.ts b/test/integration/zeroline/substation-editor-wizarding.test.ts similarity index 92% rename from test/integration/editors/substation/substation-editor-wizarding.test.ts rename to test/integration/zeroline/substation-editor-wizarding.test.ts index ebce0a955..2a0d2541e 100644 --- a/test/integration/editors/substation/substation-editor-wizarding.test.ts +++ b/test/integration/zeroline/substation-editor-wizarding.test.ts @@ -1,10 +1,9 @@ import { fixture, html, expect } from '@open-wc/testing'; import fc from 'fast-check'; +import { regExp, regexString } from '../../foundation.js'; -import '../../../mock-wizard.js'; -import { WizardingElement } from '../../../../src/Wizarding.js'; - -import { regexString, regExp } from '../../../foundation.js'; +import '../../mock-wizard.js'; +import { WizardingElement } from '../../../src/Wizarding.js'; describe('substation-editor wizarding integration', () => { let doc: XMLDocument; diff --git a/test/integration/editors/substation/voltage-level-editor-wizarding-editing.test.ts b/test/integration/zeroline/voltage-level-editor-wizarding-editing.test.ts similarity index 97% rename from test/integration/editors/substation/voltage-level-editor-wizarding-editing.test.ts rename to test/integration/zeroline/voltage-level-editor-wizarding-editing.test.ts index 7435ad3e6..7730ad402 100644 --- a/test/integration/editors/substation/voltage-level-editor-wizarding-editing.test.ts +++ b/test/integration/zeroline/voltage-level-editor-wizarding-editing.test.ts @@ -1,11 +1,11 @@ import { fixture, html, expect } from '@open-wc/testing'; -import '../../../mock-wizard-editor.js'; -import { EditingElement } from '../../../../src/Editing.js'; -import { WizardingElement } from '../../../../src/Wizarding.js'; -import { VoltageLevelEditor } from '../../../../src/editors/substation/voltage-level-editor.js'; +import '../../mock-wizard-editor.js'; -import { WizardTextField } from '../../../../src/wizard-textfield.js'; +import { EditingElement } from '../../../src/Editing.js'; +import { VoltageLevelEditor } from '../../../src/zeroline/voltage-level-editor.js'; +import { WizardingElement } from '../../../src/Wizarding.js'; +import { WizardTextField } from '../../../src/wizard-textfield.js'; describe('voltage-level-editor wizarding editing integration', () => { describe('edit wizard', () => { diff --git a/test/integration/editors/substation/voltage-level-editor-wizarding.test.ts b/test/integration/zeroline/voltage-level-editor-wizarding.test.ts similarity index 97% rename from test/integration/editors/substation/voltage-level-editor-wizarding.test.ts rename to test/integration/zeroline/voltage-level-editor-wizarding.test.ts index ae62ace2d..1e0857c74 100644 --- a/test/integration/editors/substation/voltage-level-editor-wizarding.test.ts +++ b/test/integration/zeroline/voltage-level-editor-wizarding.test.ts @@ -1,10 +1,10 @@ import { fixture, html, expect } from '@open-wc/testing'; import fc from 'fast-check'; -import '../../../mock-wizard.js'; -import { WizardingElement } from '../../../../src/Wizarding.js'; +import '../../mock-wizard.js'; +import { WizardingElement } from '../../../src/Wizarding.js'; -import { regexString, regExp, inverseRegExp } from '../../../foundation.js'; +import { regexString, regExp, inverseRegExp } from '../../foundation.js'; describe('voltage-level-editor wizarding integration', () => { let doc: XMLDocument; diff --git a/test/testfiles/zeroline/iedalloctest.scd b/test/testfiles/zeroline/iedalloctest.scd new file mode 100644 index 000000000..c6de2e96d --- /dev/null +++ b/test/testfiles/zeroline/iedalloctest.scd @@ -0,0 +1,64 @@ + + +
+ TrainingIEC61850 + + + +
+ + + + 110.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20 + + + + + + + + + + + + + + + + +
diff --git a/test/testfiles/zeroline/substationonly.scd b/test/testfiles/zeroline/substationonly.scd new file mode 100644 index 000000000..b5d2a9a90 --- /dev/null +++ b/test/testfiles/zeroline/substationonly.scd @@ -0,0 +1,41 @@ + + +
+ TrainingIEC61850 + + + +
+ + + 110.0 + + + + + + + + + + + + + + + + + + + + + + + + + 20 + + + + +
diff --git a/test/unit/zeroline-pane.test.ts b/test/unit/zeroline-pane.test.ts new file mode 100644 index 000000000..53f67d621 --- /dev/null +++ b/test/unit/zeroline-pane.test.ts @@ -0,0 +1,145 @@ +import { expect, fixture, html } from '@open-wc/testing'; +import { + attachedIeds, + getAttachedIeds, +} from '../../src/zeroline/foundation.js'; + +describe('zeroline-pane', () => { + let doc: XMLDocument; + + let substation1: Element; + let substation2: Element; + + let voltageLevel1: Element; + let voltageLevel2: Element; + + let bay1: Element; + let bay2: Element; + + let remainingIeds: Set; + + beforeEach(async () => { + doc = await fetch('/base/test/testfiles/zeroline/iedalloctest.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + substation1 = doc.querySelector('Substation[name="AA1"]')!; + substation2 = doc.querySelector('Substation[name="AA2"]')!; + + voltageLevel1 = doc.querySelector('VoltageLevel[name="E1"]')!; + voltageLevel2 = doc.querySelector('VoltageLevel[name="J1"]')!; + + bay1 = doc.querySelector('Bay[name="Bay1"]')!; + bay2 = doc.querySelector('Bay[name="Bay2"]')!; + + remainingIeds = new Set(Array.from(doc.querySelectorAll('IED'))); + }); + + it('per default looks like the latest snapshot', async () => { + const element = await fixture( + html`` + ); + + await new Promise(resolve => setTimeout(resolve, 2000)); // await animation + + expect(element).shadowDom.to.equalSnapshot(); + }).timeout(5000); + + it('readonly looks like the latest snapshot', async () => { + const element = await fixture( + html`` + ); + + await new Promise(resolve => setTimeout(resolve, 2000)); // await animation + + expect(element).shadowDom.to.equalSnapshot(); + }).timeout(5000); + + it('showieds looks like the latest snapshot', async () => { + const element = await fixture( + html`` + ); + + await new Promise(resolve => setTimeout(resolve, 2000)); // await animation + + expect(element).shadowDom.to.equalSnapshot(); + }).timeout(5000); + + describe('attachedIeds', () => { + it('returns IEDs that cannot be allocated to any element', async () => { + const ieds = await attachedIeds(doc.documentElement!, remainingIeds); + + expect(ieds.length).to.equal(2); + expect(ieds[0].getAttribute('name')).to.equal('IED6'); + expect(ieds[1].getAttribute('name')).to.equal('IED8'); + }); + describe('return IEDs for Bay elements that', () => { + it('are connected itself or underlaying conduncting equipment', async () => { + const ieds = await attachedIeds(bay1, remainingIeds); + + expect(ieds.length).to.equal(2); + expect(ieds[0].getAttribute('name')).to.equal('IED1'); + expect(ieds[1].getAttribute('name')).to.equal('IED4'); + }); + + it('are not connected to another Bay as well', async () => { + expect((await attachedIeds(bay2, remainingIeds)).length).to.equal(0); + }); + }); + describe('return IEDs for VoltageLevel elements that', () => { + it('are connected itself or underlaying elements', async () => { + const ieds = await attachedIeds(voltageLevel1, remainingIeds); + + expect(ieds.length).to.equal(2); + expect(ieds[0].getAttribute('name')).to.equal('IED2'); + expect(ieds[1].getAttribute('name')).to.equal('IED5'); + }); + + it('are not connected to another Bay ass well', async () => { + expect( + (await attachedIeds(voltageLevel2, remainingIeds)).length + ).to.equal(0); + }); + }); + describe('return IEDs for Substation elements that', () => { + it('are connected itself or underlaying elements', async () => { + const ieds = await attachedIeds(substation1, remainingIeds); + + expect(ieds.length).to.equal(2); + expect(ieds[0].getAttribute('name')).to.equal('IED3'); + expect(ieds[1].getAttribute('name')).to.equal('IED7'); + }); + it('are not connected to another Substation ass well', async () => { + expect( + (await attachedIeds(substation2, remainingIeds)).length + ).to.equal(0); + }); + }); + }); + it('both the functions return every IED only once', async () => { + const numSub1 = (await attachedIeds(substation1, remainingIeds)).length; + const numSub2 = (await attachedIeds(substation2, remainingIeds)).length; + const numVolt1 = (await attachedIeds(voltageLevel1, remainingIeds)).length; + const numVolt2 = (await attachedIeds(voltageLevel2, remainingIeds)).length; + const numBay1 = (await attachedIeds(bay1, remainingIeds)).length; + const numBay2 = (await attachedIeds(bay2, remainingIeds)).length; + + const numUnRef = (await attachedIeds(doc.documentElement, remainingIeds)) + .length; + + const sumIeds = + numBay1 + numBay2 + numVolt1 + numVolt2 + numSub1 + numSub2 + numUnRef; + + expect(sumIeds).to.equal(doc.querySelectorAll('IED').length); + }); +}).timeout(10000); diff --git a/test/unit/editors/substation/BayEditor.test.ts b/test/unit/zeroline/BayEditor.test.ts similarity index 83% rename from test/unit/editors/substation/BayEditor.test.ts rename to test/unit/zeroline/BayEditor.test.ts index cd8da65dc..305a46c39 100644 --- a/test/unit/editors/substation/BayEditor.test.ts +++ b/test/unit/zeroline/BayEditor.test.ts @@ -1,11 +1,10 @@ -import { WizardInput, isCreate, isUpdate } from '../../../../src/foundation.js'; import { fixture, html, expect } from '@open-wc/testing'; -import '../../../../src/wizard-textfield.js'; -import { BayEditor } from '../../../../src/editors/substation/bay-editor.js'; -import { updateNamingAction } from '../../../../src/editors/substation/foundation.js'; -import { wizards } from '../../../../src/wizards/wizard-library.js'; -import { createAction } from '../../../../src/wizards/bay.js'; +import { WizardInput, isCreate, isUpdate } from '../../../src/foundation.js'; + +import '../../../src/wizard-textfield.js'; +import { updateNamingAction } from '../../../src/zeroline/foundation.js'; +import { createAction } from '../../../src/wizards/bay.js'; describe('BayEditor', () => { const noOp = () => { diff --git a/test/unit/editors/substation/ConductingEquipmentEditor.test.ts b/test/unit/zeroline/ConductingEquipmentEditor.test.ts similarity index 83% rename from test/unit/editors/substation/ConductingEquipmentEditor.test.ts rename to test/unit/zeroline/ConductingEquipmentEditor.test.ts index adca90819..5ca0e5620 100644 --- a/test/unit/editors/substation/ConductingEquipmentEditor.test.ts +++ b/test/unit/zeroline/ConductingEquipmentEditor.test.ts @@ -1,7 +1,9 @@ import { fixture, html, expect } from '@open-wc/testing'; -import { WizardInput, isCreate, isUpdate } from '../../../../src/foundation.js'; -import { ConductingEquipmentEditor } from '../../../../src/editors/substation/conducting-equipment-editor.js'; -import { updateNamingAction } from '../../../../src/editors/substation/foundation.js'; + +import { WizardInput, isCreate, isUpdate } from '../../../src/foundation.js'; +import { updateNamingAction } from '../../../src/zeroline/foundation.js'; +import { createAction } from '../../../src/wizards/conductingequipment.js'; + describe('ConductingEquipmentEditor', () => { const noOp = () => { return; @@ -36,10 +38,10 @@ describe('ConductingEquipmentEditor', () => { ).documentElement; }); - /* it('returns a WizardAction which returns a Create EditorAction', () => { - const wizardAction = ConductingEquipmentEditor.createAction(parent); + it('returns a WizardAction which returns a Create EditorAction', () => { + const wizardAction = createAction(parent); expect(wizardAction(inputs, newWizard())[0]).to.satisfy(isCreate); - }); */ + }); }); describe('updateAction', () => { diff --git a/test/unit/editors/substation/SubstationEditor.test.ts b/test/unit/zeroline/SubstationEditor.test.ts similarity index 83% rename from test/unit/editors/substation/SubstationEditor.test.ts rename to test/unit/zeroline/SubstationEditor.test.ts index e71dd76e4..9f11919e0 100644 --- a/test/unit/editors/substation/SubstationEditor.test.ts +++ b/test/unit/zeroline/SubstationEditor.test.ts @@ -1,8 +1,9 @@ import { fixture, html, expect } from '@open-wc/testing'; -import { WizardInput, isCreate, isUpdate } from '../../../../src/foundation.js'; -import { SubstationEditor } from '../../../../src/editors/substation/substation-editor.js'; -import { updateNamingAction } from '../../../../src/editors/substation/foundation.js'; +import { WizardInput, isCreate, isUpdate } from '../../../src/foundation.js'; +import { createAction } from '../../../src/wizards/substation.js'; +import { updateNamingAction } from '../../../src/zeroline/foundation.js'; + describe('SubstationEditor', () => { const noOp = () => { return; @@ -34,10 +35,10 @@ describe('SubstationEditor', () => { ).documentElement; }); - /* it('returns a WizardAction which returns a Create EditorAction', () => { - const wizardAction = SubstationEditor.createAction(parent); + it('returns a WizardAction which returns a Create EditorAction', () => { + const wizardAction = createAction(parent); expect(wizardAction(inputs, newWizard())[0]).to.satisfy(isCreate); - }); */ + }); }); describe('updateAction', () => { diff --git a/test/unit/editors/substation/VoltageLevelEditor.test.ts b/test/unit/zeroline/VoltageLevelEditor.test.ts similarity index 97% rename from test/unit/editors/substation/VoltageLevelEditor.test.ts rename to test/unit/zeroline/VoltageLevelEditor.test.ts index 7dd5e9352..11263e15a 100644 --- a/test/unit/editors/substation/VoltageLevelEditor.test.ts +++ b/test/unit/zeroline/VoltageLevelEditor.test.ts @@ -4,12 +4,12 @@ import { isCreate, isUpdate, isDelete, -} from '../../../../src/foundation.js'; -import { VoltageLevelEditor } from '../../../../src/editors/substation/voltage-level-editor.js'; +} from '../../../src/foundation.js'; + import { createAction, updateAction, -} from '../../../../src/wizards/voltagelevel.js'; +} from '../../../src/wizards/voltagelevel.js'; describe('VoltageLevelEditor', () => { describe('with no nulled properties', () => { diff --git a/test/unit/editors/substation/bay-editor.test.ts b/test/unit/zeroline/bay-editor.test.ts similarity index 63% rename from test/unit/editors/substation/bay-editor.test.ts rename to test/unit/zeroline/bay-editor.test.ts index a80d62e61..b449e6f99 100644 --- a/test/unit/editors/substation/bay-editor.test.ts +++ b/test/unit/zeroline/bay-editor.test.ts @@ -1,7 +1,7 @@ import { fixture, html, expect } from '@open-wc/testing'; -import '../../../../src/editors/substation/bay-editor.js'; -import { BayEditor } from '../../../../src/editors/substation/bay-editor.js'; +import '../../../src/zeroline/bay-editor.js'; +import { BayEditor } from '../../../src/zeroline/bay-editor.js'; describe('bay-editor', () => { let element: BayEditor; @@ -20,13 +20,17 @@ describe('bay-editor', () => { ); }); - it('has a name property', () => - expect(element).to.have.property('name', 'COUPLING_BAY')); - - it('has a desc property', () => - expect(element).to.have.property('desc', 'Bay')); - it('looks like the latest snapshot', () => { expect(element).shadowDom.to.equalSnapshot(); }); + + describe('with readonly property', () => { + beforeEach(async () => { + element.readonly = true; + await element.requestUpdate(); + }); + it('looks like the latest snapshot', () => { + expect(element).shadowDom.to.equalSnapshot(); + }); + }); }); diff --git a/test/unit/editors/substation/conducting-equipment-editor.test.ts b/test/unit/zeroline/conducting-equipment-editor.test.ts similarity index 63% rename from test/unit/editors/substation/conducting-equipment-editor.test.ts rename to test/unit/zeroline/conducting-equipment-editor.test.ts index 595807473..a0551b9f1 100644 --- a/test/unit/editors/substation/conducting-equipment-editor.test.ts +++ b/test/unit/zeroline/conducting-equipment-editor.test.ts @@ -1,7 +1,7 @@ import { fixture, html, expect } from '@open-wc/testing'; -import '../../../../src/editors/substation/conducting-equipment-editor.js'; -import { ConductingEquipmentEditor } from '../../../../src/editors/substation/conducting-equipment-editor.js'; +import '../../../src/zeroline/conducting-equipment-editor.js'; +import { ConductingEquipmentEditor } from '../../../src/zeroline/conducting-equipment-editor.js'; describe('conducting-equipment-editor', () => { let element: ConductingEquipmentEditor; @@ -20,13 +20,17 @@ describe('conducting-equipment-editor', () => { ); }); - it('has a name property', () => - expect(element).to.have.property('name', 'QA1')); - - it('has a desc property', () => - expect(element).to.have.property('desc', 'coupling field ciscuit breaker')); - it('looks like the latest snapshot', () => { expect(element).shadowDom.to.equalSnapshot(); }); + + describe('with readonly property', () => { + beforeEach(async () => { + element.readonly = true; + await element.requestUpdate(); + }); + it('looks like the latest snapshot', () => { + expect(element).shadowDom.to.equalSnapshot(); + }); + }); }); diff --git a/test/unit/editors/substation/substation-editor.test.ts b/test/unit/zeroline/substation-editor.test.ts similarity index 61% rename from test/unit/editors/substation/substation-editor.test.ts rename to test/unit/zeroline/substation-editor.test.ts index b1eb74683..e728e7631 100644 --- a/test/unit/editors/substation/substation-editor.test.ts +++ b/test/unit/zeroline/substation-editor.test.ts @@ -1,7 +1,7 @@ import { html, fixture, expect } from '@open-wc/testing'; -import '../../../../src/editors/substation/substation-editor.js'; -import { SubstationEditor } from '../../../../src/editors/substation/substation-editor.js'; +import '../../../src/zeroline/substation-editor.js'; +import { SubstationEditor } from '../../../src/zeroline/substation-editor.js'; describe('substation-editor', () => { let element: SubstationEditor; @@ -16,13 +16,17 @@ describe('substation-editor', () => { >`); }); - it('has a name property', () => - expect(element).to.have.property('name', 'AA1')); - - it('has a desc property', () => - expect(element).to.have.property('desc', 'Substation')); - it('looks like the latest snapshot', () => { expect(element).shadowDom.to.equalSnapshot(); }); + + describe('with readonly property', () => { + beforeEach(async () => { + element.readonly = true; + await element.requestUpdate(); + }); + it('looks like the latest snapshot', () => { + expect(element).shadowDom.to.equalSnapshot(); + }); + }); }); diff --git a/test/unit/editors/substation/voltage-level-editor.test.ts b/test/unit/zeroline/voltage-level-editor.test.ts similarity index 63% rename from test/unit/editors/substation/voltage-level-editor.test.ts rename to test/unit/zeroline/voltage-level-editor.test.ts index e30ccff57..148cf44d4 100644 --- a/test/unit/editors/substation/voltage-level-editor.test.ts +++ b/test/unit/zeroline/voltage-level-editor.test.ts @@ -1,7 +1,7 @@ import { fixture, html, expect } from '@open-wc/testing'; -import '../../../../src/editors/substation/voltage-level-editor.js'; -import { VoltageLevelEditor } from '../../../../src/editors/substation/voltage-level-editor.js'; +import '../../../src/zeroline/voltage-level-editor.js'; +import { VoltageLevelEditor } from '../../../src/zeroline/voltage-level-editor.js'; describe('voltage-level-editor', () => { let element: VoltageLevelEditor; @@ -20,13 +20,17 @@ describe('voltage-level-editor', () => { ); }); - it('has a name property', () => - expect(element).to.have.property('name', 'E1')); - - it('has a desc property', () => - expect(element).to.have.property('desc', 'Voltage Level')); - it('looks like the latest snapshot', () => { expect(element).shadowDom.to.equalSnapshot(); }); + + describe('with readonly property', () => { + beforeEach(async () => { + element.readonly = true; + await element.requestUpdate(); + }); + it('looks like the latest snapshot', () => { + expect(element).shadowDom.to.equalSnapshot(); + }); + }); });