diff --git a/packages/open-scd/src/editors/ied/ldevice-container.ts b/packages/open-scd/src/editors/ied/ldevice-container.ts index e007564050..29b2485e8f 100644 --- a/packages/open-scd/src/editors/ied/ldevice-container.ts +++ b/packages/open-scd/src/editors/ied/ldevice-container.ts @@ -17,12 +17,15 @@ import { getDescriptionAttribute, getInstanceAttribute, getNameAttribute, + getLdNameAttribute, + newWizardEvent, } from '../../foundation.js'; import { logicalDeviceIcon } from '../../icons/ied-icons.js'; import '../../action-pane.js'; import './ln-container.js'; +import { wizards } from '../../wizards/wizard-library.js'; import { Container } from './foundation.js'; /** [[`IED`]] plugin subeditor for editing `LDevice` element. */ @@ -34,12 +37,20 @@ export class LDeviceContainer extends Container { @query('#toggleButton') toggleButton!: IconButtonToggle | undefined; + private openEditWizard(): void { + const wizard = wizards['LDevice'].edit(this.element); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + private header(): TemplateResult { const nameOrInst = getNameAttribute(this.element) ?? getInstanceAttribute(this.element); const desc = getDescriptionAttribute(this.element); + const ldName = getLdNameAttribute(this.element); - return html`${nameOrInst}${desc ? html` — ${desc}` : nothing}`; + return html`${nameOrInst}${desc ? html` — ${desc}` : nothing}${ldName + ? html` — ${ldName}` + : nothing}`; } protected firstUpdated(): void { @@ -70,6 +81,12 @@ export class LDeviceContainer extends Container { return html` ${logicalDeviceIcon} + + this.openEditWizard()} + > + ${lnElements.length > 0 ? html`` + : html``, + html``, + html``, + ]; +} + +function ldNameIsAllowed(element: Element): boolean { + const ConfLdName = element + .closest('IED') + ?.querySelector('Services > ConfLdName'); + if (ConfLdName) return true; + + return false; +} + +function updateAction(element: Element): WizardActor { + return (inputs: WizardInputElement[]): SimpleAction[] => { + const ldAttrs: Record = {}; + const ldKeys = ['ldName', 'desc']; + ldKeys.forEach(key => { + ldAttrs[key] = getValue(inputs.find(i => i.label === key)!); + }); + + if (ldKeys.some(key => ldAttrs[key] !== element.getAttribute(key))) { + const newElement = cloneElement(element, ldAttrs); + return [ + { + old: { element }, + new: { element: newElement }, + }, + ]; + } + return []; + }; +} + +export function editLDeviceWizard(element: Element): Wizard { + return [ + { + title: get('ldevice.wizard.title.edit'), + element, + primary: { + icon: 'edit', + label: get('save'), + action: updateAction(element), + }, + content: renderLdeviceWizard( + element.getAttribute('ldName'), + !ldNameIsAllowed(element), + element.getAttribute('desc'), + element.getAttribute('inst') + ), + }, + ]; +} diff --git a/packages/open-scd/src/wizards/wizard-library.ts b/packages/open-scd/src/wizards/wizard-library.ts index a5ad747a18..5689bbfa20 100644 --- a/packages/open-scd/src/wizards/wizard-library.ts +++ b/packages/open-scd/src/wizards/wizard-library.ts @@ -21,6 +21,7 @@ import { } from './powertransformer.js'; import { editSubNetworkWizard } from './subnetwork.js'; import { editIEDWizard } from './ied.js'; +import { editLDeviceWizard } from './ldevice.js'; import { editTrgOpsWizard } from './trgops.js'; import { createDaWizard } from './da.js'; import { editDAIWizard } from './dai.js'; @@ -317,7 +318,7 @@ export const wizards: Record< create: emptyWizard, }, LDevice: { - edit: emptyWizard, + edit: editLDeviceWizard, create: emptyWizard, }, LN: { diff --git a/packages/open-scd/test/testfiles/wizards/ied.scd b/packages/open-scd/test/testfiles/wizards/ied.scd index 753b1c2131..7c6e485afa 100644 --- a/packages/open-scd/test/testfiles/wizards/ied.scd +++ b/packages/open-scd/test/testfiles/wizards/ied.scd @@ -118,6 +118,7 @@ + diff --git a/packages/open-scd/test/unit/editors/ied/__snapshots__/ldevice-container.test.snap.js b/packages/open-scd/test/unit/editors/ied/__snapshots__/ldevice-container.test.snap.js index f0890ec587..e320ea49d7 100644 --- a/packages/open-scd/test/unit/editors/ied/__snapshots__/ldevice-container.test.snap.js +++ b/packages/open-scd/test/unit/editors/ied/__snapshots__/ldevice-container.test.snap.js @@ -5,6 +5,13 @@ snapshots["ldevice-container LDevice Element with LN Elements and all LN Element ` + + + + + + + + + + + +
@@ -85,6 +106,13 @@ snapshots["ldevice-container LDevice Element without LN Element looks like the l ` + + + +
diff --git a/packages/open-scd/test/unit/wizards/__snapshots__/ldevice.test.snap.js b/packages/open-scd/test/unit/wizards/__snapshots__/ldevice.test.snap.js new file mode 100644 index 0000000000..8739657908 --- /dev/null +++ b/packages/open-scd/test/unit/wizards/__snapshots__/ldevice.test.snap.js @@ -0,0 +1,52 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Wizards for SCL element LDevice Allowing/Disallowing ldName editing looks like the latest snapshot"] = +` +
+ + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element LDevice Allowing/Disallowing ldName editing looks like the latest snapshot */ + diff --git a/packages/open-scd/test/unit/wizards/ldevice.test.ts b/packages/open-scd/test/unit/wizards/ldevice.test.ts new file mode 100644 index 0000000000..372fb07565 --- /dev/null +++ b/packages/open-scd/test/unit/wizards/ldevice.test.ts @@ -0,0 +1,109 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import '../../mock-wizard.js'; +import { MockWizard } from '../../mock-wizard.js'; + +import { WizardTextField } from '../../../src/wizard-textfield.js'; +import { + WizardInputElement, + createUpdateAction, + getValue, +} from '../../../src/foundation.js'; +import { editLDeviceWizard } from '../../../src/wizards/ldevice.js'; +import { + fetchDoc, + setWizardTextFieldValue, + expectUpdateAction, +} from './test-support.js'; + +describe('Wizards for SCL element LDevice', () => { + let doc: XMLDocument; + let ied: Element; + let services: Element; + let ldevice: Element; + let element: MockWizard; + let inputs: WizardInputElement[]; + + beforeEach(async () => { + doc = await fetchDoc('/test/testfiles/wizards/ied.scd'); + ied = doc.querySelector('IED[name="IED3"]')!; + services = ied.querySelector('Services')!; + ldevice = ied.querySelectorAll('AccessPoint > Server > LDevice')[0]; + element = await fixture(html``); + const wizard = editLDeviceWizard(ldevice); + element.workflow.push(() => wizard); + await element.requestUpdate(); + inputs = Array.from(element.wizardUI.inputs); + }); + + it('contains a wizard-textfield with a non-empty "inst" value', async () => { + expect( + (inputs).find(textField => textField.label == 'ldInst') + ?.value + ).to.be.equal(ldevice.getAttribute('inst')); + }); + it('contains a wizard-textfield with an empty "desc" value', async () => { + expect( + (inputs).find(textField => textField.label == 'desc') + ?.value + ).to.be.equal(''); + }); + it('contains a wizard-textfield with an empty "ldName" value', async () => { + expect( + (inputs).find(textField => textField.label == 'ldName') + ?.value + ).to.be.equal(''); + }); + + describe('Allowing/Disallowing ldName editing', () => { + it('ConfLdName should not be present and therefore ldName should be readonly', async function () { + expect(services.querySelector('ConfLdName')).to.not.exist; + expect(inputs[0]).to.have.attribute('readonly'); + }); + + it('looks like the latest snapshot', async () => { + await expect(element.wizardUI.dialog).dom.to.equalSnapshot(); + }); + + it('ConfLdName should be present in IED1 and therefore ldName should not be readonly', async function () { + ied = doc.querySelector('IED[name="IED1"]')!; + services = ied.querySelector('Services')!; + ldevice = ied.querySelectorAll('AccessPoint > Server > LDevice')[0]; + element = await fixture(html``); + const wizard = editLDeviceWizard(ldevice); + element.workflow.push(() => wizard); + await element.requestUpdate(); + inputs = Array.from(element.wizardUI.inputs); + + expect(services.querySelector('ConfLdName')).to.exist; + expect(inputs[0]).to.not.have.attribute('readonly'); + + describe('Modify ldName', () => { + it('should be registered as an update action', async () => { + const newValue = 'LDevice1'; + const ldeviceTextField = (inputs).find( + textField => textField.label == 'ldName' + )!; + + expect(ldeviceTextField?.value).to.be.equal(''); + await setWizardTextFieldValue(ldeviceTextField, newValue); + + const ldNameVal = getValue(inputs.find(i => i.label === 'ldName')!)!; + expect(ldNameVal).to.not.be.equal(ldevice.getAttribute('ldName')); + + const simpleAction = createUpdateAction(ldevice, { + ldName: ldNameVal, + }); + + expectUpdateAction( + simpleAction, + ldevice.tagName, + ldeviceTextField.label, + null, + newValue + ); + }); + }); + }); + }); +});