Skip to content

Commit 3d5c5bd

Browse files
refactor(editors/connectedap-editor): move wizards to wizard library (openscd#448)
* refactor(wizards/connectedap): move wizards * refactor(wizards/connectedap): edit wizard and its imports * test(wizards/connectedap): move wizarding tests to unit test * refactor(wizards/connectedap): update action and its tests * refactor(wizards/connectedap): create wizard its action and tests * test(connected-ap): remove leftover snapshot * refactor(wizards/connected-ap): better function naming * test(wizards/connectedap): better naming
1 parent 55e0c7e commit 3d5c5bd

18 files changed

+2574
-1735
lines changed

src/editors/communication/connectedap-editor.ts

Lines changed: 3 additions & 264 deletions
Original file line numberDiff line numberDiff line change
@@ -5,281 +5,20 @@ import {
55
html,
66
property,
77
} from 'lit-element';
8-
import { ifDefined } from 'lit-html/directives/if-defined';
9-
import { translate, get } from 'lit-translate';
108

11-
import '@material/mwc-checkbox';
129
import '@material/mwc-fab';
13-
import '@material/mwc-formfield';
14-
import '@material/mwc-list/mwc-list-item';
15-
import '@material/mwc-list/mwc-check-list-item';
16-
import '@material/mwc-icon';
17-
import { Checkbox } from '@material/mwc-checkbox';
18-
import { List } from '@material/mwc-list';
19-
import { ListItemBase } from '@material/mwc-list/mwc-list-item-base';
2010

2111
import '../../action-icon.js';
22-
import '../../wizard-textfield.js';
23-
import '../../filtered-list.js';
24-
import {
25-
EditorAction,
26-
newWizardEvent,
27-
Wizard,
28-
WizardActor,
29-
WizardInput,
30-
newActionEvent,
31-
compareNames,
32-
getValue,
33-
createElement,
34-
ComplexAction,
35-
} from '../../foundation.js';
36-
import { selectors } from './foundation.js';
37-
import {
38-
getTypes,
39-
typePattern,
40-
typeNullable,
41-
typeMaxLength,
42-
} from './p-types.js';
43-
44-
/** Data needed to uniquely identify an `AccessPoint` */
45-
interface apAttributes {
46-
iedName: string;
47-
apName: string;
48-
}
49-
50-
/** Description of a `ListItem` representing an `IED` and `AccessPoint` */
51-
interface ItemDescription {
52-
value: apAttributes;
53-
connected?: boolean;
54-
}
55-
56-
/** Sorts disabled `ListItem`s to the bottom. */
57-
function compareListItemConnection(
58-
a: ItemDescription,
59-
b: ItemDescription
60-
): number {
61-
if (a.connected !== b.connected) return b.connected ? -1 : 1;
62-
return 0;
63-
}
64-
65-
function isEqualAddress(oldAddr: Element, newAdddr: Element): boolean {
66-
return (
67-
Array.from(oldAddr.querySelectorAll(selectors.Address + ' > P')).filter(
68-
pType =>
69-
!newAdddr
70-
.querySelector(`Address > P[type="${pType.getAttribute('type')}"]`)
71-
?.isEqualNode(pType)
72-
).length === 0
73-
);
74-
}
75-
76-
function createAddressElement(
77-
inputs: WizardInput[],
78-
parent: Element,
79-
instType: boolean
80-
): Element {
81-
const element = createElement(parent.ownerDocument, 'Address', {});
82-
83-
inputs
84-
.filter(input => getValue(input) !== null)
85-
.forEach(validInput => {
86-
const type = validInput.label;
87-
const child = createElement(parent.ownerDocument, 'P', { type });
88-
if (instType)
89-
child.setAttributeNS(
90-
'http://www.w3.org/2001/XMLSchema-instance',
91-
'xsi:type',
92-
'tP_' + type
93-
);
94-
child.textContent = getValue(validInput);
95-
element.appendChild(child);
96-
});
97-
98-
return element;
99-
}
100-
101-
function createConnectedApAction(parent: Element): WizardActor {
102-
return (
103-
inputs: WizardInput[],
104-
wizard: Element,
105-
list?: List | null
106-
): EditorAction[] => {
107-
if (!list) return [];
108-
109-
const apValue = (<ListItemBase[]>list.selected).map(
110-
item => <apAttributes>JSON.parse(item.value)
111-
);
112-
113-
const actions = apValue.map(
114-
value =>
115-
<EditorAction>{
116-
new: {
117-
parent,
118-
element: createElement(parent.ownerDocument, 'ConnectedAP', {
119-
iedName: value.iedName,
120-
apName: value.apName,
121-
}),
122-
},
123-
}
124-
);
125-
126-
return actions;
127-
};
128-
}
129-
130-
function renderWizardPage(element: Element): TemplateResult {
131-
const doc = element.ownerDocument;
132-
133-
const accPoints = Array.from(doc.querySelectorAll(':root > IED'))
134-
.sort(compareNames)
135-
.flatMap(ied =>
136-
Array.from(ied.querySelectorAll(':root > IED > AccessPoint'))
137-
)
138-
.map(accP => {
139-
return {
140-
iedName: accP.parentElement!.getAttribute('name')!,
141-
apName: accP.getAttribute('name')!,
142-
};
143-
});
144-
145-
const accPointDescription = accPoints
146-
.map(value => {
147-
return {
148-
value,
149-
connected:
150-
doc?.querySelector(
151-
`:root > Communication > SubNetwork > ConnectedAP[iedName="${value.iedName}"][apName="${value.apName}"]`
152-
) !== null,
153-
};
154-
})
155-
.sort(compareListItemConnection);
156-
157-
if (accPointDescription.length)
158-
return html` <filtered-list id="apList" multi
159-
>${accPointDescription.map(
160-
item => html`<mwc-check-list-item
161-
value="${JSON.stringify(item.value)}"
162-
twoline
163-
?disabled=${item.connected}
164-
><span>${item.value.apName}</span
165-
><span slot="secondary"
166-
>${item.value.iedName}</span
167-
></mwc-check-list-item
168-
>`
169-
)}
170-
</filtered-list>`;
171-
172-
return html`<mwc-list-item disabled graphic="icon">
173-
<span>${translate('lnode.wizard.placeholder')}</span>
174-
<mwc-icon slot="graphic">info</mwc-icon>
175-
</mwc-list-item>`;
176-
}
177-
178-
/** @returns a Wizard for creating `element` `ConnectedAP`. */
179-
export function createConnectedApWizard(element: Element): Wizard {
180-
return [
181-
{
182-
title: get('connectedap.wizard.title.connect'),
183-
primary: {
184-
icon: 'save',
185-
label: get('save'),
186-
action: createConnectedApAction(element),
187-
},
188-
content: [renderWizardPage(element)],
189-
},
190-
];
191-
}
192-
193-
export function editConnectedApAction(parent: Element): WizardActor {
194-
return (inputs: WizardInput[], wizard: Element): EditorAction[] => {
195-
const instType: boolean =
196-
(<Checkbox>wizard.shadowRoot?.querySelector('#instType'))?.checked ??
197-
false;
198-
199-
const newAddress = createAddressElement(inputs, parent, instType);
200-
201-
const complexAction: ComplexAction = {
202-
actions: [],
203-
title: get('connectedap.action.addaddress', {
204-
iedName: parent.getAttribute('iedName') ?? '',
205-
apName: parent.getAttribute('apName') ?? '',
206-
}),
207-
};
208-
209-
const oldAddress = parent.querySelector(selectors.Address);
210-
211-
if (oldAddress !== null && !isEqualAddress(oldAddress, newAddress)) {
212-
// We cannot use updateAction on address as both address child elements P are changed
213-
complexAction.actions.push({
214-
old: {
215-
parent,
216-
element: oldAddress,
217-
reference: oldAddress.nextSibling,
218-
},
219-
});
220-
complexAction.actions.push({
221-
new: {
222-
parent,
223-
element: newAddress,
224-
reference: oldAddress.nextSibling,
225-
},
226-
});
227-
} else if (oldAddress === null)
228-
complexAction.actions.push({
229-
new: {
230-
parent: parent,
231-
element: newAddress,
232-
},
233-
});
234-
235-
return [complexAction];
236-
};
237-
}
238-
239-
function editConnectedApWizard(element: Element): Wizard {
240-
return [
241-
{
242-
title: get('connectedap.wizard.title.edit'),
243-
element,
244-
primary: {
245-
icon: 'save',
246-
label: get('save'),
247-
action: editConnectedApAction(element),
248-
},
249-
content: [
250-
html`<mwc-formfield
251-
label="${translate('connectedap.wizard.addschemainsttype')}"
252-
>
253-
<mwc-checkbox
254-
id="instType"
255-
?checked="${Array.from(
256-
element.querySelectorAll(selectors.Address + ' > P')
257-
).filter(pType => pType.getAttribute('xsi:type')).length > 0}"
258-
></mwc-checkbox> </mwc-formfield
259-
>${getTypes(element).map(
260-
ptype =>
261-
html`<wizard-textfield
262-
label="${ptype}"
263-
pattern="${ifDefined(typePattern[ptype])}"
264-
?nullable=${typeNullable[ptype]}
265-
.maybeValue=${element.querySelector(
266-
`:root > Communication > SubNetwork > ConnectedAP > Address > P[type="${ptype}"]`
267-
)?.innerHTML ?? null}
268-
maxLength="${ifDefined(typeMaxLength[ptype])}"
269-
></wizard-textfield>`
270-
)}`,
271-
],
272-
},
273-
];
274-
}
12+
import { newWizardEvent, newActionEvent } from '../../foundation.js';
13+
import { editConnectedApWizard } from '../../wizards/connectedap.js';
27514

27615
/** [[`Communication`]] subeditor for a `ConnectedAP` element. */
27716
@customElement('connectedap-editor')
27817
export class ConnectedAPEditor extends LitElement {
27918
/** SCL element ConnectedAP */
28019
@property({ attribute: false })
28120
element!: Element;
282-
/** ConductingEquipment apName attribute */
21+
/** ConnectedAP attribute apName */
28322
@property({ type: String })
28423
get apName(): string {
28524
return this.element.getAttribute('apName') ?? 'UNDEFINED';

0 commit comments

Comments
 (0)