Skip to content

Commit 88d3743

Browse files
authored
feat(editors/Communication) Allow editing of GSE/SMV in the Communication editor. Closes openscd#1126 (openscd#1127)
1 parent db93440 commit 88d3743

File tree

8 files changed

+411
-12
lines changed

8 files changed

+411
-12
lines changed

src/editors/communication/gse-editor.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import {
1010
import '@material/mwc-icon';
1111

1212
import '../../action-icon.js';
13+
import { newWizardEvent, newActionEvent } from '../../foundation.js';
1314
import { sizableGooseIcon } from '../../icons/icons.js';
15+
import { editGseWizard } from '../../wizards/gse.js';
1416

1517
@customElement('gse-editor')
1618
export class GseEditor extends LitElement {
@@ -29,9 +31,37 @@ export class GseEditor extends LitElement {
2931
);
3032
}
3133

34+
private openEditWizard(): void {
35+
this.dispatchEvent(newWizardEvent(editGseWizard(this.element)));
36+
}
37+
38+
remove(): void {
39+
if (this.element)
40+
this.dispatchEvent(
41+
newActionEvent({
42+
old: {
43+
parent: this.element.parentElement!,
44+
element: this.element,
45+
reference: this.element.nextSibling,
46+
},
47+
})
48+
);
49+
}
50+
3251
render(): TemplateResult {
33-
return html`<action-icon label="${this.label}"
34-
><mwc-icon slot="icon">${sizableGooseIcon}</mwc-icon></action-icon
35-
>`;
52+
return html`<action-icon label="${this.label}" .icon="${sizableGooseIcon}"
53+
><mwc-fab
54+
slot="action"
55+
mini
56+
icon="edit"
57+
@click="${() => this.openEditWizard()}"
58+
></mwc-fab>
59+
<mwc-fab
60+
slot="action"
61+
mini
62+
icon="delete"
63+
@click="${() => this.remove()}}"
64+
></mwc-fab
65+
></action-icon>`;
3666
}
3767
}

src/editors/communication/smv-editor.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import '@material/mwc-icon';
1111

1212
import '../../action-icon.js';
1313
import { sizableSmvIcon } from '../../icons/icons.js';
14+
import { newWizardEvent, newActionEvent } from '../../foundation.js';
15+
import { editSMvWizard } from '../../wizards/smv.js';
1416

1517
@customElement('smv-editor')
1618
export class SmvEditor extends LitElement {
@@ -29,9 +31,37 @@ export class SmvEditor extends LitElement {
2931
);
3032
}
3133

