From af668a3eeaaad949343dcddb6bb9c1fd49fd61ac Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Wed, 12 Jan 2022 17:23:04 +0100 Subject: [PATCH 01/12] refactor(wizards/subnetwork): move wizards --- src/editors/Communication.ts | 2 +- .../communication/subnetwork-editor.ts | 232 +---------------- src/wizards/subnetwork.ts | 236 ++++++++++++++++++ .../editors/communication/SubNetwork.test.ts | 8 +- 4 files changed, 244 insertions(+), 234 deletions(-) create mode 100644 src/wizards/subnetwork.ts diff --git a/src/editors/Communication.ts b/src/editors/Communication.ts index e7b4b0b365..c81b7bbd21 100644 --- a/src/editors/Communication.ts +++ b/src/editors/Communication.ts @@ -10,7 +10,7 @@ import { } from '../foundation.js'; import { selectors, styles } from './communication/foundation.js'; import './communication/subnetwork-editor.js'; -import { subNetworkWizard } from './communication/subnetwork-editor.js'; +import { subNetworkWizard } from '../wizards/subnetwork.js'; /** An editor [[`plugin`]] for editing the `Communication` section. */ export default class CommunicationPlugin extends LitElement { diff --git a/src/editors/communication/subnetwork-editor.ts b/src/editors/communication/subnetwork-editor.ts index 3cb5f133aa..07c709056c 100644 --- a/src/editors/communication/subnetwork-editor.ts +++ b/src/editors/communication/subnetwork-editor.ts @@ -6,245 +6,19 @@ import { property, css, } from 'lit-element'; -import { translate, get } from 'lit-translate'; +import { translate } from 'lit-translate'; import '@material/mwc-icon-button'; -import '../../wizard-textfield.js'; import './connectedap-editor.js'; import { - EditorAction, newWizardEvent, - Wizard, - WizardActor, - WizardInput, newActionEvent, - getValue, - getMultiplier, - patterns, compareNames, - createElement, - cloneElement, } from '../../foundation.js'; -import { styles, WizardOptions, isCreateOptions } from './foundation.js'; +import { styles } from './foundation.js'; import { createConnectedApWizard } from '../../wizards/connectedap.js'; - -/** Initial attribute values suggested for `SubNetwork` creation */ -const initial = { - type: '8-MMS', - bitrate: '100', - multiplier: 'M', -}; - -function getBitRateAction( - oldBitRate: Element | null, - BitRate: string | null, - multiplier: string | null, - SubNetwork: Element -): EditorAction { - if (oldBitRate === null) - return { - new: { - parent: SubNetwork, - element: new DOMParser().parseFromString( - `${BitRate === null ? '' : BitRate}`, - 'application/xml' - ).documentElement, - reference: SubNetwork.firstElementChild, - }, - }; - - if (BitRate === null) - return { - old: { - parent: SubNetwork, - element: oldBitRate, - reference: oldBitRate.nextSibling, - }, - }; - - const newBitRate = cloneElement(oldBitRate, { multiplier }); - newBitRate.textContent = BitRate; - - return { - old: { element: oldBitRate }, - new: { element: newBitRate }, - }; -} - -export function updateSubNetworkAction(element: Element): WizardActor { - return (inputs: WizardInput[], wizard: Element): EditorAction[] => { - const name = inputs.find(i => i.label === 'name')!.value!; - const desc = getValue(inputs.find(i => i.label === 'desc')!); - const type = getValue(inputs.find(i => i.label === 'type')!); - const BitRate = getValue(inputs.find(i => i.label === 'BitRate')!); - const multiplier = getMultiplier(inputs.find(i => i.label === 'BitRate')!); - - let subNetworkAction: EditorAction | null; - let bitRateAction: EditorAction | null; - - if ( - name === element.getAttribute('name') && - desc === element.getAttribute('desc') && - type === element.getAttribute('type') - ) { - subNetworkAction = null; - } else { - const newElement = cloneElement(element, { name, desc, type }); - subNetworkAction = { old: { element }, new: { element: newElement } }; - } - - if ( - BitRate === - (element.querySelector('SubNetwork > BitRate')?.textContent?.trim() ?? - null) && - multiplier === - (element - .querySelector('SubNetwork > BitRate') - ?.getAttribute('multiplier') ?? null) - ) { - bitRateAction = null; - } else { - bitRateAction = getBitRateAction( - element.querySelector('SubNetwork > BitRate'), - BitRate, - multiplier, - subNetworkAction?.new.element ?? element - ); - } - - const actions: EditorAction[] = []; - if (subNetworkAction) actions.push(subNetworkAction); - if (bitRateAction) actions.push(bitRateAction); - return actions; - }; -} - -export function createSubNetworkAction(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')!); - const type = getValue(inputs.find(i => i.label === 'type')!); - const BitRate = getValue(inputs.find(i => i.label === 'BitRate')!); - const multiplier = getMultiplier(inputs.find(i => i.label === 'BitRate')!); - - const element = createElement(parent.ownerDocument, 'SubNetwork', { - name, - desc, - type, - }); - - if (BitRate !== null) { - const bitRateElement = createElement(parent.ownerDocument, 'BitRate', { - unit: 'b/s', - multiplier, - }); - bitRateElement.textContent = BitRate; - element.appendChild(bitRateElement); - } - - const action = { - new: { - parent, - element, - }, - }; - - return [action]; - }; -} - -export function subNetworkWizard(options: WizardOptions): Wizard { - const [ - heading, - actionName, - actionIcon, - action, - name, - desc, - type, - BitRate, - multiplier, - element, - ] = isCreateOptions(options) - ? [ - get('subnetwork.wizard.title.add'), - get('add'), - 'add', - createSubNetworkAction(options.parent), - '', - '', - initial.type, - initial.bitrate, - initial.multiplier, - undefined, - ] - : [ - get('subnetwork.wizard.title.edit'), - get('save'), - 'edit', - updateSubNetworkAction(options.element), - options.element.getAttribute('name'), - options.element.getAttribute('desc'), - options.element.getAttribute('type'), - options.element - .querySelector('SubNetwork > BitRate') - ?.textContent?.trim() ?? null, - options.element - .querySelector('SubNetwork > BitRate') - ?.getAttribute('multiplier') ?? null, - options.element, - ]; - - return [ - { - title: heading, - element, - primary: { - icon: actionIcon, - label: actionName, - action: action, - }, - content: [ - html``, - html``, - html``, - html``, - ], - }, - ]; -} +import { subNetworkWizard } from '../../wizards/subnetwork.js'; /** [[`Communication`]] subeditor for a `SubNetwork` element. */ @customElement('subnetwork-editor') diff --git a/src/wizards/subnetwork.ts b/src/wizards/subnetwork.ts new file mode 100644 index 0000000000..43705cba5b --- /dev/null +++ b/src/wizards/subnetwork.ts @@ -0,0 +1,236 @@ +import { html } from 'lit-element'; +import { get, translate } from 'lit-translate'; + +import '../wizard-textfield.js'; +import { + cloneElement, + createElement, + EditorAction, + getMultiplier, + getValue, + patterns, + Wizard, + WizardActor, + WizardInput, +} from '../foundation.js'; +import { + isCreateOptions, + WizardOptions, +} from '../editors/communication/foundation.js'; + +/** Initial attribute values suggested for `SubNetwork` creation */ +const initial = { + type: '8-MMS', + bitrate: '100', + multiplier: 'M', +}; + +function getBitRateAction( + oldBitRate: Element | null, + BitRate: string | null, + multiplier: string | null, + SubNetwork: Element +): EditorAction { + if (oldBitRate === null) + return { + new: { + parent: SubNetwork, + element: new DOMParser().parseFromString( + `${BitRate === null ? '' : BitRate}`, + 'application/xml' + ).documentElement, + reference: SubNetwork.firstElementChild, + }, + }; + + if (BitRate === null) + return { + old: { + parent: SubNetwork, + element: oldBitRate, + reference: oldBitRate.nextSibling, + }, + }; + + const newBitRate = cloneElement(oldBitRate, { multiplier }); + newBitRate.textContent = BitRate; + + return { + old: { element: oldBitRate }, + new: { element: newBitRate }, + }; +} + +export function updateSubNetworkAction(element: Element): WizardActor { + return (inputs: WizardInput[], wizard: Element): EditorAction[] => { + const name = inputs.find(i => i.label === 'name')!.value!; + const desc = getValue(inputs.find(i => i.label === 'desc')!); + const type = getValue(inputs.find(i => i.label === 'type')!); + const BitRate = getValue(inputs.find(i => i.label === 'BitRate')!); + const multiplier = getMultiplier(inputs.find(i => i.label === 'BitRate')!); + + let subNetworkAction: EditorAction | null; + let bitRateAction: EditorAction | null; + + if ( + name === element.getAttribute('name') && + desc === element.getAttribute('desc') && + type === element.getAttribute('type') + ) { + subNetworkAction = null; + } else { + const newElement = cloneElement(element, { name, desc, type }); + subNetworkAction = { old: { element }, new: { element: newElement } }; + } + + if ( + BitRate === + (element.querySelector('SubNetwork > BitRate')?.textContent?.trim() ?? + null) && + multiplier === + (element + .querySelector('SubNetwork > BitRate') + ?.getAttribute('multiplier') ?? null) + ) { + bitRateAction = null; + } else { + bitRateAction = getBitRateAction( + element.querySelector('SubNetwork > BitRate'), + BitRate, + multiplier, + subNetworkAction?.new.element ?? element + ); + } + + const actions: EditorAction[] = []; + if (subNetworkAction) actions.push(subNetworkAction); + if (bitRateAction) actions.push(bitRateAction); + return actions; + }; +} + +export function createSubNetworkAction(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')!); + const type = getValue(inputs.find(i => i.label === 'type')!); + const BitRate = getValue(inputs.find(i => i.label === 'BitRate')!); + const multiplier = getMultiplier(inputs.find(i => i.label === 'BitRate')!); + + const element = createElement(parent.ownerDocument, 'SubNetwork', { + name, + desc, + type, + }); + + if (BitRate !== null) { + const bitRateElement = createElement(parent.ownerDocument, 'BitRate', { + unit: 'b/s', + multiplier, + }); + bitRateElement.textContent = BitRate; + element.appendChild(bitRateElement); + } + + const action = { + new: { + parent, + element, + }, + }; + + return [action]; + }; +} + +export function subNetworkWizard(options: WizardOptions): Wizard { + const [ + heading, + actionName, + actionIcon, + action, + name, + desc, + type, + BitRate, + multiplier, + element, + ] = isCreateOptions(options) + ? [ + get('subnetwork.wizard.title.add'), + get('add'), + 'add', + createSubNetworkAction(options.parent), + '', + '', + initial.type, + initial.bitrate, + initial.multiplier, + undefined, + ] + : [ + get('subnetwork.wizard.title.edit'), + get('save'), + 'edit', + updateSubNetworkAction(options.element), + options.element.getAttribute('name'), + options.element.getAttribute('desc'), + options.element.getAttribute('type'), + options.element + .querySelector('SubNetwork > BitRate') + ?.textContent?.trim() ?? null, + options.element + .querySelector('SubNetwork > BitRate') + ?.getAttribute('multiplier') ?? null, + options.element, + ]; + + return [ + { + title: heading, + element, + primary: { + icon: actionIcon, + label: actionName, + action: action, + }, + content: [ + html``, + html``, + html``, + html``, + ], + }, + ]; +} diff --git a/test/unit/editors/communication/SubNetwork.test.ts b/test/unit/editors/communication/SubNetwork.test.ts index d4150677c9..cd4b76fd02 100644 --- a/test/unit/editors/communication/SubNetwork.test.ts +++ b/test/unit/editors/communication/SubNetwork.test.ts @@ -1,16 +1,16 @@ import { fixture, html, expect } from '@open-wc/testing'; import '../../../../src/wizard-textfield.js'; -import { - createSubNetworkAction, - updateSubNetworkAction, -} from '../../../../src/editors/communication/subnetwork-editor.js'; import { WizardInput, isCreate, isUpdate, isDelete, } from '../../../../src/foundation.js'; +import { + createSubNetworkAction, + updateSubNetworkAction, +} from '../../../../src/wizards/subnetwork.js'; describe('SubNetworkEditor', () => { describe('with no nulled properties', () => { From d775f1b86937fabc4361fc9044194cbc6f3c1c86 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Wed, 12 Jan 2022 17:32:30 +0100 Subject: [PATCH 02/12] refactor(editors/communication/subnetwork-editor): minor changes/doc --- .../communication/subnetwork-editor.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/editors/communication/subnetwork-editor.ts b/src/editors/communication/subnetwork-editor.ts index 07c709056c..a122c4c76a 100644 --- a/src/editors/communication/subnetwork-editor.ts +++ b/src/editors/communication/subnetwork-editor.ts @@ -23,21 +23,25 @@ import { subNetworkWizard } from '../../wizards/subnetwork.js'; /** [[`Communication`]] subeditor for a `SubNetwork` element. */ @customElement('subnetwork-editor') export class SubNetworkEditor extends LitElement { - @property() + /** SCL element SubNetwork */ + @property({ attribute: false }) element!: Element; - + /** SubNetwork attribute name */ @property() get name(): string { - return this.element.getAttribute('name') ?? ''; + return this.element.getAttribute('name') ?? 'UNDEFINED'; } + /** SubNetwork attribute desc */ @property() get desc(): string | null { return this.element.getAttribute('desc') ?? null; } + /** SubNetwork attribute type */ @property() get type(): string | null { return this.element.getAttribute('type') ?? null; } + /** SubNetwork child elements BitRate label */ @property() get bitrate(): string | null { const V = this.element.querySelector('BitRate'); @@ -48,11 +52,11 @@ export class SubNetworkEditor extends LitElement { return v ? v + u : null; } - openConnectedAPwizard(): void { + private openConnectedAPwizard(): void { this.dispatchEvent(newWizardEvent(createConnectedApWizard(this.element))); } - openEditWizard(): void { + private openEditWizard(): void { this.dispatchEvent( newWizardEvent(subNetworkWizard({ element: this.element })) ); @@ -71,7 +75,7 @@ export class SubNetworkEditor extends LitElement { ); } - renderSubNetworkSpecs(): TemplateResult { + private renderSubNetworkSpecs(): TemplateResult { if (!this.type && !this.bitrate) return html``; return html`(${this.type}${this.type && this.bitrate @@ -79,7 +83,7 @@ export class SubNetworkEditor extends LitElement { : html``}${this.bitrate})`; } - renderHeader(): TemplateResult { + private renderHeader(): TemplateResult { return html`

${this.name} ${this.desc === null ? '' : html`—`} ${this.desc} ${this.renderSubNetworkSpecs()} @@ -106,7 +110,7 @@ export class SubNetworkEditor extends LitElement {

`; } - renderIedContainer(): TemplateResult[] { + private renderIedContainer(): TemplateResult[] { return Array.from(this.element.querySelectorAll('ConnectedAP') ?? []) .map(connAP => connAP.getAttribute('iedName')!) .filter((v, i, a) => a.indexOf(v) === i) From 31d7d36ff399a4521d7213e8d219bcaf20e000f0 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Thu, 13 Jan 2022 15:51:31 +0100 Subject: [PATCH 03/12] refactor(editors/communication/subnetwork-editor): use action-pane --- src/editors/Communication.ts | 30 +++++++---- .../communication/subnetwork-editor.ts | 53 ++++++++----------- .../subnetwork-editor.test.snap.js | 52 +++++++++--------- 3 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/editors/Communication.ts b/src/editors/Communication.ts index c81b7bbd21..b981348f64 100644 --- a/src/editors/Communication.ts +++ b/src/editors/Communication.ts @@ -8,7 +8,7 @@ import { newActionEvent, createElement, } from '../foundation.js'; -import { selectors, styles } from './communication/foundation.js'; +import { selectors } from './communication/foundation.js'; import './communication/subnetwork-editor.js'; import { subNetworkWizard } from '../wizards/subnetwork.js'; @@ -60,24 +60,32 @@ export default class CommunicationPlugin extends LitElement { icon="add" label="${get('subnetwork.wizard.title.add')}" @click=${() => this.openCreateSubNetworkWizard()} - >${Array.from(this.doc.querySelectorAll(selectors.SubNetwork) ?? []).map( - subnetwork => - html`` - )}`; + > +
+ ${Array.from(this.doc.querySelectorAll(selectors.SubNetwork) ?? []).map( + subnetwork => + html`` + )} +
`; } static styles = css` - ${styles} + :host { + width: 100vw; + } + section { + outline: none; + padding: 8px 12px 16px; + } + + subnetwork-editor { + margin: 8px 12px 16px; + } mwc-fab { position: fixed; bottom: 32px; right: 32px; } - - :host { - width: 100vw; - } `; } diff --git a/src/editors/communication/subnetwork-editor.ts b/src/editors/communication/subnetwork-editor.ts index a122c4c76a..a8f328ab67 100644 --- a/src/editors/communication/subnetwork-editor.ts +++ b/src/editors/communication/subnetwork-editor.ts @@ -84,30 +84,8 @@ export class SubNetworkEditor extends LitElement { } private renderHeader(): TemplateResult { - return html`

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

`; + return html` ${this.name} ${this.desc === null ? '' : html`—`} + ${this.desc} ${this.renderSubNetworkSpecs()}`; } private renderIedContainer(): TemplateResult[] { @@ -116,8 +94,7 @@ export class SubNetworkEditor extends LitElement { .filter((v, i, a) => a.indexOf(v) === i) .sort(compareNames) .map( - iedName => html`
-

${iedName}

+ iedName => html`
${Array.from( this.element.ownerDocument.querySelectorAll( @@ -133,15 +110,31 @@ export class SubNetworkEditor extends LitElement { >` )}
-
` + ` ); } render(): TemplateResult { - return html`
- ${this.renderHeader()} + return html` + + this.openEditWizard()} + > + + + this.remove()} + > + +
${this.renderIedContainer()}
-
`; + `; } static styles = css` diff --git a/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js b/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js index 4f71771fea..5ab43db715 100644 --- a/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js +++ b/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js @@ -2,40 +2,40 @@ export const snapshots = {}; snapshots["subnetwork-editor looks like the latest snapshot"] = -`
-

- StationBus — desc - (8-MMS—100.0b/s) - - - - - -

+` + + + + + + + + + + + +
-
-

- IED1 -

-
+
-
+ `; /* end snapshot subnetwork-editor looks like the latest snapshot */ From 6cb0da0ea38076e5ec06c4cee5ee12803ea6f5e4 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Thu, 13 Jan 2022 15:55:04 +0100 Subject: [PATCH 04/12] refactor(editors/communication): minor code improvements --- src/editors/Communication.ts | 7 ++--- .../communication/subnetwork-editor.ts | 26 +++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/editors/Communication.ts b/src/editors/Communication.ts index b981348f64..17a4ce51f2 100644 --- a/src/editors/Communication.ts +++ b/src/editors/Communication.ts @@ -3,13 +3,13 @@ import { translate, get } from 'lit-translate'; import '@material/mwc-fab'; +import './communication/subnetwork-editor.js'; import { newWizardEvent, newActionEvent, createElement, } from '../foundation.js'; import { selectors } from './communication/foundation.js'; -import './communication/subnetwork-editor.js'; import { subNetworkWizard } from '../wizards/subnetwork.js'; /** An editor [[`plugin`]] for editing the `Communication` section. */ @@ -18,7 +18,7 @@ export default class CommunicationPlugin extends LitElement { @property() doc!: XMLDocument; - createCommunication(): void { + private createCommunication(): void { this.dispatchEvent( newActionEvent({ new: { @@ -30,7 +30,7 @@ export default class CommunicationPlugin extends LitElement { } /** Opens a [[`WizardDialog`]] for creating a new `SubNetwork` element. */ - openCreateSubNetworkWizard(): void { + private openCreateSubNetworkWizard(): void { if (!this.doc.querySelector(selectors.Communication)) this.createCommunication(); @@ -55,6 +55,7 @@ export default class CommunicationPlugin extends LitElement { @click=${() => this.openCreateSubNetworkWizard()} > `; + return html` connAP.getAttribute('iedName')!) @@ -114,6 +101,19 @@ export class SubNetworkEditor extends LitElement { ); } + private renderSubNetworkSpecs(): TemplateResult { + if (!this.type && !this.bitrate) return html``; + + return html`(${this.type}${this.type && this.bitrate + ? html`—` + : html``}${this.bitrate})`; + } + + private renderHeader(): TemplateResult { + return html` ${this.name} ${this.desc === null ? '' : html`—`} + ${this.desc} ${this.renderSubNetworkSpecs()}`; + } + render(): TemplateResult { return html` From e07a49413aca26b6cb43d34b6b62de6b6f538296 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Thu, 13 Jan 2022 16:03:48 +0100 Subject: [PATCH 05/12] refactor(editors/foundation): remove selectors --- src/editors/Communication.ts | 20 ++++++++++++-------- src/editors/communication/foundation.ts | 22 ---------------------- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/src/editors/Communication.ts b/src/editors/Communication.ts index 17a4ce51f2..532d897776 100644 --- a/src/editors/Communication.ts +++ b/src/editors/Communication.ts @@ -8,8 +8,8 @@ import { newWizardEvent, newActionEvent, createElement, + isPublic, } from '../foundation.js'; -import { selectors } from './communication/foundation.js'; import { subNetworkWizard } from '../wizards/subnetwork.js'; /** An editor [[`plugin`]] for editing the `Communication` section. */ @@ -31,20 +31,20 @@ export default class CommunicationPlugin extends LitElement { /** Opens a [[`WizardDialog`]] for creating a new `SubNetwork` element. */ private openCreateSubNetworkWizard(): void { - if (!this.doc.querySelector(selectors.Communication)) + if (!this.doc.querySelector(':root > Communication')) this.createCommunication(); this.dispatchEvent( newWizardEvent( subNetworkWizard({ - parent: this.doc.querySelector('Communication')!, + parent: this.doc.querySelector(':root > Communication')!, }) ) ); } render(): TemplateResult { - if (!this.doc?.querySelector(selectors.SubNetwork)) + if (!this.doc?.querySelector(':root > Communication >SubNetwork')) return html`

${translate('communication.missing')} this.openCreateSubNetworkWizard()} >
- ${Array.from(this.doc.querySelectorAll(selectors.SubNetwork) ?? []).map( - subnetwork => - html`` - )} + ${Array.from(this.doc.querySelectorAll('SubNetwork') ?? []) + .filter(isPublic) + .map( + subnetwork => + html`` + )}
`; } diff --git a/src/editors/communication/foundation.ts b/src/editors/communication/foundation.ts index 0006a19b6f..aa04df9d4d 100644 --- a/src/editors/communication/foundation.ts +++ b/src/editors/communication/foundation.ts @@ -18,28 +18,6 @@ export function isCreateOptions( return (options).parent !== undefined; } -// Communication element hierarchy -const substationPath = [ - ':root', - 'Communication', - 'SubNetwork', - 'ConnectedAP', - 'Address', -]; - -export type CommunicationTag = - | 'Communication' - | 'SubNetwork' - | 'ConnectedAP' - | 'Address'; - -/** `Private`-safeguarded selectors for `Communication` and its descendants */ -export const selectors = >( - Object.fromEntries( - substationPath.map((e, i, a) => [e, a.slice(0, i + 1).join(' > ')]) - ) -); - /** Common `CSS` styles used by communication subeditors */ export const styles = css` :host(.moving) section { From a46c677076efd1c41cae25d3a031885cacd2c4c3 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Thu, 13 Jan 2022 16:07:46 +0100 Subject: [PATCH 06/12] refactor(editors/communication/subnetwork-editor): fail save selector --- src/editors/communication/subnetwork-editor.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/editors/communication/subnetwork-editor.ts b/src/editors/communication/subnetwork-editor.ts index 17961e0929..90dd2ca7c9 100644 --- a/src/editors/communication/subnetwork-editor.ts +++ b/src/editors/communication/subnetwork-editor.ts @@ -76,7 +76,9 @@ export class SubNetworkEditor extends LitElement { } private renderIedContainer(): TemplateResult[] { - return Array.from(this.element.querySelectorAll('ConnectedAP') ?? []) + return Array.from( + this.element.querySelectorAll(':scope > ConnectedAP') ?? [] + ) .map(connAP => connAP.getAttribute('iedName')!) .filter((v, i, a) => a.indexOf(v) === i) .sort(compareNames) @@ -84,9 +86,9 @@ export class SubNetworkEditor extends LitElement { iedName => html`
${Array.from( - this.element.ownerDocument.querySelectorAll( - `ConnectedAP[iedName="${iedName}"]` - ) + this.element.parentElement?.querySelectorAll( + `:scope > SubNetwork > ConnectedAP[iedName="${iedName}"]` + ) ?? [] ).map( connectedAP => html` Date: Fri, 14 Jan 2022 11:39:35 +0100 Subject: [PATCH 07/12] refactor(wizards/subnetwork): update edit wizard --- src/wizards/subnetwork.ts | 236 ++++++++++------ src/wizards/wizard-library.ts | 15 +- .../subnetwork-editor-wizarding.test.snap.js | 4 +- test/testfiles/valid2003.scd | 2 +- .../editors/communication/SubNetwork.test.ts | 174 +----------- .../__snapshots__/subnetwork.test.snap.js | 122 +++++++++ test/unit/wizards/subnetwork.test.ts | 255 ++++++++++++++++++ 7 files changed, 546 insertions(+), 262 deletions(-) create mode 100644 test/unit/wizards/__snapshots__/subnetwork.test.snap.js create mode 100644 test/unit/wizards/subnetwork.test.ts diff --git a/src/wizards/subnetwork.ts b/src/wizards/subnetwork.ts index 43705cba5b..56c86c21f6 100644 --- a/src/wizards/subnetwork.ts +++ b/src/wizards/subnetwork.ts @@ -1,4 +1,4 @@ -import { html } from 'lit-element'; +import { html, TemplateResult } from 'lit-element'; import { get, translate } from 'lit-translate'; import '../wizard-textfield.js'; @@ -25,90 +25,50 @@ const initial = { multiplier: 'M', }; -function getBitRateAction( - oldBitRate: Element | null, - BitRate: string | null, - multiplier: string | null, - SubNetwork: Element -): EditorAction { - if (oldBitRate === null) - return { - new: { - parent: SubNetwork, - element: new DOMParser().parseFromString( - `${BitRate === null ? '' : BitRate}`, - 'application/xml' - ).documentElement, - reference: SubNetwork.firstElementChild, - }, - }; - - if (BitRate === null) - return { - old: { - parent: SubNetwork, - element: oldBitRate, - reference: oldBitRate.nextSibling, - }, - }; - - const newBitRate = cloneElement(oldBitRate, { multiplier }); - newBitRate.textContent = BitRate; - - return { - old: { element: oldBitRate }, - new: { element: newBitRate }, - }; +interface ContentOptions { + name: string | null; + desc: string | null; + type: string | null; + BitRate: string | null; + multiplier: string | null; } -export function updateSubNetworkAction(element: Element): WizardActor { - return (inputs: WizardInput[], wizard: Element): EditorAction[] => { - const name = inputs.find(i => i.label === 'name')!.value!; - const desc = getValue(inputs.find(i => i.label === 'desc')!); - const type = getValue(inputs.find(i => i.label === 'type')!); - const BitRate = getValue(inputs.find(i => i.label === 'BitRate')!); - const multiplier = getMultiplier(inputs.find(i => i.label === 'BitRate')!); - - let subNetworkAction: EditorAction | null; - let bitRateAction: EditorAction | null; - - if ( - name === element.getAttribute('name') && - desc === element.getAttribute('desc') && - type === element.getAttribute('type') - ) { - subNetworkAction = null; - } else { - const newElement = cloneElement(element, { name, desc, type }); - subNetworkAction = { old: { element }, new: { element: newElement } }; - } - - if ( - BitRate === - (element.querySelector('SubNetwork > BitRate')?.textContent?.trim() ?? - null) && - multiplier === - (element - .querySelector('SubNetwork > BitRate') - ?.getAttribute('multiplier') ?? null) - ) { - bitRateAction = null; - } else { - bitRateAction = getBitRateAction( - element.querySelector('SubNetwork > BitRate'), - BitRate, - multiplier, - subNetworkAction?.new.element ?? element - ); - } - - const actions: EditorAction[] = []; - if (subNetworkAction) actions.push(subNetworkAction); - if (bitRateAction) actions.push(bitRateAction); - return actions; - }; +function contentSubNetwork(options: ContentOptions): TemplateResult[] { + return [ + html``, + html``, + html``, + html``, + ]; } export function createSubNetworkAction(parent: Element): WizardActor { @@ -234,3 +194,113 @@ export function subNetworkWizard(options: WizardOptions): Wizard { }, ]; } + +function getBitRateAction( + oldBitRate: Element | null, + BitRate: string | null, + multiplier: string | null, + SubNetwork: Element +): EditorAction { + if (oldBitRate === null) + return { + new: { + parent: SubNetwork, + element: new DOMParser().parseFromString( + `${BitRate === null ? '' : BitRate}`, + 'application/xml' + ).documentElement, + reference: SubNetwork.firstElementChild, + }, + }; + + if (BitRate === null) + return { + old: { + parent: SubNetwork, + element: oldBitRate, + reference: oldBitRate.nextSibling, + }, + }; + + const newBitRate = cloneElement(oldBitRate, { multiplier }); + newBitRate.textContent = BitRate; + + return { + old: { element: oldBitRate }, + new: { element: newBitRate }, + }; +} + +function updateSubNetworkAction(element: Element): WizardActor { + return (inputs: WizardInput[]): EditorAction[] => { + const name = inputs.find(i => i.label === 'name')!.value!; + const desc = getValue(inputs.find(i => i.label === 'desc')!); + const type = getValue(inputs.find(i => i.label === 'type')!); + const BitRate = getValue(inputs.find(i => i.label === 'BitRate')!); + const multiplier = getMultiplier(inputs.find(i => i.label === 'BitRate')!); + + let subNetworkAction: EditorAction | null; + let bitRateAction: EditorAction | null; + + if ( + name === element.getAttribute('name') && + desc === element.getAttribute('desc') && + type === element.getAttribute('type') + ) { + subNetworkAction = null; + } else { + const newElement = cloneElement(element, { name, desc, type }); + subNetworkAction = { old: { element }, new: { element: newElement } }; + } + + if ( + BitRate === + (element.querySelector('SubNetwork > BitRate')?.textContent?.trim() ?? + null) && + multiplier === + (element + .querySelector('SubNetwork > BitRate') + ?.getAttribute('multiplier') ?? null) + ) { + bitRateAction = null; + } else { + bitRateAction = getBitRateAction( + element.querySelector('SubNetwork > BitRate'), + BitRate, + multiplier, + subNetworkAction?.new.element ?? element + ); + } + + const actions: EditorAction[] = []; + if (subNetworkAction) actions.push(subNetworkAction); + if (bitRateAction) actions.push(bitRateAction); + return actions; + }; +} + +export function editSubNetworkWizard(element: Element): Wizard { + const name = element.getAttribute('name'); + const desc = element.getAttribute('desc'); + const type = element.getAttribute('type'); + const BitRate = + element.querySelector('SubNetwork > BitRate')?.textContent?.trim() ?? null; + const multiplier = + element.querySelector('SubNetwork > BitRate')?.getAttribute('multiplier') ?? + null; + + return [ + { + title: get('wizard.title.edit', { tagName: element.tagName }), + element, + primary: { + icon: 'save', + label: get('save'), + action: updateSubNetworkAction(element), + }, + content: contentSubNetwork({ name, desc, type, BitRate, multiplier }), + }, + ]; +} diff --git a/src/wizards/wizard-library.ts b/src/wizards/wizard-library.ts index f700809f1e..e7233cb513 100644 --- a/src/wizards/wizard-library.ts +++ b/src/wizards/wizard-library.ts @@ -1,14 +1,21 @@ import { SCLTag, Wizard } from '../foundation.js'; import { createBayWizard, editBayWizard } from './bay.js'; -import { createConductingEquipmentWizard, editConductingEquipmentWizard} from './conductingequipment.js'; +import { + createConductingEquipmentWizard, + editConductingEquipmentWizard, +} from './conductingequipment.js'; import { editConnectivityNodeWizard } from './connectivitynode.js'; import { createFCDAsWizard } from './fcda.js'; import { lNodeWizard } from './lnode.js'; import { createSubstationWizard, substationEditWizard } from './substation.js'; import { editTerminalWizard } from './terminal.js'; -import { voltageLevelCreateWizard, voltageLevelEditWizard } from './voltagelevel.js'; -import { editPowerTransformerWizard } from "./powertransformer.js"; +import { + voltageLevelCreateWizard, + voltageLevelEditWizard, +} from './voltagelevel.js'; +import { editPowerTransformerWizard } from './powertransformer.js'; +import { editSubNetworkWizard } from './subnetwork.js'; type SclElementWizard = (element: Element) => Wizard | undefined; @@ -460,7 +467,7 @@ export const wizards: Record< create: emptyWizard, }, SubNetwork: { - edit: emptyWizard, + edit: editSubNetworkWizard, create: emptyWizard, }, Subject: { diff --git a/test/integration/editors/communication/__snapshots__/subnetwork-editor-wizarding.test.snap.js b/test/integration/editors/communication/__snapshots__/subnetwork-editor-wizarding.test.snap.js index d9a7bc7275..0c1f2978cb 100644 --- a/test/integration/editors/communication/__snapshots__/subnetwork-editor-wizarding.test.snap.js +++ b/test/integration/editors/communication/__snapshots__/subnetwork-editor-wizarding.test.snap.js @@ -4,7 +4,7 @@ export const snapshots = {}; snapshots["subnetwork-editor wizarding integration edit/add Subnetwork wizard looks like the latest snapshot"] = `
@@ -49,7 +49,7 @@ snapshots["subnetwork-editor wizarding integration edit/add Subnetwork wizard lo - + 100.0
diff --git a/test/unit/editors/communication/SubNetwork.test.ts b/test/unit/editors/communication/SubNetwork.test.ts index cd4b76fd02..56617a4d5e 100644 --- a/test/unit/editors/communication/SubNetwork.test.ts +++ b/test/unit/editors/communication/SubNetwork.test.ts @@ -1,16 +1,8 @@ import { fixture, html, expect } from '@open-wc/testing'; import '../../../../src/wizard-textfield.js'; -import { - WizardInput, - isCreate, - isUpdate, - isDelete, -} from '../../../../src/foundation.js'; -import { - createSubNetworkAction, - updateSubNetworkAction, -} from '../../../../src/wizards/subnetwork.js'; +import { WizardInput, isCreate } from '../../../../src/foundation.js'; +import { createSubNetworkAction } from '../../../../src/wizards/subnetwork.js'; describe('SubNetworkEditor', () => { describe('with no nulled properties', () => { @@ -51,167 +43,5 @@ describe('SubNetworkEditor', () => { expect(wizardAction(inputs, newWizard())[0]).to.satisfy(isCreate); }); }); - - describe('has an updateAction that', () => { - let element: Element; - beforeEach(() => { - element = new DOMParser().parseFromString( - '', - 'application/xml' - ).documentElement; - }); - - describe('with missing child element BitRate', () => { - let element: Element; - beforeEach(() => { - element = new DOMParser().parseFromString( - '', - 'application/xml' - ).documentElement; - }); - - it('returns a WizardAction which retruns two EditorActions', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard()).length).to.equal(2); - }); - - it('returns a WizardAction with the first returned EditorAction beeing an Update', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard())[0]).to.satisfy(isUpdate); - }); - - it('returns a WizardAction with the second returned EditorAction beeing a Create', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard())[1]).to.satisfy(isCreate); - }); - }); - - describe('with present child element BitRate', () => { - let element: Element; - beforeEach(() => { - element = new DOMParser().parseFromString( - ` - 100 - `, - 'application/xml' - ).documentElement; - }); - - it('returns a WizardAction which returns two EditorActions', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard()).length).to.equal(2); - }); - - it('returns a WizardAction with the first returned EditorAction beeing an Update', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard())[0]).to.satisfy(isUpdate); - }); - - it('returns a WizardAction with the second returned EditorAction beeing a Update', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard())[1]).to.satisfy(isUpdate); - }); - }); - - describe('with no change in element SubNetwork but changes in the child element BitRate', () => { - let element: Element; - beforeEach(() => { - element = new DOMParser().parseFromString( - ` - 100 - `, - 'application/xml' - ).documentElement; - }); - - it('returns a WizardAction which returns one EditorActions', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard()).length).to.equal(1); - }); - - it('returns a WizardAction with the first returned EditorAction beeing an Update', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard())[0]).to.satisfy(isUpdate); - }); - }); - - describe('with no change in SubNetwork nor BitRate', () => { - let element: Element; - beforeEach(() => { - element = new DOMParser().parseFromString( - '', - 'application/xml' - ).documentElement; - }); - - it('returns a WizardAction with an empty EditorActions array', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard()).length).to.equal(0); - }); - }); - }); - }); - - describe('with nulled properties', () => { - const noOp = () => { - return; - }; - const newWizard = (done = noOp) => { - const element = document.createElement('mwc-dialog'); - element.close = done; - return element; - }; - - let inputs: WizardInput[]; - beforeEach(async () => { - inputs = await Promise.all( - ['name', 'desc', 'type', 'BitRate'].map( - label => - >( - fixture( - html`` - ) - ) - ) - ); - }); - - describe('has an updateAction that', () => { - describe('with present child element Voltage', () => { - let element: Element; - beforeEach(async () => { - element = new DOMParser().parseFromString( - ` - 100 - `, - 'application/xml' - ).documentElement; - - inputs[3] = await fixture(html``); - }); - - it('returns a WizardAction which returns two EditorActions', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard()).length).to.equal(2); - }); - - it('returns a WizardAction with the first returned EditorAction beeing an Update', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard())[0]).to.satisfy(isUpdate); - }); - - it('returns a WizardAction with the second returned EditorAction beeing a Delete', () => { - const wizardAction = updateSubNetworkAction(element); - expect(wizardAction(inputs, newWizard())[1]).to.satisfy(isDelete); - }); - }); - }); }); }); diff --git a/test/unit/wizards/__snapshots__/subnetwork.test.snap.js b/test/unit/wizards/__snapshots__/subnetwork.test.snap.js new file mode 100644 index 0000000000..aededb3509 --- /dev/null +++ b/test/unit/wizards/__snapshots__/subnetwork.test.snap.js @@ -0,0 +1,122 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Wizards for SCL element SubNetwork include an edit wizard that with existing BitRate child element looks like the latest snapshot"] = +` +
+ + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element SubNetwork include an edit wizard that with existing BitRate child element looks like the latest snapshot */ + +snapshots["Wizards for SCL element SubNetwork include an edit wizard that with missing BitRate child element looks like the latest snapshot"] = +` +
+ + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element SubNetwork include an edit wizard that with missing BitRate child element looks like the latest snapshot */ + diff --git a/test/unit/wizards/subnetwork.test.ts b/test/unit/wizards/subnetwork.test.ts new file mode 100644 index 0000000000..d664e24c5a --- /dev/null +++ b/test/unit/wizards/subnetwork.test.ts @@ -0,0 +1,255 @@ +import { expect, fixture, html } from '@open-wc/testing'; +import { SinonSpy, spy } from 'sinon'; + +import '../../mock-wizard.js'; +import { MockWizard } from '../../mock-wizard.js'; + +import { WizardTextField } from '../../../src/wizard-textfield.js'; +import { + isCreate, + isDelete, + WizardInput, + isUpdate, + Update, + Delete, + Create, +} from '../../../src/foundation.js'; +import { editSubNetworkWizard } from '../../../src/wizards/subnetwork.js'; + +describe('Wizards for SCL element SubNetwork', () => { + let doc: XMLDocument; + let element: MockWizard; + let inputs: WizardInput[]; + let input: WizardInput | undefined; + let primaryAction: HTMLElement; + + let actionEvent: SinonSpy; + + beforeEach(async () => { + element = await fixture(html``); + + actionEvent = spy(); + window.addEventListener('editor-action', actionEvent); + }); + + describe('include an edit wizard that', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/valid2003.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + }); + + describe('with existing BitRate child element', () => { + beforeEach(async () => { + const wizard = editSubNetworkWizard(doc.querySelector('SubNetwork')!); + element.workflow.push(() => wizard); + await element.requestUpdate(); + + inputs = Array.from(element.wizardUI.inputs); + + primaryAction = ( + element.wizardUI.dialog?.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + }); + + it('looks like the latest snapshot', async () => { + await expect(element.wizardUI.dialog).dom.to.equalSnapshot(); + }); + + it('does not edit any attributes with unchanged wizard inputs', async () => { + primaryAction.click(); + await element.requestUpdate(); + expect(actionEvent).to.not.have.been.called; + }); + + it('triggers an editor action to update name attribute', async () => { + input = inputs.find(input => input.label === 'name'); + input.value = 'newSubNetName'; + await input.requestUpdate(); + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isUpdate); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.old.element).to.have.a.attribute( + 'name', + 'StationBus' + ); + expect(updateAction.new.element).to.have.a.attribute( + 'name', + 'newSubNetName' + ); + }); + + it('triggers an editor action to update desc attribute', async () => { + input = inputs.find(input => input.label === 'desc'); + await input.requestUpdate(); + + (input).nullSwitch?.click(); + input.value = 'myNewSubNetworkDesc'; + await input.requestUpdate(); + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isUpdate); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.old.element).to.not.have.a.attribute('desc'); + expect(updateAction.new.element).to.have.a.attribute( + 'desc', + 'myNewSubNetworkDesc' + ); + }); + + it('triggers an editor action to update type attribute', async () => { + input = inputs.find(input => input.label === 'type'); + input.value = 'myNewSubNetType'; + await input.requestUpdate(); + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isUpdate); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.old.element).to.have.a.attribute('type', '8-MMS'); + expect(updateAction.new.element).to.have.a.attribute( + 'type', + 'myNewSubNetType' + ); + }); + + it('triggers an editor action to update BitRate element', async () => { + input = ( + inputs.find(input => input.label === 'BitRate') + ); + input.value = '200.'; + (input).multiplier = 'M'; + await input.requestUpdate(); + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isUpdate); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.old.element.innerHTML.trim()).to.equal('100.0'); + expect(updateAction.old.element).to.not.have.attribute('multiplier'); + expect(updateAction.new.element.innerHTML.trim()).to.equal('200.'); + expect(updateAction.new.element).to.have.attribute('multiplier', 'M'); + }); + + it('triggers an editor action to remove BitRate element', async () => { + input = ( + inputs.find(input => input.label === 'BitRate') + ); + await input.requestUpdate(); + + (input).nullSwitch?.click(); + await input.requestUpdate(); + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isDelete); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.old.element.innerHTML.trim()).to.equal('100.0'); + expect(updateAction.old.element).to.not.have.attribute('multiplier'); + }); + }); + + describe('with missing BitRate child element', () => { + beforeEach(async () => { + const wizard = editSubNetworkWizard( + doc.querySelector('SubNetwork[name="ProcessBus"]')! + ); + element.workflow.push(() => wizard); + await element.requestUpdate(); + + inputs = Array.from(element.wizardUI.inputs); + + primaryAction = ( + element.wizardUI.dialog?.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + }); + + it('looks like the latest snapshot', async () => { + await expect(element.wizardUI.dialog).dom.to.equalSnapshot(); + }); + + it('triggers an editor action to create a complete BitRate element', async () => { + input = ( + inputs.find(input => input.label === 'BitRate') + ); + await input.requestUpdate(); + + (input).nullSwitch?.click(); + (input).value = '100.0'; + (input).multiplier = 'M'; + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isCreate); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.new.element.innerHTML.trim()).to.equal('100.0'); + expect(updateAction.new.element).to.have.attribute('multiplier', 'M'); + }); + + it('triggers an editor action to create BitRate element with multiplier only', async () => { + input = ( + inputs.find(input => input.label === 'BitRate') + ); + await input.requestUpdate(); + + (input).nullSwitch?.click(); + (input).multiplier = 'M'; + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isCreate); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.new.element.innerHTML.trim()).to.equal(''); + expect(updateAction.new.element).to.have.attribute('multiplier'); + }); + + it('triggers an editor action to create BitRate element with bit rate only', async () => { + input = ( + inputs.find(input => input.label === 'BitRate') + ); + await input.requestUpdate(); + + (input).nullSwitch?.click(); + (input).value = '100.0'; + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isCreate); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.new.element.innerHTML.trim()).to.equal('100.0'); + expect(updateAction.new.element).to.not.have.attribute('multiplier'); + }); + }); + }); +}); From 36d21dfa0fa037bec55dd8772131c331311aa911 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Fri, 14 Jan 2022 13:34:08 +0100 Subject: [PATCH 08/12] refactor(wizards/subnetwork): create wizard and its tests --- src/editors/Communication.ts | 14 +-- src/editors/communication/foundation.ts | 18 --- .../communication/subnetwork-editor.ts | 8 +- src/wizards/subnetwork.ts | 100 ++------------- .../editors/communication/SubNetwork.test.ts | 47 ------- .../communication/subnetwork-editor.test.ts | 5 +- .../__snapshots__/subnetwork.test.snap.js | 116 ++++++++++++++++++ test/unit/wizards/subnetwork.test.ts | 106 +++++++++++++++- 8 files changed, 244 insertions(+), 170 deletions(-) delete mode 100644 test/unit/editors/communication/SubNetwork.test.ts diff --git a/src/editors/Communication.ts b/src/editors/Communication.ts index 532d897776..cf5ea64b73 100644 --- a/src/editors/Communication.ts +++ b/src/editors/Communication.ts @@ -10,7 +10,7 @@ import { createElement, isPublic, } from '../foundation.js'; -import { subNetworkWizard } from '../wizards/subnetwork.js'; +import { createSubNetworkWizard } from '../wizards/subnetwork.js'; /** An editor [[`plugin`]] for editing the `Communication` section. */ export default class CommunicationPlugin extends LitElement { @@ -31,16 +31,10 @@ export default class CommunicationPlugin extends LitElement { /** Opens a [[`WizardDialog`]] for creating a new `SubNetwork` element. */ private openCreateSubNetworkWizard(): void { - if (!this.doc.querySelector(':root > Communication')) - this.createCommunication(); + const parent = this.doc.querySelector(':root > Communication'); + if (!parent) this.createCommunication(); - this.dispatchEvent( - newWizardEvent( - subNetworkWizard({ - parent: this.doc.querySelector(':root > Communication')!, - }) - ) - ); + this.dispatchEvent(newWizardEvent(createSubNetworkWizard(parent!))); } render(): TemplateResult { diff --git a/src/editors/communication/foundation.ts b/src/editors/communication/foundation.ts index aa04df9d4d..232f0422ad 100644 --- a/src/editors/communication/foundation.ts +++ b/src/editors/communication/foundation.ts @@ -1,23 +1,5 @@ import { css } from 'lit-element'; -export type ElementEditor = Element & { - element: Element; -}; - -interface UpdateOptions { - element: Element; -} -interface CreateOptions { - parent: Element; -} -export type WizardOptions = UpdateOptions | CreateOptions; - -export function isCreateOptions( - options: WizardOptions -): options is CreateOptions { - return (options).parent !== undefined; -} - /** Common `CSS` styles used by communication subeditors */ export const styles = css` :host(.moving) section { diff --git a/src/editors/communication/subnetwork-editor.ts b/src/editors/communication/subnetwork-editor.ts index 90dd2ca7c9..13730a930a 100644 --- a/src/editors/communication/subnetwork-editor.ts +++ b/src/editors/communication/subnetwork-editor.ts @@ -18,7 +18,8 @@ import { } from '../../foundation.js'; import { styles } from './foundation.js'; import { createConnectedApWizard } from '../../wizards/connectedap.js'; -import { subNetworkWizard } from '../../wizards/subnetwork.js'; +import { editSubNetworkWizard } from '../../wizards/subnetwork.js'; +import { wizards } from '../../wizards/wizard-library.js'; /** [[`Communication`]] subeditor for a `SubNetwork` element. */ @customElement('subnetwork-editor') @@ -57,9 +58,8 @@ export class SubNetworkEditor extends LitElement { } private openEditWizard(): void { - this.dispatchEvent( - newWizardEvent(subNetworkWizard({ element: this.element })) - ); + const wizard = wizards['SubNetwork'].edit(this.element); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); } remove(): void { diff --git a/src/wizards/subnetwork.ts b/src/wizards/subnetwork.ts index 56c86c21f6..47f3f8f47b 100644 --- a/src/wizards/subnetwork.ts +++ b/src/wizards/subnetwork.ts @@ -13,10 +13,6 @@ import { WizardActor, WizardInput, } from '../foundation.js'; -import { - isCreateOptions, - WizardOptions, -} from '../editors/communication/foundation.js'; /** Initial attribute values suggested for `SubNetwork` creation */ const initial = { @@ -72,7 +68,7 @@ function contentSubNetwork(options: ContentOptions): TemplateResult[] { } export function createSubNetworkAction(parent: Element): WizardActor { - return (inputs: WizardInput[], wizard: Element): EditorAction[] => { + return (inputs: WizardInput[]): EditorAction[] => { const name = getValue(inputs.find(i => i.label === 'name')!); const desc = getValue(inputs.find(i => i.label === 'desc')!); const type = getValue(inputs.find(i => i.label === 'type')!); @@ -105,92 +101,22 @@ export function createSubNetworkAction(parent: Element): WizardActor { }; } -export function subNetworkWizard(options: WizardOptions): Wizard { - const [ - heading, - actionName, - actionIcon, - action, - name, - desc, - type, - BitRate, - multiplier, - element, - ] = isCreateOptions(options) - ? [ - get('subnetwork.wizard.title.add'), - get('add'), - 'add', - createSubNetworkAction(options.parent), - '', - '', - initial.type, - initial.bitrate, - initial.multiplier, - undefined, - ] - : [ - get('subnetwork.wizard.title.edit'), - get('save'), - 'edit', - updateSubNetworkAction(options.element), - options.element.getAttribute('name'), - options.element.getAttribute('desc'), - options.element.getAttribute('type'), - options.element - .querySelector('SubNetwork > BitRate') - ?.textContent?.trim() ?? null, - options.element - .querySelector('SubNetwork > BitRate') - ?.getAttribute('multiplier') ?? null, - options.element, - ]; - +export function createSubNetworkWizard(parent: Element): Wizard { return [ { - title: heading, - element, + title: get('wizard.title.create', { tagName: 'SubNetwork' }), primary: { - icon: actionIcon, - label: actionName, - action: action, + icon: 'add', + label: get('add'), + action: createSubNetworkAction(parent), }, - content: [ - html``, - html``, - html``, - html``, - ], + content: contentSubNetwork({ + name: '', + desc: '', + type: initial.type, + BitRate: initial.bitrate, + multiplier: initial.multiplier, + }), }, ]; } diff --git a/test/unit/editors/communication/SubNetwork.test.ts b/test/unit/editors/communication/SubNetwork.test.ts deleted file mode 100644 index 56617a4d5e..0000000000 --- a/test/unit/editors/communication/SubNetwork.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { fixture, html, expect } from '@open-wc/testing'; - -import '../../../../src/wizard-textfield.js'; -import { WizardInput, isCreate } from '../../../../src/foundation.js'; -import { createSubNetworkAction } from '../../../../src/wizards/subnetwork.js'; - -describe('SubNetworkEditor', () => { - describe('with no nulled properties', () => { - const noOp = () => { - return; - }; - const newWizard = (done = noOp) => { - const element = document.createElement('mwc-dialog'); - element.close = done; - return element; - }; - - let inputs: WizardInput[]; - beforeEach(async () => { - inputs = await Promise.all( - ['name', 'desc', 'type', 'BitRate'].map( - label => - >( - fixture( - html`` - ) - ) - ) - ); - }); - - describe('has a createAction that', () => { - let parent: Element; - beforeEach(() => { - parent = new DOMParser().parseFromString( - '', - 'application/xml' - ).documentElement; - }); - - it('returns a WizardAction which returns a Create EditorAction', () => { - const wizardAction = createSubNetworkAction(parent); - expect(wizardAction(inputs, newWizard())[0]).to.satisfy(isCreate); - }); - }); - }); -}); diff --git a/test/unit/editors/communication/subnetwork-editor.test.ts b/test/unit/editors/communication/subnetwork-editor.test.ts index 18b1152256..2ad9aa5e26 100644 --- a/test/unit/editors/communication/subnetwork-editor.test.ts +++ b/test/unit/editors/communication/subnetwork-editor.test.ts @@ -28,7 +28,6 @@ describe('subnetwork-editor', () => { it('has a type property', () => expect(element).to.have.property('type', '8-MMS')); - it('looks like the latest snapshot', async () => { - await expect(element).shadowDom.to.equalSnapshot(); - }); + it('looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); }); diff --git a/test/unit/wizards/__snapshots__/subnetwork.test.snap.js b/test/unit/wizards/__snapshots__/subnetwork.test.snap.js index aededb3509..867ece682d 100644 --- a/test/unit/wizards/__snapshots__/subnetwork.test.snap.js +++ b/test/unit/wizards/__snapshots__/subnetwork.test.snap.js @@ -120,3 +120,119 @@ snapshots["Wizards for SCL element SubNetwork include an edit wizard that with m `; /* end snapshot Wizards for SCL element SubNetwork include an edit wizard that with missing BitRate child element looks like the latest snapshot */ +snapshots["Wizards for SCL element SubNetwork include an edit wizard that looks like the latest snapshot"] = +` +
+ + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element SubNetwork include an edit wizard that looks like the latest snapshot */ + +snapshots["Wizards for SCL element SubNetwork include an create wizard that looks like the latest snapshot"] = +` +
+ + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element SubNetwork include an create wizard that looks like the latest snapshot */ + diff --git a/test/unit/wizards/subnetwork.test.ts b/test/unit/wizards/subnetwork.test.ts index d664e24c5a..9e694f2a0e 100644 --- a/test/unit/wizards/subnetwork.test.ts +++ b/test/unit/wizards/subnetwork.test.ts @@ -14,7 +14,10 @@ import { Delete, Create, } from '../../../src/foundation.js'; -import { editSubNetworkWizard } from '../../../src/wizards/subnetwork.js'; +import { + createSubNetworkWizard, + editSubNetworkWizard, +} from '../../../src/wizards/subnetwork.js'; describe('Wizards for SCL element SubNetwork', () => { let doc: XMLDocument; @@ -252,4 +255,105 @@ describe('Wizards for SCL element SubNetwork', () => { }); }); }); + + describe('include an create wizard that', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/valid2003.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + const wizard = createSubNetworkWizard( + doc.querySelector('Communication')! + ); + element.workflow.push(() => wizard); + await element.requestUpdate(); + + inputs = Array.from(element.wizardUI.inputs); + + primaryAction = ( + element.wizardUI.dialog?.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + }); + + it('looks like the latest snapshot', async () => { + await expect(element.wizardUI.dialog).dom.to.equalSnapshot(); + }); + + it('does not allow creating SubNetwork with empty name attribute', async () => { + input = inputs.find(input => input.label === 'name'); + input.value = ''; + await input.requestUpdate(); + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.not.be.called; + }); + + it('triggers an editor action to create SubNetwork element including BitRate', async () => { + input = inputs.find(input => input.label === 'name'); + input.value = 'myNewSubNetworkName'; + await input.requestUpdate(); + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isCreate); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.new.element).to.have.a.attribute( + 'name', + 'myNewSubNetworkName' + ); + expect(updateAction.new.element).to.have.a.attribute('desc', ''); + expect(updateAction.new.element).to.have.a.attribute('type', '8-MMS'); + expect(updateAction.new.element.querySelector('BitRate')).to.exist; + expect( + updateAction.new.element.querySelector('BitRate') + ).to.have.attribute('multiplier', 'M'); + expect( + updateAction.new.element.querySelector('BitRate')?.textContent?.trim() + ).to.equal('100'); + }); + + it('triggers an editor action to create SubNetwork element excluding non required /BitRate', async () => { + const name = ( + inputs.find(input => input.label === 'name') + ); + const desc = ( + inputs.find(input => input.label === 'desc') + ); + const type = ( + inputs.find(input => input.label === 'type') + ); + const bitrate = ( + inputs.find(input => input.label === 'BitRate') + ); + await element.requestUpdate(); + + desc.nullSwitch?.click(); + type.nullSwitch?.click(); + bitrate.nullSwitch?.click(); + name.value = 'myNewSubNetworkName'; + await name.requestUpdate(); + + primaryAction.click(); + await element.requestUpdate(); + + expect(actionEvent).to.be.calledOnce; + expect(actionEvent.args[0][0].detail.action).to.satisfy(isCreate); + + const updateAction = actionEvent.args[0][0].detail.action; + expect(updateAction.new.element).to.have.a.attribute( + 'name', + 'myNewSubNetworkName' + ); + expect(updateAction.new.element).to.not.have.a.attribute('desc'); + expect(updateAction.new.element).to.not.have.a.attribute('type'); + expect(updateAction.new.element.querySelector('BitRate')).to.not.exist; + }); + }); }); From ee8af899e8ddde0fc4fb69fe2c93599c8f60521b Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Mon, 17 Jan 2022 08:54:16 +0100 Subject: [PATCH 09/12] refactor(editors/communication/subnetwork-editor): minor improvements --- .../communication/subnetwork-editor.ts | 15 +++---- test/testfiles/communication.scd | 39 ++++++++++++++++ .../subnetwork-editor.test.snap.js | 2 + .../communication/subnetwork-editor.test.ts | 45 ++++++++++++++++--- 4 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 test/testfiles/communication.scd diff --git a/src/editors/communication/subnetwork-editor.ts b/src/editors/communication/subnetwork-editor.ts index 13730a930a..e5c88cfa4a 100644 --- a/src/editors/communication/subnetwork-editor.ts +++ b/src/editors/communication/subnetwork-editor.ts @@ -18,7 +18,6 @@ import { } from '../../foundation.js'; import { styles } from './foundation.js'; import { createConnectedApWizard } from '../../wizards/connectedap.js'; -import { editSubNetworkWizard } from '../../wizards/subnetwork.js'; import { wizards } from '../../wizards/wizard-library.js'; /** [[`Communication`]] subeditor for a `SubNetwork` element. */ @@ -45,12 +44,12 @@ export class SubNetworkEditor extends LitElement { /** SubNetwork child elements BitRate label */ @property() get bitrate(): string | null { - const V = this.element.querySelector('BitRate'); - if (V === null) return null; - const v = V.textContent ?? ''; - const m = V.getAttribute('multiplier'); + const Br = this.element.querySelector('BitRate'); + if (Br === null) return null; + const br = Br.textContent ?? ''; + const m = Br.getAttribute('multiplier'); const u = m === null ? 'b/s' : ' ' + m + 'b/s'; - return v ? v + u : null; + return br ? br + u : null; } private openConnectedAPwizard(): void { @@ -76,9 +75,7 @@ export class SubNetworkEditor extends LitElement { } private renderIedContainer(): TemplateResult[] { - return Array.from( - this.element.querySelectorAll(':scope > ConnectedAP') ?? [] - ) + return Array.from(this.element.querySelectorAll(':scope > ConnectedAP')) .map(connAP => connAP.getAttribute('iedName')!) .filter((v, i, a) => a.indexOf(v) === i) .sort(compareNames) diff --git a/test/testfiles/communication.scd b/test/testfiles/communication.scd new file mode 100644 index 0000000000..7c97af5458 --- /dev/null +++ b/test/testfiles/communication.scd @@ -0,0 +1,39 @@ + + +
+ TrainingIEC61850 + + + +
+ + + 100.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js b/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js index 5ab43db715..6f803de804 100644 --- a/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js +++ b/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js @@ -32,6 +32,8 @@ snapshots["subnetwork-editor looks like the latest snapshot"] =
+ +
diff --git a/test/unit/editors/communication/subnetwork-editor.test.ts b/test/unit/editors/communication/subnetwork-editor.test.ts index 2ad9aa5e26..a6ef4ebe3d 100644 --- a/test/unit/editors/communication/subnetwork-editor.test.ts +++ b/test/unit/editors/communication/subnetwork-editor.test.ts @@ -5,16 +5,17 @@ import { SubNetworkEditor } from '../../../../src/editors/communication/subnetwo describe('subnetwork-editor', () => { let element: SubNetworkEditor; - let validSCL: XMLDocument; + let subNetwork: Element; + beforeEach(async () => { - validSCL = await fetch('/test/testfiles/valid2007B4.scd') + const validSCL = await fetch('/test/testfiles/communication.scd') .then(response => response.text()) .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + subNetwork = validSCL.querySelector('SubNetwork')!; element = ( await fixture( - html`` + html`` ) ); }); @@ -22,12 +23,46 @@ describe('subnetwork-editor', () => { it('has a name property', () => expect(element).to.have.property('name', 'StationBus')); + it('indicates missing required name as UNDEFINED', async () => { + subNetwork.removeAttribute('name'); + await element.requestUpdate(); + + expect(element).to.have.property('name', 'UNDEFINED'); + }); + it('has a desc property', () => expect(element).to.have.property('desc', 'desc')); it('has a type property', () => expect(element).to.have.property('type', '8-MMS')); + it('return null with missing type attribute', async () => { + subNetwork.removeAttribute('type'); + await element.requestUpdate(); + + expect(element).to.have.property('type', null); + }); + + it('has a BitRate property', () => + expect(element).to.have.property('bitrate', '100.0b/s')); + + it('includes multiplier to bitrate property', async () => { + const bitrate = subNetwork.querySelector('BitRate'); + bitrate?.setAttribute('multiplier', 'M'); + await element.requestUpdate(); + + expect(element).to.have.property('bitrate', '100.0 Mb/s'); + }); + + it('returns null with missing BitRate content event though BitRate exist as element', async () => { + const bitrate = subNetwork.querySelector('BitRate'); + bitrate!.textContent = null; + bitrate?.setAttribute('multiplier', 'M'); + await element.requestUpdate(); + + expect(element).to.have.property('bitrate', null); + }); + it('looks like the latest snapshot', async () => await expect(element).shadowDom.to.equalSnapshot()); }); From 7b99bd7137819dc64f67054a1d05d18cd4aee21e Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Mon, 17 Jan 2022 08:56:39 +0100 Subject: [PATCH 10/12] refactor(editors/communication/subnetwork-editor): remove code duplication --- src/editors/communication/foundation.ts | 65 ------------------- .../communication/subnetwork-editor.ts | 8 ++- 2 files changed, 5 insertions(+), 68 deletions(-) delete mode 100644 src/editors/communication/foundation.ts diff --git a/src/editors/communication/foundation.ts b/src/editors/communication/foundation.ts deleted file mode 100644 index 232f0422ad..0000000000 --- a/src/editors/communication/foundation.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { css } from 'lit-element'; - -/** Common `CSS` styles used by communication subeditors */ -export const styles = css` - :host(.moving) section { - opacity: 0.3; - } - - section { - background-color: var(--mdc-theme-surface); - transition: all 200ms linear; - outline-color: var(--mdc-theme-primary); - outline-style: solid; - outline-width: 0px; - margin: 8px 12px 16px; - opacity: 1; - } - - section:focus { - 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); - } - - section:focus-within { - outline-width: 2px; - transition: all 250ms linear; - } - - h1, - h2, - h3 { - color: var(--mdc-theme-on-surface); - font-family: 'Roboto', sans-serif; - font-weight: 300; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - margin: 0px; - line-height: 48px; - padding-left: 0.3em; - transition: background-color 150ms linear; - } - - section:focus-within > h1, - section:focus-within > h2, - section:focus-within > h3 { - color: var(--mdc-theme-surface); - background-color: var(--mdc-theme-primary); - transition: background-color 200ms linear; - } - - h1 > nav, - h2 > nav, - h3 > nav, - h1 > abbr > mwc-icon-button, - h2 > abbr > mwc-icon-button, - h3 > abbr > mwc-icon-button { - float: right; - } - - abbr { - text-decoration: none; - border-bottom: none; - } -`; diff --git a/src/editors/communication/subnetwork-editor.ts b/src/editors/communication/subnetwork-editor.ts index e5c88cfa4a..d527fb1b0d 100644 --- a/src/editors/communication/subnetwork-editor.ts +++ b/src/editors/communication/subnetwork-editor.ts @@ -16,7 +16,6 @@ import { newActionEvent, compareNames, } from '../../foundation.js'; -import { styles } from './foundation.js'; import { createConnectedApWizard } from '../../wizards/connectedap.js'; import { wizards } from '../../wizards/wizard-library.js'; @@ -137,8 +136,6 @@ export class SubNetworkEditor extends LitElement { } static styles = css` - ${styles} - #iedSection { background-color: var(--mdc-theme-on-primary); margin: 0px; @@ -167,5 +164,10 @@ export class SubNetworkEditor extends LitElement { padding: 8px 12px 8px; grid-template-columns: repeat(auto-fit, minmax(64px, auto)); } + + abbr { + text-decoration: none; + border-bottom: none; + } `; } From d6368c01613043c20b68147f81a7559076cd691b Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Mon, 17 Jan 2022 11:27:46 +0100 Subject: [PATCH 11/12] style(editors/communication/subnetwork-editor): adjust CSS --- .../communication/subnetwork-editor.ts | 54 +++++++------------ .../subnetwork-editor.test.snap.js | 12 ++--- 2 files changed, 25 insertions(+), 41 deletions(-) diff --git a/src/editors/communication/subnetwork-editor.ts b/src/editors/communication/subnetwork-editor.ts index d527fb1b0d..0972dbff88 100644 --- a/src/editors/communication/subnetwork-editor.ts +++ b/src/editors/communication/subnetwork-editor.ts @@ -80,21 +80,19 @@ export class SubNetworkEditor extends LitElement { .sort(compareNames) .map( iedName => html` -
- ${Array.from( - this.element.parentElement?.querySelectorAll( - `:scope > SubNetwork > ConnectedAP[iedName="${iedName}"]` - ) ?? [] - ).map( - connectedAP => - html`` - )} -
+ ${Array.from( + this.element.parentElement?.querySelectorAll( + `:scope > SubNetwork > ConnectedAP[iedName="${iedName}"]` + ) ?? [] + ).map( + connectedAP => + html`` + )}
` ); } @@ -131,14 +129,17 @@ export class SubNetworkEditor extends LitElement { @click="${() => this.openConnectedAPwizard()}" > -
${this.renderIedContainer()}
+
${this.renderIedContainer()}
`; } static styles = css` - #iedSection { - background-color: var(--mdc-theme-on-primary); - margin: 0px; + #iedContainer { + display: grid; + box-sizing: border-box; + gap: 12px; + padding: 8px 12px 16px; + grid-template-columns: repeat(auto-fit, minmax(150px, auto)); } #iedSection:not(:focus):not(:focus-within) .disabled { @@ -150,21 +151,6 @@ export class SubNetworkEditor extends LitElement { opacity: 0.5; } - #connAPContainer { - display: grid; - box-sizing: border-box; - gap: 12px; - padding: 8px 12px 16px; - grid-template-columns: repeat(auto-fit, minmax(150px, auto)); - } - - #connApContainer { - display: grid; - box-sizing: border-box; - padding: 8px 12px 8px; - grid-template-columns: repeat(auto-fit, minmax(64px, auto)); - } - abbr { text-decoration: none; border-bottom: none; diff --git a/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js b/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js index 6f803de804..bacc33ce14 100644 --- a/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js +++ b/test/unit/editors/communication/__snapshots__/subnetwork-editor.test.snap.js @@ -24,17 +24,15 @@ snapshots["subnetwork-editor looks like the latest snapshot"] = -
+
-
- - - - -
+ + + +
From 88ed88e6258cffb0440c8216d99d610dce6ea6f6 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Wed, 2 Feb 2022 23:55:09 +0100 Subject: [PATCH 12/12] refactor: resolve review comments --- src/editors/Communication.ts | 1 + .../communication/subnetwork-editor.ts | 30 +++++++++---------- .../subnetwork-editor.test.snap.js | 3 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/editors/Communication.ts b/src/editors/Communication.ts index cf5ea64b73..9ed9a81d76 100644 --- a/src/editors/Communication.ts +++ b/src/editors/Communication.ts @@ -72,6 +72,7 @@ export default class CommunicationPlugin extends LitElement { :host { width: 100vw; } + section { outline: none; padding: 8px 12px 16px; diff --git a/src/editors/communication/subnetwork-editor.ts b/src/editors/communication/subnetwork-editor.ts index 0972dbff88..22ed4a9817 100644 --- a/src/editors/communication/subnetwork-editor.ts +++ b/src/editors/communication/subnetwork-editor.ts @@ -43,12 +43,12 @@ export class SubNetworkEditor extends LitElement { /** SubNetwork child elements BitRate label */ @property() get bitrate(): string | null { - const Br = this.element.querySelector('BitRate'); - if (Br === null) return null; - const br = Br.textContent ?? ''; - const m = Br.getAttribute('multiplier'); - const u = m === null ? 'b/s' : ' ' + m + 'b/s'; - return br ? br + u : null; + const bitRate = this.element.querySelector('BitRate'); + if (bitRate === null) return null; + const bitRateValue = bitRate.textContent ?? ''; + const m = bitRate.getAttribute('multiplier'); + const unit = m === null ? 'b/s' : ' ' + m + 'b/s'; + return bitRateValue ? bitRateValue + unit : null; } private openConnectedAPwizard(): void { @@ -97,21 +97,21 @@ export class SubNetworkEditor extends LitElement { ); } - private renderSubNetworkSpecs(): TemplateResult { - if (!this.type && !this.bitrate) return html``; + private subNetworkSpecs(): string { + if (!this.type && !this.bitrate) return ''; - return html`(${this.type}${this.type && this.bitrate - ? html`—` - : html``}${this.bitrate})`; + return `(${this.type}${ + this.type && this.bitrate ? ` — ${this.bitrate}` : `` + })`; } - private renderHeader(): TemplateResult { - return html` ${this.name} ${this.desc === null ? '' : html`—`} - ${this.desc} ${this.renderSubNetworkSpecs()}`; + private header(): string { + return ` ${this.name} ${this.desc === null ? '' : `— ${this.desc}`} + ${this.subNetworkSpecs()}`; } render(): TemplateResult { - return html` + return html` +`