Skip to content

Commit 440a2f6

Browse files
author
Dennis Labordus
authored
Merge pull request #795 from openscd/104-add-connected-ap
feat(editors/104): Add connected AP
2 parents 638fbf4 + 0887e72 commit 440a2f6

File tree

5 files changed

+245
-16
lines changed

5 files changed

+245
-16
lines changed

src/editors/protocol104/subnetwork-container.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import {
1010
import '@material/mwc-icon-button';
1111

1212
import './connectedap-editor.js';
13-
import { compareNames } from '../../foundation.js';
13+
import { compareNames, newWizardEvent } from '../../foundation.js';
14+
import { translate } from 'lit-translate';
15+
import { createConnectedApWizard } from './wizards/connectedap.js';
1416

1517
/** [[`104`]] subeditor for a `SubNetwork` element. */
1618
@customElement('subnetwork-104-container')
17-
export class SubNetworkEditor extends LitElement {
19+
export class SubNetwork104Container extends LitElement {
1820
/** SCL element SubNetwork */
1921
@property({ attribute: false })
2022
element!: Element;
@@ -28,6 +30,10 @@ export class SubNetworkEditor extends LitElement {
2830
return bitRateValue ? bitRateValue + unit : null;
2931
}
3032

33+
private openConnectedAPwizard(): void {
34+
this.dispatchEvent(newWizardEvent(createConnectedApWizard(this.element)));
35+
}
36+
3137
private renderIedContainer(): TemplateResult[] {
3238
return Array.from(this.element.querySelectorAll(':scope > ConnectedAP'))
3339
.map(connAP => connAP.getAttribute('iedName')!)
@@ -72,6 +78,12 @@ export class SubNetworkEditor extends LitElement {
7278

7379
render(): TemplateResult {
7480
return html`<action-pane label="${this.header()}">
81+
<abbr slot="action" title="${translate('add')}">
82+
<mwc-icon-button
83+
icon="playlist_add"
84+
@click="${() => this.openConnectedAPwizard()}"
85+
></mwc-icon-button>
86+
</abbr>
7587
<div id="iedContainer">${this.renderIedContainer()}</div>
7688
</action-pane> `;
7789
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/* @web/test-runner snapshot v1 */
2+
export const snapshots = {};
3+
4+
snapshots["subnetwork-104-container looks like the latest snapshot"] =
5+
`<action-pane label=" F1 —
6+
(104)">
7+
<abbr
8+
slot="action"
9+
title="[add]"
10+
>
11+
<mwc-icon-button icon="playlist_add">
12+
</mwc-icon-button>
13+
</abbr>
14+
<div id="iedContainer">
15+
<action-pane
16+
id="iedSection"
17+
label="B1"
18+
>
19+
<connectedap-104-editor class="disabled">
20+
</connectedap-104-editor>
21+
<connectedap-104-editor>
22+
</connectedap-104-editor>
23+
</action-pane>
24+
<action-pane
25+
id="iedSection"
26+
label="B2"
27+
>
28+
<connectedap-104-editor class="disabled">
29+
</connectedap-104-editor>
30+
<connectedap-104-editor class="disabled">
31+
</connectedap-104-editor>
32+
<connectedap-104-editor>
33+
</connectedap-104-editor>
34+
<connectedap-104-editor>
35+
</connectedap-104-editor>
36+
</action-pane>
37+
</div>
38+
</action-pane>
39+
`;
40+
/* end snapshot subnetwork-104-container looks like the latest snapshot */
41+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { html, fixture, expect } from '@open-wc/testing';
2+
3+
import '../../../../src/editors/protocol104/subnetwork-container.js'
4+
import { SubNetwork104Container } from '../../../../src/editors/protocol104/subnetwork-container.js';
5+
6+
describe('subnetwork-104-container', () => {
7+
let element: SubNetwork104Container;
8+
let subNetwork: Element;
9+
10+
beforeEach(async () => {
11+
const validSCL = await fetch('/test/testfiles/104/valid-subnetwork.scd')
12+
.then(response => response.text())
13+
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
14+
15+
subNetwork = validSCL.querySelector('SubNetwork[name="F1"]')!;
16+
element = <SubNetwork104Container>(
17+
await fixture(
18+
html`<subnetwork-104-container .element=${subNetwork}></subnetwork-104-container>`
19+
)
20+
);
21+
});
22+
23+
it('looks like the latest snapshot', async () => {
24+
await expect(element).shadowDom.to.equalSnapshot();
25+
});
26+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* @web/test-runner snapshot v1 */
2+
export const snapshots = {};
3+
4+
snapshots["Wizards for SCL element ConnectedAP include a create wizard that looks like the latest snapshot"] =
5+
`<mwc-dialog
6+
defaultaction="close"
7+
heading="[wizard.title.add]"
8+
open=""
9+
style="--mdc-dialog-min-width:calc(100% + 0px)"
10+
>
11+
<div id="wizard-content">
12+
<filtered-list
13+
id="apList"
14+
multi=""
15+
>
16+
<mwc-check-list-item
17+
aria-disabled="false"
18+
graphic="control"
19+
mwc-list-item=""
20+
tabindex="0"
21+
value="B3>AP1"
22+
>
23+
<span>
24+
B3>AP1
25+
</span>
26+
</mwc-check-list-item>
27+
<mwc-check-list-item
28+
aria-disabled="true"
29+
disabled=""
30+
graphic="control"
31+
mwc-list-item=""
32+
tabindex="-1"
33+
value="B1>AP1"
34+
>
35+
<span>
36+
B1>AP1
37+
</span>
38+
</mwc-check-list-item>
39+
<mwc-check-list-item
40+
aria-disabled="true"
41+
disabled=""
42+
graphic="control"
43+
mwc-list-item=""
44+
tabindex="-1"
45+
value="B2>AP1"
46+
>
47+
<span>
48+
B2>AP1
49+
</span>
50+
</mwc-check-list-item>
51+
</filtered-list>
52+
</div>
53+
<mwc-button
54+
dialogaction="close"
55+
label="[cancel]"
56+
slot="secondaryAction"
57+
style="--mdc-theme-primary: var(--mdc-theme-error)"
58+
>
59+
</mwc-button>
60+
<mwc-button
61+
icon="save"
62+
label="[save]"
63+
slot="primaryAction"
64+
trailingicon=""
65+
>
66+
</mwc-button>
67+
</mwc-dialog>
68+
`;
69+
/* end snapshot Wizards for SCL element ConnectedAP include a create wizard that looks like the latest snapshot */
70+

test/unit/editors/protocol104/wizards/connectedap.test.ts

+94-14
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import { SinonSpy, spy } from 'sinon';
44
import '../../../../mock-wizard.js';
55

66
import { Checkbox } from '@material/mwc-checkbox';
7-
import { editConnectedAp104Wizard } from '../../../../../src/editors/protocol104/wizards/connectedap.js';
7+
import { createConnectedApWizard, editConnectedAp104Wizard } from '../../../../../src/editors/protocol104/wizards/connectedap.js';
88
import { ComplexAction, Create, Delete, isCreate, isDelete, WizardInputElement } from '../../../../../src/foundation.js';
99
import { MockWizard } from '../../../../mock-wizard.js';
1010
import { WizardTextField } from '../../../../../src/wizard-textfield.js';
11+
import { ListItemBase } from '@material/mwc-list/mwc-list-item-base';
1112

1213
describe('Wizards for SCL element ConnectedAP', () => {
1314
let doc: XMLDocument;
@@ -19,29 +20,31 @@ describe('Wizards for SCL element ConnectedAP', () => {
1920
let actionEvent: SinonSpy;
2021

2122
beforeEach(async () => {
22-
element = <MockWizard>await fixture(html`<mock-wizard></mock-wizard>`);
23-
2423
doc = await fetch('/test/testfiles/104/valid-subnetwork.scd')
2524
.then(response => response.text())
2625
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
2726

28-
const wizard = editConnectedAp104Wizard(doc.querySelector('SubNetwork[type="104"] > ConnectedAP[apName="AP2"]')!);
29-
element.workflow.push(() => wizard);
30-
await element.requestUpdate();
31-
32-
inputs = Array.from(element.wizardUI.inputs);
33-
34-
primaryAction = <HTMLElement>(
35-
element.wizardUI.dialog?.querySelector(
36-
'mwc-button[slot="primaryAction"]'
37-
)
38-
);
27+
element = <MockWizard>await fixture(html`<mock-wizard></mock-wizard>`);
3928

4029
actionEvent = spy();
4130
window.addEventListener('editor-action', actionEvent);
4231
});
4332

4433
describe('include an edit wizard that', () => {
34+
beforeEach(async () => {
35+
const wizard = editConnectedAp104Wizard(doc.querySelector('SubNetwork[type="104"] > ConnectedAP[apName="AP2"]')!);
36+
element.workflow.push(() => wizard);
37+
await element.requestUpdate();
38+
39+
inputs = Array.from(element.wizardUI.inputs);
40+
41+
primaryAction = <HTMLElement>(
42+
element.wizardUI.dialog?.querySelector(
43+
'mwc-button[slot="primaryAction"]'
44+
)
45+
);
46+
});
47+
4548
it('does not edit any P element with unchanged wizard inputs', async () => {
4649
primaryAction.click();
4750
await element.requestUpdate();
@@ -133,4 +136,81 @@ describe('Wizards for SCL element ConnectedAP', () => {
133136
).to.exist;
134137
});
135138
});
139+
140+
describe('include a create wizard that', () => {
141+
beforeEach(async () => {
142+
const wizard = createConnectedApWizard(doc.querySelector('SubNetwork[type="104"] > ConnectedAP[apName="AP2"]')!);
143+
element.workflow.push(() => wizard);
144+
await element.requestUpdate();
145+
146+
inputs = Array.from(element.wizardUI.inputs);
147+
148+
primaryAction = <HTMLElement>(
149+
element.wizardUI.dialog?.querySelector(
150+
'mwc-button[slot="primaryAction"]'
151+
)
152+
);
153+
});
154+
155+
it('looks like the latest snapshot', async () => {
156+
await expect(element.wizardUI.dialog).dom.to.equalSnapshot();
157+
});
158+
159+
it('does not allow to add connected AccessPoints', () => {
160+
const disabledItems = Array.from(
161+
element.wizardUI.dialog!.querySelectorAll<ListItemBase>(
162+
'mwc-check-list-item'
163+
)
164+
).filter(item => item.disabled);
165+
166+
for (const item of disabledItems) {
167+
const [iedName, apName] = item.value.split('>');
168+
expect(
169+
doc.querySelector(
170+
`ConnectedAP[iedName="${iedName}"][apName="${apName}"]`
171+
)
172+
).to.exist;
173+
}
174+
});
175+
176+
it('allows to add unconnected AccessPoints', () => {
177+
const enabledItems = Array.from(
178+
element.wizardUI.dialog!.querySelectorAll<ListItemBase>(
179+
'mwc-check-list-item'
180+
)
181+
).filter(item => !item.disabled);
182+
183+
for (const item of enabledItems) {
184+
const [iedName, apName] = item.value.split('>');
185+
expect(
186+
doc.querySelector(
187+
`ConnectedAP[iedName="${iedName}"][apName="${apName}"]`
188+
)
189+
).to.not.exist;
190+
}
191+
});
192+
193+
it('shows all AccessPoint in the project', async () => {
194+
expect(
195+
element.wizardUI.dialog?.querySelectorAll('mwc-check-list-item').length
196+
).to.equal(doc.querySelectorAll(':root > IED > AccessPoint').length)
197+
});
198+
199+
it('triggers a create editor action on primary action', async () => {
200+
Array.from(
201+
element.wizardUI.dialog!.querySelectorAll<ListItemBase>(
202+
'mwc-check-list-item'
203+
)
204+
)
205+
.filter(item => !item.disabled)[0]
206+
.click();
207+
await element.requestUpdate();
208+
209+
primaryAction.click();
210+
await element.requestUpdate();
211+
212+
expect(actionEvent).to.be.calledOnce;
213+
expect(actionEvent.args[0][0].detail.action).to.satisfy(isCreate);
214+
});
215+
});
136216
});

0 commit comments

Comments
 (0)