34+
private openEditWizard(): void {
35+
this.dispatchEvent(newWizardEvent(editSMvWizard(this.element)));
36+
}
37+
38+
remove(): void {
39+
if (this.element)
40+
this.dispatchEvent(
41+
newActionEvent({
42+
old: {
43+
parent: this.element.parentElement!,
44+
element: this.element,
45+
reference: this.element.nextSibling,
46+
},
47+
})
48+
);
49+
}
50+
3251
render(): TemplateResult {
33-
return html`<action-icon label="${this.label}"
34-
><mwc-icon slot="icon">${sizableSmvIcon}</mwc-icon>
35-
</action-icon>`;
52+
return html`<action-icon label="${this.label}" .icon="${sizableSmvIcon}"
53+
><mwc-fab
54+
slot="action"
55+
mini
56+
icon="edit"
57+
@click="${() => this.openEditWizard()}"
58+
></mwc-fab>
59+
<mwc-fab
60+
slot="action"
61+
mini
62+
icon="delete"
63+
@click="${() => this.remove()}}"
64+
></mwc-fab
65+
></action-icon>`;
3666
}
3767
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { fixture, html, expect } from '@open-wc/testing';
2+
3+
import '../../../mock-wizard-editor.js';
4+
import { MockWizardEditor } from '../../../mock-wizard-editor.js';
5+
6+
import '../../../../src/editors/communication/gse-editor.js';
7+
import { GseEditor } from '../../../../src/editors/communication/gse-editor.js';
8+
import { WizardTextField } from '../../../../src/wizard-textfield.js';
9+
10+
describe('gse-editor wizarding editing integration', () => {
11+
describe('edit wizard', () => {
12+
let doc: XMLDocument;
13+
let parent: MockWizardEditor;
14+
let element: GseEditor | null;
15+
let secondaryAction: HTMLElement;
16+
let primaryAction: HTMLElement;
17+
let macAddressField: WizardTextField;
18+
19+
beforeEach(async () => {
20+
doc = await fetch('/test/testfiles/valid2007B4.scd')
21+
.then(response => response.text())
22+
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
23+
parent = <MockWizardEditor>(
24+
await fixture(
25+
html`<mock-wizard-editor
26+
><gse-editor
27+
.element=${doc.querySelector('ConnectedAP[iedName="IED1"] > GSE')}
28+
></gse-editor>
29+
></mock-wizard-editor
30+
>`
31+
)
32+
);
33+
element = parent.querySelector('gse-editor');
34+
await (<HTMLElement>(
35+
element?.shadowRoot?.querySelector('mwc-fab[icon="edit"]')
36+
)).click();
37+
await parent.updateComplete;
38+
39+
macAddressField = <WizardTextField>(
40+
parent.wizardUI.dialog?.querySelector(
41+
'wizard-textfield[label="MAC-Address"]'
42+
)
43+
);
44+
secondaryAction = <HTMLElement>(
45+
parent.wizardUI.dialog?.querySelector(
46+
'mwc-button[slot="secondaryAction"]'
47+
)
48+
);
49+
primaryAction = <HTMLElement>(
50+
parent.wizardUI.dialog?.querySelector(
51+
'mwc-button[slot="primaryAction"]'
52+
)
53+
);
54+
});
55+
56+
it('closes on secondary action', async () => {
57+
expect(parent.wizardUI.dialog).to.exist;
58+
secondaryAction.click();
59+
await new Promise(resolve => setTimeout(resolve, 100)); // await animation
60+
expect(parent.wizardUI.dialog).to.not.exist;
61+
});
62+
63+
it('changes MAC address attribute on primary action', async () => {
64+
expect(
65+
doc.querySelector(
66+
'ConnectedAP[iedName="IED1"] > GSE > Address > P[type="MAC-Address"]'
67+
)?.textContent
68+
).to.equal('01-0C-CD-01-00-10');
69+
macAddressField.value = '01-0C-CD-01-0F-FF';
70+
await parent.requestUpdate();
71+
primaryAction.click();
72+
await parent.requestUpdate();
73+
expect(
74+
doc.querySelector(
75+
'ConnectedAP[iedName="IED1"] > GSE > Address > P[type="MAC-Address"]'
76+
)?.textContent
77+
).to.equal('01-0C-CD-01-0F-FF');
78+
});
79+
80+
it('does not change Address if no changes have been made', async () => {
81+
const reference = doc.querySelector('ConnectedAP[iedName="IED1"] > GSE');
82+
primaryAction.click();
83+
expect(
84+
doc
85+
.querySelector('ConnectedAP[iedName="IED1"] > GSE')
86+
?.isEqualNode(reference)
87+
).to.be.true;
88+
});
89+
});
90+
91+
describe('remove action', () => {
92+
let doc: XMLDocument;
93+
let parent: MockWizardEditor;
94+
let element: GseEditor | null;
95+
let deleteButton: HTMLElement;
96+
97+
beforeEach(async () => {
98+
doc = await fetch('/test/testfiles/valid2007B4.scd')
99+
.then(response => response.text())
100+
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
101+
parent = <MockWizardEditor>(
102+
await fixture(
103+
html`<mock-wizard-editor
104+
><gse-editor
105+
.element=${doc.querySelector('ConnectedAP[iedName="IED1"] > GSE')}
106+
></gse-editor
107+
></mock-wizard-editor>`
108+
)
109+
);
110+
element = parent.querySelector('gse-editor');
111+
await parent.updateComplete;
112+
deleteButton = <HTMLElement>(
113+
element?.shadowRoot?.querySelector('mwc-fab[icon="delete"]')
114+
);
115+
});
116+
117+
it('removes GSE on delete button click', async () => {
118+
expect(doc.querySelector('ConnectedAP[iedName="IED1"] > GSE')).to.exist;
119+
deleteButton.click();
120+
await parent.updateComplete;
121+
expect(doc.querySelector('ConnectedAP[iedName="IED1"] > GSE')).to.not
122+
.exist;
123+
});
124+
});
125+
});
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { fixture, html, expect } from '@open-wc/testing';
2+
3+
import '../../../mock-wizard-editor.js';
4+
import { MockWizardEditor } from '../../../mock-wizard-editor.js';
5+
6+
import '../../../../src/editors/communication/smv-editor.js';
7+
import { SmvEditor } from '../../../../src/editors/communication/smv-editor.js';
8+
import { WizardTextField } from '../../../../src/wizard-textfield.js';
9+
10+
describe('smv-editor wizarding editing integration', () => {
11+
describe('edit wizard', () => {
12+
let doc: XMLDocument;
13+
let parent: MockWizardEditor;
14+
let element: SmvEditor | null;
15+
let secondaryAction: HTMLElement;
16+
let primaryAction: HTMLElement;
17+
let macAddressField: WizardTextField;
18+
19+
beforeEach(async () => {
20+
doc = await fetch('/test/testfiles/valid2007B4.scd')
21+
.then(response => response.text())
22+
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
23+
parent = <MockWizardEditor>(
24+
await fixture(
25+
html`<mock-wizard-editor
26+
><smv-editor
27+
.element=${doc.querySelector('ConnectedAP[iedName="IED3"] > SMV')}
28+
></smv-editor>
29+
></mock-wizard-editor
30+
>`
31+
)
32+
);
33+
element = parent.querySelector('smv-editor');
34+
await (<HTMLElement>(
35+
element?.shadowRoot?.querySelector('mwc-fab[icon="edit"]')
36+
)).click();
37+
await parent.updateComplete;
38+
39+
macAddressField = <WizardTextField>(
40+
parent.wizardUI.dialog?.querySelector(
41+
'wizard-textfield[label="MAC-Address"]'
42+
)
43+
);
44+
secondaryAction = <HTMLElement>(
45+
parent.wizardUI.dialog?.querySelector(
46+
'mwc-button[slot="secondaryAction"]'
47+
)
48+
);
49+
primaryAction = <HTMLElement>(
50+
parent.wizardUI.dialog?.querySelector(
51+
'mwc-button[slot="primaryAction"]'
52+
)
53+
);
54+
});
55+
56+
it('closes on secondary action', async () => {
57+
expect(parent.wizardUI.dialog).to.exist;
58+
secondaryAction.click();
59+
await new Promise(resolve => setTimeout(resolve, 100)); // await animation
60+
expect(parent.wizardUI.dialog).to.not.exist;
61+
});
62+
63+
it('changes MAC address attribute on primary action', async () => {
64+
expect(
65+
doc.querySelector(
66+
'ConnectedAP[iedName="IED3"] > SMV > Address > P[type="MAC-Address"]'
67+
)?.textContent
68+
).to.equal('01-0C-CD-04-00-20');
69+
macAddressField.value = '01-0C-CD-04-0F-FF';
70+
await parent.requestUpdate();
71+
primaryAction.click();
72+
await parent.requestUpdate();
73+
expect(
74+
doc.querySelector(
75+
'ConnectedAP[iedName="IED3"] > SMV > Address > P[type="MAC-Address"]'
76+
)?.textContent
77+
).to.equal('01-0C-CD-04-0F-FF');
78+
});
79+
80+
it('does not change Address if no changes have been made', async () => {
81+
const reference = doc.querySelector('ConnectedAP[iedName="IED3"] > SMV');
82+
primaryAction.click();
83+
expect(
84+
doc
85+
.querySelector('ConnectedAP[iedName="IED3"] > SMV')
86+
?.isEqualNode(reference)
87+
).to.be.true;
88+
});
89+
});
90+
91+
describe('remove action', () => {
92+
let doc: XMLDocument;
93+
let parent: MockWizardEditor;
94+
let element: SmvEditor | null;
95+
let deleteButton: HTMLElement;
96+
97+
beforeEach(async () => {
98+
doc = await fetch('/test/testfiles/valid2007B4.scd')
99+
.then(response => response.text())
100+
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
101+
parent = <MockWizardEditor>(
102+
await fixture(
103+
html`<mock-wizard-editor
104+
><smv-editor
105+
.element=${doc.querySelector('ConnectedAP[iedName="IED3"] > SMV')}
106+
></smv-editor
107+
></mock-wizard-editor>`
108+
)
109+
);
110+
element = parent.querySelector('smv-editor');
111+
await parent.updateComplete;
112+
deleteButton = <HTMLElement>(
113+
element?.shadowRoot?.querySelector('mwc-fab[icon="delete"]')
114+
);
115+
});
116+
117+
it('removes SMV on delete button click', async () => {
118+
expect(doc.querySelector('ConnectedAP[iedName="IED3"] > SMV')).to.exist;
119+
deleteButton.click();
120+
await parent.updateComplete;
121+
expect(doc.querySelector('ConnectedAP[iedName="IED3"] > SMV')).to.not
122+
.exist;
123+
});
124+
});
125+
});

