Skip to content

Commit

Permalink
feat(substation/general-equipment-editor): edit wizard (openscd#1089)
Browse files Browse the repository at this point in the history
* fix(rebasing 976 in main)

* fix(generalEquipment.ts): added pattern and maxLength

* fix(generalEquipment.ts): changed maxLenght into minLength
  • Loading branch information
marcvanraalte authored Nov 28, 2022
1 parent cfd16d6 commit 95ba5ab
Show file tree
Hide file tree
Showing 9 changed files with 474 additions and 9 deletions.
29 changes: 21 additions & 8 deletions src/editors/substation/general-equipment-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ import '../../action-pane.js';
import '../../editors/substation/eq-function-editor.js';
import '../../editors/substation/l-node-editor.js';
import { generalConductingEquipmentIcon } from '../../icons/icons.js';
import { getChildElementsByTagName } from '../../foundation.js';
//import { styles } from './foundation.js';
import { getChildElementsByTagName, newWizardEvent } from '../../foundation.js';
import { translate } from 'lit-translate';
import { wizards } from '../../wizards/wizard-library.js';

@customElement('general-equipment-editor')
export class GeneralEquipmentEditor extends LitElement {
@property({ attribute: false })
doc!: XMLDocument;
@property({ attribute: false })
element!: Element;
@property({ type: Boolean })
readonly = false;

/** Whether `Function` and `SubFunction` are rendered */
@property({ type: Boolean })
showfunctions = false;
Expand All @@ -40,9 +40,12 @@ export class GeneralEquipmentEditor extends LitElement {
return `${name} ${desc ? `— ${desc}` : ''}`;
}

private renderLNodes(): TemplateResult {
if (!this.showfunctions) return html``;
openEditWizard(): void {
const wizard = wizards['GeneralEquipment'].edit(this.element);
if (wizard) this.dispatchEvent(newWizardEvent(wizard));
}

private renderLNodes(): TemplateResult {
const lNodes = getChildElementsByTagName(this.element, 'LNode');

return lNodes.length
Expand All @@ -59,8 +62,6 @@ export class GeneralEquipmentEditor extends LitElement {
}

private renderEqFunctions(): TemplateResult {
if (!this.showfunctions) return html``;

const eFunctions = getChildElementsByTagName(this.element, 'EqFunction');

return eFunctions.length
Expand All @@ -77,10 +78,22 @@ export class GeneralEquipmentEditor extends LitElement {
render(): TemplateResult {
if (this.showfunctions)
return html`<action-pane label=${this.header}>
<abbr slot="action" title="${translate('edit')}">
<mwc-icon-button
icon="edit"
@click=${() => this.openEditWizard()}
></mwc-icon-button>
</abbr>
${this.renderLNodes()} ${this.renderEqFunctions()}
</action-pane>`;

return html`<action-icon label=${this.header}>
<abbr slot="action" title="${translate('edit')}">
<mwc-icon-button
icon="edit"
@click=${() => this.openEditWizard()}
></mwc-icon-button>
</abbr>
<mwc-icon slot="icon">${generalConductingEquipmentIcon}</mwc-icon>
</action-icon>`;
}
Expand Down
114 changes: 114 additions & 0 deletions src/wizards/generalEquipment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { html, TemplateResult } from 'lit-element';
import { get, translate } from 'lit-translate';
import {
cloneElement,
getChildElementsByTagName,
getValue,
SimpleAction,
Wizard,
WizardActor,
WizardInputElement,
} from '../foundation';

export function editGeneralEquipmentWizard(element: Element): Wizard {
const name = element.getAttribute('name');
const desc = element.getAttribute('desc');
const type = element.getAttribute('type');
const virtual = element.getAttribute('virtual');
const reservedNames: string[] = getChildElementsByTagName(
element.parentElement!,
'GeneralEquipment'
)
.filter(sibling => sibling !== element)
.map(sibling => sibling.getAttribute('name')!);

return [
{
title: get('wizard.title.edit', { tagName: 'GeneralEquipment' }),
primary: {
icon: 'save',
label: get('save'),
action: updateGeneralEquipmentAction(element),
},
content: [
...contentGeneralEquipmentWizard({
name,
desc,
type,
virtual,
reservedNames,
}),
],
},
];
}

function updateGeneralEquipmentAction(element: Element): WizardActor {
return (inputs: WizardInputElement[]): SimpleAction[] => {
const generalEquipmentAttrs: Record<string, string | null> = {};
const generalEquipmentKeys = ['name', 'desc', 'type', 'virtual'];
generalEquipmentKeys.forEach(key => {
generalEquipmentAttrs[key] = getValue(inputs.find(i => i.label === key)!);
});

if (
generalEquipmentKeys.some(
key => generalEquipmentAttrs[key] !== element.getAttribute(key)
)
) {
const newElement = cloneElement(element, generalEquipmentAttrs);
return [
{
old: { element },
new: { element: newElement },
},
];
}

return [];
};
}

interface ContentOptions {
name: string | null;
desc: string | null;
type: string | null;
virtual: string | null;
reservedNames: string[];
}

export function contentGeneralEquipmentWizard(
content: ContentOptions
): TemplateResult[] {
return [
html`<wizard-textfield
label="name"
.maybeValue=${content.name}
helper="${translate('scl.name')}"
required
validationMessage="${translate('textfield.required')}"
.reservedValues=${content.reservedNames}
dialogInitialFocus
></wizard-textfield>`,
html`<wizard-textfield
label="desc"
.maybeValue=${content.desc}
nullable
helper="${translate('scl.desc')}"
></wizard-textfield>`,
html`<wizard-textfield
label="type"
.maybeValue=${content.type}
helper="${translate('scl.type')}"
minLength="${3}"
pattern="AXN|BAT|MOT|FAN|FIL|PMP|TNK|VLV|E[A-Z]*"
required
></wizard-textfield>`,
html`<wizard-checkbox
label="virtual"
.maybeValue=${content.virtual}
helper="${translate('scl.virtual')}"
nullable
></wizard-checkbox>`,
];
}
3 changes: 2 additions & 1 deletion src/wizards/wizard-library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
} from './subfunction.js';
import { editSampledValueControlWizard } from './sampledvaluecontrol.js';
import { editSubEquipmentWizard } from './subequipment.js';
import { editGeneralEquipmentWizard } from './generalEquipment.js';

type SclElementWizard = (
element: Element,
Expand Down Expand Up @@ -223,7 +224,7 @@ export const wizards: Record<
create: createFunctionWizard,
},
GeneralEquipment: {
edit: emptyWizard,
edit: editGeneralEquipmentWizard,
create: emptyWizard,
},
GetCBValues: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { expect, fixture, html } from '@open-wc/testing';

import '../../../mock-wizard-editor.js';
import { MockWizardEditor } from '../../../mock-wizard-editor.js';

import '../../../../src/editors/substation/general-equipment-editor.js';
import { GeneralEquipmentEditor } from '../../../../src/editors/substation/general-equipment-editor.js';
import { WizardTextField } from '../../../../src/wizard-textfield.js';

describe('general-equipment-editor wizarding editing integration', () => {
describe('edit wizard', () => {
let doc: XMLDocument;
let parent: MockWizardEditor;
let element: GeneralEquipmentEditor | null;

let nameField: WizardTextField;
let descField: WizardTextField;
let typeField: WizardTextField;
let secondaryAction: HTMLElement;
let primaryAction: HTMLElement;

beforeEach(async () => {
doc = await fetch(
'/test/testfiles/editors/substation/generalequipment.scd'
)
.then(response => response.text())
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
parent = <MockWizardEditor>(
await fixture(
html`<mock-wizard-editor
><general-equipment-editor
.element=${doc.querySelector('GeneralEquipment')}
></general-equipment-editor
></mock-wizard-editor>`
)
);

element = parent.querySelector('general-equipment-editor');
await (<HTMLElement>(
element?.shadowRoot?.querySelector('mwc-icon-button[icon="edit"]')
)).click();
await parent.updateComplete;

nameField = <WizardTextField>(
parent.wizardUI.dialog?.querySelector('wizard-textfield[label="name"]')
);
descField = <WizardTextField>(
parent.wizardUI.dialog?.querySelector('wizard-textfield[label="desc"]')
);
typeField = <WizardTextField>(
parent.wizardUI.dialog?.querySelector('wizard-textfield[label="type"]')
);
secondaryAction = <HTMLElement>(
parent.wizardUI.dialog?.querySelector(
'mwc-button[slot="secondaryAction"]'
)
);
primaryAction = <HTMLElement>(
parent.wizardUI.dialog?.querySelector(
'mwc-button[slot="primaryAction"]'
)
);
});
it('closes on secondary action', async () => {
secondaryAction.click();
await new Promise(resolve => setTimeout(resolve, 100)); // await animation
expect(parent.wizardUI.dialog).to.not.exist;
});
it('does not change name attribute if not unique within parent element', async () => {
const oldName = nameField.value;
nameField.value = 'genSub2';
primaryAction.click();
await parent.updateComplete;
expect(
doc.querySelector('GeneralEquipment')?.getAttribute('name')
).to.equal(oldName);
});
it('changes name attribute on primary action', async () => {
parent.wizardUI.inputs[0].value = 'newName';
primaryAction.click();
await parent.updateComplete;
expect(
doc.querySelector('GeneralEquipment')?.getAttribute('name')
).to.equal('newName');
});
it('changes type attribute on primary action', async () => {
parent.wizardUI.inputs[2].value = 'newAXN';
primaryAction.click();
await parent.updateComplete;
expect(
doc.querySelector('GeneralEquipment')?.getAttribute('type')
).to.equal('newAXN');
});
it('changes desc attribute on primary action', async () => {
descField.value = 'newDesc';
primaryAction.click();
await parent.updateComplete;
expect(
doc.querySelector('GeneralEquipment')?.getAttribute('desc')
).to.equal('newDesc');
});
it('deletes desc attribute if wizard-textfield is deactivated', async () => {
await new Promise(resolve => setTimeout(resolve, 100)); // await animation
descField.nullSwitch!.click();
await parent.updateComplete;
await primaryAction.click();
await parent.updateComplete;
expect(doc.querySelector('GeneralEquipment')?.getAttribute('desc')).to.be
.null;
});
});
});
2 changes: 2 additions & 0 deletions test/testfiles/editors/substation/generalequipment.scd
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<EqFunction name="genEqFunc" />
<GeneralEquipment name="someGenCon" desc="someDesc" type="MOT"/>
</GeneralEquipment>
<GeneralEquipment name="genSub2" desc="someDesc" type="AXN">
</GeneralEquipment>
<VoltageLevel name="E1" desc="" nomFreq="50" numPhases="3">
<GeneralEquipment name="genVolt1" type="BAT"/>
<PowerTransformer name="pTrans" type="PTW">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ snapshots["Editor web component for GeneralEquipment SCL element rendered as act
label="genSub"
tabindex="0"
>
<abbr
slot="action"
title="[edit]"
>
<mwc-icon-button icon="edit">
</mwc-icon-button>
</abbr>
<mwc-icon slot="icon">
</mwc-icon>
</action-icon>
Expand All @@ -17,6 +24,13 @@ snapshots["Editor web component for GeneralEquipment SCL element rendered as act
label="genSub — someDesc"
tabindex="0"
>
<abbr
slot="action"
title="[edit]"
>
<mwc-icon-button icon="edit">
</mwc-icon-button>
</abbr>
<div class="container lnode">
<l-node-editor>
</l-node-editor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ snapshots["substation-editor with general-equipment children with showfunctions
<div class="actionicon content">
<general-equipment-editor>
</general-equipment-editor>
<general-equipment-editor>
</general-equipment-editor>
</div>
<voltage-level-editor>
</voltage-level-editor>
Expand Down Expand Up @@ -537,6 +539,8 @@ snapshots["substation-editor with general-equipment children with showfunctions
<div class="content">
<general-equipment-editor showfunctions="">
</general-equipment-editor>
<general-equipment-editor showfunctions="">
</general-equipment-editor>
</div>
<voltage-level-editor showfunctions="">
</voltage-level-editor>
Expand Down
Loading

0 comments on commit 95ba5ab

Please sign in to comment.