test/unit/editors/communication/__snapshots__/gse-editor.test.snap.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,18 @@ snapshots["Editor web component for GSE element looks like its latest snapshot"]
66
label="QB2_Disconnector/GOOSE2"
77
tabindex="0"
88
>
9-
<mwc-icon slot="icon">
10-
</mwc-icon>
9+
<mwc-fab
10+
icon="edit"
11+
mini=""
12+
slot="action"
13+
>
14+
</mwc-fab>
15+
<mwc-fab
16+
icon="delete"
17+
mini=""
18+
slot="action"
19+
>
20+
</mwc-fab>
1121
</action-icon>
1222
`;
1323
/* end snapshot Editor web component for GSE element looks like its latest snapshot */

test/unit/editors/communication/__snapshots__/smv-editor.test.snap.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,18 @@ snapshots["Editor web component for SMV element looks like its latest snapshot"]
66
label="CurrentTransformer/voltageOnly"
77
tabindex="0"
88
>
9-
<mwc-icon slot="icon">
10-
</mwc-icon>
9+
<mwc-fab
10+
icon="edit"
11+
mini=""
12+
slot="action"
13+
>
14+
</mwc-fab>
15+
<mwc-fab
16+
icon="delete"
17+
mini=""
18+
slot="action"
19+
>
20+
</mwc-fab>
1121
</action-icon>
1222
`;
1323
/* end snapshot Editor web component for SMV element looks like its latest snapshot */

0 commit comments

Comments
 (0)